Хорошая схема проектирования для реализации различных видов поведения по предмету
Каков наилучший дизайн для этого сценария?
У меня разные типы объектов: User
, Channel
, MessageBox
, UserGroup
и т.д.
User
и Channel
могут иметь разрешение на другие объекты. Например, User
имеет следующее перечисление, определенное как его разрешения для MessageBox
:
CanRead,
CanWrite,
CanDelete,
...
Другие перечисления определены для User
как владелец других типов объектов.
Кроме того, Channel
имеет разные значения enum для этих объектов. Например, рассмотрите 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
перечисление. Создайте класс factory и вызовите Менеджеры, например:
IPermissionManager UserManager =
PermissionFactory.Create(PermissionsManagerTypes.User);
if (UserManager.HasAccess(CurrentUser,
CurrentMessageBox,
UserPermissions.CanReadMessages))
Это своего рода неудачная попытка связать классы вместе. Но я подумал, что было бы неплохо упомянуть об этом здесь, чтобы вы знали, чего я пытаюсь достичь.
P.S. Я не могу определить классы как статические, так как они должны иметь приватную переменную типа ObjectContext
(Entity Framework).
Есть ли лучшее решение для достижения этого?
Спасибо и извиняюсь за очень длительный вопрос.
Ответы
Ответ 1
Ну, это довольно сложно ответить. Вы можете попытаться создать базовый интерфейс 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);
}
Что-то вроде этого будет единственным способом решить эту проблему, не принимая зависимость от обработки разрешений в ваших типах доменов. Тем не менее я абсолютно уверен, что это не идеальное решение.
Ответ 2
Я вообще не думал об этом, но:
interface HasPermissions {
getPermissionsFor(object)
}
User implements HasPermissions
Channel implements HasPermissions
etc..
User user = new User();
if user.getPermissionsFor(object).contains(canRead)