Проектное решение, необходимое для сценария валидации - PullRequest
5 голосов
/ 16 июля 2010

Я хотел бы проверить некоторые объекты. Проверка состоит из двух частей:

  • проверка того, имеет ли пользователь право доступа к объекту (конкретные права уже рассчитаны и сохранены в логических значениях, максимум 4 роли)
  • проверка, находится ли объект в определенном состоянии (из набора состояний)

У меня много правил (всего около 25), таких как приведенные ниже, которые необходимо проверить:

  • isOwner && (status == 11 || status == 13 || status == 14)
  • ! (IsOwner && isReceiver && status == 12)
  • .....

У меня есть эти правила, распределенные по нескольким методам, 4 или 5 правил в методе. Если правило не выполняется, другие правила не проверяются. Мне нужно создать валидатор (или настроить уже созданный) в каждом методе, который использует эту валидацию.

Я ищу шаблон проектирования, который бы упростил построение структуры для проверки объектов. Моя цель - предоставить конкретные сообщения об ошибках. Например, если проверка не удалась, потому что у пользователя нет прав, я хочу сообщить ему / ей об этом. Если это не удалось из-за состояния объекта, то я хочу показать, что .

Сначала я подумал о шаблоне декоратора. У меня есть объект обработчика сообщений об ошибках, который может быть украшен конкретными сообщениями об ошибках. Один декоратор будет проверять права пользователя, а другой - состояния. Но порядок, в котором я создаю свои объекты валидатора, не имеет значения, поэтому мощность шаблона декоратора не используется. (AFAIK это одно большое преимущество использования декоратора - смешивание украшений). Я думаю, что цепь может быть лучше для этого случая ...?!?! Какой вариант дизайна вы бы порекомендовали для этого сценария?

Ответы [ 5 ]

2 голосов
/ 16 июля 2010

Вместо того, чтобы думать о том, какой шаблон использовать, подумайте о том, какие объекты имеют смысл, и каким должно быть их поведение.

В этом случае вам нужно передать объект в ряд правил. Если не выполняется одно из правил, это вызовет сообщение, а остальные правила не будут проверены (верно)?

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

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

Подумайте об идеальном решении, а затем определите модель. Не пытайтесь найти шаблон для применения.

1 голос
/ 16 июля 2010

Использование стратегии (например: список или проверяемые правила) + конечный автомат (например: перечисление с yield (.net)).

class Program
    {
        public class StateObject
        {
            public virtual int State { get; set; }
        }

        public abstract class Rule
        {
            public abstract Result Check(StateObject objectToBeChecked);

        }

        public class DefaultAllow : Rule
        {
            public override Result Check(StateObject objectToBeChecked)
            {
                Console.WriteLine("DefaultAllow: allow");
                return Result.Allow;
            }
        }

        public class DefaultDeny : Rule
        {
            public override Result Check(StateObject objectToBeChecked)
            {
                Console.WriteLine("DefaultDeny: deny");
                return Result.Deny;
            }
        }

        public class DefaultState : Rule
        {
            public override Result Check(StateObject objectToBeChecked)
            {
                Console.WriteLine("DefaultState: state: {0}", objectToBeChecked.State);
                return objectToBeChecked.State == 1 ? Result.Allow : Result.Deny;
            }
        }

        public class Strategy
        {

            public virtual IEnumerable<Rule> GetRules()
            {
                return new List<Rule>()
                           {
                               new DefaultAllow(),
                               new DefaultState()
                           };
            }
        }

        public class Validator
        {
            private readonly Strategy _strategy;

            public Validator(Strategy strategy)
            {
                _strategy = strategy;
            }

            public IEnumerable<Result> Process(StateObject objectToBeChecked)
            {
                foreach (Rule rule in _strategy.GetRules())
                    yield return rule.Check(objectToBeChecked);
            }

        }

        public class MyStateMachine
        {
            private readonly Validator _validator;
            private StateObject _stateObject;

            public event EventHandler OnAllow;
            public event EventHandler OnDeny;
            public event EventHandler OnError;

            public MyStateMachine(Validator validator)
            {
                _validator = validator;
            }

            public void Init(StateObject stateObject)
            {
                _stateObject = stateObject;
            }

            protected virtual void Validate()
            {
                Result result = Result.Allow; // default 

                foreach (Result r in _validator.Process(_stateObject))
                {
                    result = r;
                    if (r != Result.Allow)
                        break;
                }

                if (result == Result.Allow)
                    Notify(OnAllow);
                else if (result == Result.Deny)
                    Notify(OnDeny);
                else if (result == Result.Error)
                    Notify(OnError);
                else
                    throw new NotSupportedException();

                Console.WriteLine("Result: {0}", result);
            }


            private void Notify(EventHandler handler)
            {
                if (handler != null)
                    handler.Invoke(_stateObject, EventArgs.Empty);
            }


            public void ChangeState(int prevState, int newState)
            {
                if (prevState != _stateObject.State)
                    throw new InvalidStateException();

                _stateObject.State = newState;

                Validate(); // maybe this,  maybe before assign a new state 
            }
        }

        public class InvalidStateException : Exception { }

        public enum Result { Allow, Deny, Error }


        static void Main(string[] args)
        {
            Strategy defaultStrategy = new Strategy();
            Validator ruleChecker = new Validator(defaultStrategy);
            MyStateMachine stateMachine = new MyStateMachine(ruleChecker);

            StateObject objectToBeChecked = new StateObject();
            stateMachine.Init(objectToBeChecked);

            stateMachine.ChangeState(objectToBeChecked.State, 1);
            stateMachine.ChangeState(objectToBeChecked.State, 2);


            Console.ReadLine();
        }
    }
1 голос
/ 16 июля 2010

Я бы использовал цепочку ответственности.Вы заставляете свой объект проходить через цепочку.

1 голос
/ 16 июля 2010

Вы должны использовать шаблон стратегии для этого сценария, потому что вам нужны разные алгоритмы для данной роли пользователя (isOwner) и кода состояния.

0 голосов
/ 16 июля 2010

Я бы, скорее всего, использовал шаблон спецификации от DDD, а затем использовал бы что-то вроде составной спецификации, чтобы связать все необходимые детали.

Проверить эту тему

...