Хороший шаблон дизайна для реализации различных форм поведения на предмете - PullRequest
9 голосов
/ 06 июня 2011

Каков лучший дизайн для этого сценария?
У меня есть разные типы объектов: User, Channel, MessageBox, UserGroup и т. Д.
User и Channel могут иметь разрешение на другие объекты. Например, User имеет следующее перечисление, определенное как его разрешения для MessageBox:

CanRead,
CanWrite,
CanDelete,
...

Другие перечисления определены для User как владелец других типов объектов.

Кроме того, Channel имеет различные значения перечисления для этих объектов. Например, рассмотрим Channel как владельца и MessageBox как объект:

CanDispath
CanRetrieve
...

Все разрешения сохраняются и извлекаются из определенной таблицы в базе данных с использованием побитового сравнения:

OwnerID........OwnerType........ObjectID........ObjectType........AccessLevel  
  1              User              10           MessageBox            38     
  5             Channel            12           MessageBox            18  

Теперь в коде позади, Каков наилучший способ реализации классов разрешений?

1 - Определите PermissionManager, UserPermissionManager, ChannelPermissionManager классов отдельно друг от друга. Другие классы просто звоните PermissionManager как:

if (new PermissionManager.HasAccess(CurrentUser,  
                                    CurrentMessageBox, 
                                    UserPermissions.CanReadMessages))  

Затем PermissionManager решает, к какому классу это относится, на основе OwnerType (UserPermissionManager или ChannelPermissionManager) и вызывает его метод HasAccess. Таким образом, PermissionManager.HasAccess всегда вызывается, и я думаю, что это может сделать код более понятным и расширяемым. Это мое предпочтительное решение, но поскольку PermissionManager, UserPermissionManager и ChannelPermissionManager относятся к одному и тому же контексту, я думаю, что должна быть иерархия или, возможно, интерфейс, чтобы эти 3 класса стали более интегрированными. Но я не знаю, как их связать.

2 - Определите IPermissionManager интерфейс и внедрите UserPermissionManager и ChannelPermissionManager из него. Добавьте PermissionManagerTypes enum. Создайте класс фабрики и назовите менеджеров как:

IPermissionManager UserManager =   
    PermissionFactory.Create(PermissionsManagerTypes.User);
if (UserManager.HasAccess(CurrentUser,  
                          CurrentMessageBox, 
                          UserPermissions.CanReadMessages))  

Это своего рода неудачная попытка связать классы вместе. Но я подумал, что было бы хорошо упомянуть об этом здесь, чтобы вы знали, чего я пытаюсь достичь.

P.S. Я не могу определить классы как статические, так как они должны иметь закрытую переменную типа ObjectContext (Entity Framework).

Есть ли лучшее решение для достижения этой цели?
Спасибо и извинения за очень длинный вопрос.

Ответы [ 2 ]

3 голосов
/ 06 июня 2011

Ну, на это довольно сложно ответить.Вы можете попытаться создать базовый интерфейс IPermission;

interface IPermission<TOwner>
{
}

Затем вы реализуете этот интерфейс для типов, для которых вы хотите иметь разрешение.

class UserPermission : IPermission<User>
{
    public UserPermission(CustomerPermissionType type)
    {
        // Store the type
    }
}

class ChannelPermission : IPermission<Channel>
{
    public ChannelPermission (ChannelPermissionType type)
    {
        // Store the type
    }
}

Теперь вам нужноинтерфейс, предоставляющий разрешения для определенных объектов.

interface IPermissionProvider
{
    bool HasPermission<TOwner>(IPermission<TOwner> permission, TOwner owner, object target);
}

На данный момент у вас есть базовая функциональность для запроса разрешений.Проблема состоит в том, как управлять различной обработкой для прав пользователя и канала.Вы можете реализовать что-то вроде этого:

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);
    }
}

Последним шагом будет создание конкретных реализаций IPermissionProvider для пользователя и канала и их регистрация в PermissionDispatcher при запуске приложения / службы.

Использование будет так просто:

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);

}

Примерно так будет единственным способом решить эту проблему, не принимая зависимость от обработки разрешений в типах вашего домена.,Тем не менее я абсолютно уверен, что это не идеальное решение.

1 голос
/ 06 июня 2011

Я вообще не думал об этом, но:

interface HasPermissions {
   getPermissionsFor(object)
}

User implements HasPermissions

Channel implements HasPermissions

etc..


User user = new User();

if user.getPermissionsFor(object).contains(canRead)
...