A good design pattern for implementing different behaviors on a subject
What is the best design for this scenario?
I have different Object types:User
, Channel
, MessageBox
, UserGroup
, etc.
User
and Channel
can have permission on other objects. For example User
has the following enum defined as its permissions for MessageBox
:
CanRead,
CanWrite,
CanDelete,
...
Other enums are defined for User
as the owner of other object types.
Also, Channel
has different enum values on these objects. For example, consider Channel
as the owner and MessageBox
as the object:
CanDispath
CanRetrieve
...
All the permissions are saved and retrieved from a specific table in database using bitwise comparison:
OwnerID........OwnerType........ObjectID........ObjectType........AccessLevel
1 User 10 MessageBox 38
5 Channel 12 MessageBox 18
Now in code behind, What's the best way to implement permission classes?
1- Define PermissionManager
, UserPermissionManager
, ChannelPermissionManager
classes separately from each other. Other classes just call PermissionManager
like:
if (new PermissionManager.HasAccess(CurrentUser,
CurrentMessageBox,
UserPermissions.CanReadMessages))
Then PermissionManager
decides what class this is related to based on the OwnerType
(UserPermissionManager
or ChannelPermissionManager
) and calls its HasAccess
method. This way, PermissionManager.HasAccess
is always being called and I think it can make the code more maintainable and extensible. This is my preferred solution but since PermissionManager
, UserPermissionManager
and ChannelPermissionManager
refer to the same context, I think there should be a hierarchy or possibly an interface so these 3 classes become more integrated. But I don't know how to relate them together.
2- Define IPermissionManager
interface and implement UserPermissionManager
and ChannelPermissionManager
from it. Add PermissionManagerTypes
enum. Create a factory cl开发者_开发问答ass and call Managers like:
IPermissionManager UserManager =
PermissionFactory.Create(PermissionsManagerTypes.User);
if (UserManager.HasAccess(CurrentUser,
CurrentMessageBox,
UserPermissions.CanReadMessages))
This is a kind of failed try to relate classes together. But I thought It'd be good to mention it here to let you know what I'm trying to achieve.
P.S. I cannot define classes as static since they need to have a private variable of type ObjectContext
(Entity Framework).
Is there a better solution to achieve this?
Thank you and Apologies for the very lengthy question.Well, this is pretty hard to answer. You could try to create a base interface IPermission;
interface IPermission<TOwner>
{
}
Then you implement this interface for the types you want to be able to own a permission.
class UserPermission : IPermission<User>
{
public UserPermission(CustomerPermissionType type)
{
// Store the type
}
}
class ChannelPermission : IPermission<Channel>
{
public ChannelPermission (ChannelPermissionType type)
{
// Store the type
}
}
Now you need an interface that provides permissions for specific objects.
interface IPermissionProvider
{
bool HasPermission<TOwner>(IPermission<TOwner> permission, TOwner owner, object target);
}
At this point you have the basic functionality to query for permissions. The problem is how to manage the different handling for User and Channel permissions. You could implement something like this:
class PermissionDispatcher : IPermissionProvider
{
public void RegisterPermissionProvider<TOwner>(IPermissionProvider permissionProvider)
{
// Store it somewhere
}
public IPermissionProvider GetPermissionProvider<TOwner>()
{
// Look up a permission provider that is registered for the specified type TOwner and return it.
}
bool IPermissionProvider.HasPermission<TOwner>(IPermission<TOwner> permission, TOwner owner, object target)
{
IPermissionProvider permissionProvider = GetPermissionProvider<TOwner>();
return permissionProvider .HasPermission<TOwner>(permission, owner, target);
}
}
The last step would then be to create specific implementations of IPermissionProvider for User and Channel and register them to the PermissionDispatcher at the startup of your application / service.
The usage would be as simple as this:
void foo()
{
IPermissionProvider permissionProvider = ... the dispatcher, could be a singleton ...;
User user = ...;
MessageBox messageBox = ...;
UserPermission userCanReadPermission = new UserPermission(UserPermissionType.CanRead);
bool hasUserCanReadPermission = permissionProvider.HasPermission(userCanReadPermission, user, messageBox);
}
Something like this will be the only way to solve this without taking a dependency on permission handling in your domain types. Nevertheless I'm absolutely sure this is no perfect solution.
I put no thought into this at all, but:
interface HasPermissions {
getPermissionsFor(object)
}
User implements HasPermissions
Channel implements HasPermissions
etc..
User user = new User();
if user.getPermissionsFor(object).contains(canRead)
精彩评论