验证方案所需的设计解决方案

时间:2010-07-16 06:59:16

标签: design-patterns architecture validation

我想验证一些对象。验证有两部分:

  • 验证用户是否有权访问该对象(特定权限已经计算并以布尔值存储,最多有4个角色)
  • 检查对象是否处于某种状态(来自一组状态)

我有很多规则(实际上总共大约25个),比如下面那些需要验证的规则:

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

我将这些规则分散在几种方法中,方法中有4或5个规则。 如果规则失败,则不会检查其他规则。 我需要在每个使用此验证的方法中构造一个验证器(或设置一个已构造的验证器)。

我正在寻找一种设计模式,可以更容易地构建一个结构来验证对象。我的目标是能够提供特定的错误消息。例如,如果验证失败,因为用户没有权限,那么我想让他/她知道这一点。如果由于对象的状态而失败,那么我想显示它

首先我是装饰模式。我有一个错误消息处理程序对象,可以使用特定的错误消息进行修饰。一个装饰器将检查用户权限,另一个装饰器用于状态。但是我构建验证器对象的顺序无关紧要,因此不使用装饰器模式的功能。 (AFAIK这是使用装饰器的一大优势 - 混合装饰)。我认为链条可能更适合这种情况......?!?!您会为此方案推荐哪种设计方案?

5 个答案:

答案 0 :(得分:2)

不要考虑使用什么样的模式,而是考虑哪些对象有意义,以及他们的行为应该是什么。

在这种情况下,您要做的是将对象传递给一系列规则。如果它失败了其中一个规则,那将触发一条消息,其余规则不会被检查(这是正确的)吗?

如果是这样,你会注意到我们没有谈论数据传递给链中所有规则的场景......这表明了一系列命令模式,而不是装饰器

另一方面,如果你想将它传递给所有规则,对我来说听起来更像是一个访客模式。

考虑理想的解决方案,然后确定模式。不要试图找到要应用的模式。

答案 1 :(得分:1)

您应该为此方案使用策略模式,因为您需要针对给定用户角色(isOwner)和状态代码使用不同的算法。

答案 2 :(得分:1)

我会使用一系列责任。你让你的对象通过链。

答案 3 :(得分:1)

使用策略(例如:要检查的列表或规则)+状态机(例如:带有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();
        }
    }

答案 4 :(得分:0)

我可能会使用DDD中的规范模式,然后使用像复合规范之类的东西来链接你需要的所有不同部分。

Check out this thread