开发者

Design solution needed for Validation scenario

开发者 https://www.devze.com 2023-01-07 19:15 出处:网络
I would like to validate some objects. The validation has two parts: validation whether the user has right to access the object (the specific rights are already calculated and stored in boolean valu

I would like to validate some objects. The validation has two parts:

  • validation whether the user has right to access the object (the specific rights are already calculated and stored in boolean values, there are maximum 4 roles)
  • checking if the object is in a certain state (from a set of states)

I have a lot of rules (actually around 25 in total ) like the ones below that need to be validated:

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

I have these rules spread out in several methods, 4 or 5 rules in a method. If a rule fails then the other rules do not get checked. I need to construct a validator (or set up an already constructed one) in every method that uses this validation.

I am looking for a design pattern that would make it easier to build a structure to validate objects. My goal is to be able to provide specific error messages. For instance if the validation failed because the user doesn't have rights then I want to let him/her know this. If it failed because the object's state then I want to display that .

First I though of the decorator pattern. I have an error message handler object that could be decorated with the specific error messages. One decorator would check for user rights and another one for states. But the order in which I am constructing my validator objects does not matter so the power of decorator pattern is not used. (AFAIK this is on开发者_运维百科e big advantage of using decorator - mixing the decorations). I think a chain might be better for this case...?!?! What design alternative would you recommend for this scenario?


Instead of thinking about what pattern to use, think about what objects make sense, and what their behaviors should be.

In this case, what you want to do is pass an object to a series of rules. If it fails one of the rules, that will trigger a message, and the rest of the rules don't get checked (is that correct)?

If so, you'll notice that what we're not talking about a scenario where the data is passed to all of the rules in the chain... which is indicative of a chain of command pattern, rather than a decorator.

On the other hand, if you want to pass it to all of the rules, it sounds to me almost more like a visitor pattern.

Think of the ideal solution, and then identify the pattern. Don't start by trying to find a pattern to apply.


I would use a chain of responsability. You make your object to pass through the chain.


You should use strategy pattern for this scenario because you need different algorithms for given user role (isOwner) and status code.


Use strategy (for example: list or rules to be checked) + state machine (for example: enumeration with 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();
        }
    }
0

精彩评论

暂无评论...
验证码 换一张
取 消