枚举组合的设计模式(方法参数)

本文关键字:方法 参数 设计模式 组合 枚举 | 更新日期: 2023-09-27 18:01:58

我有一个类似于这个的代码块:

public bool DoMyThings(EnumRootAction root, EnumChildAction child){
          switch(root){
               case EnumRootAction.Type1:
                                switch(child){
                                       case EnumChildAction.Ac1: 
                                                    // DO something
                                                    break;
                                        case EnumChildAction.Ac2: 
                                                    // DO something
                                                    break;
                                        default: // child is not child of type root
                                                return false;
                                }
                                break;
                case EnumRootAction.Type2:
                                switch(child){
                                       case EnumChildAction.Ac1: 
                                                    // DO something
                                                    break;
                                        case EnumChildAction.Ac3: 
                                                    // DO something
                                                    break;
                                        default: // child is not child of type root
                                                return false;
                                }
                                break;
                ... // etc...
                default: 
                    return false;                       
        }
        return true;
     }

然而,我确实认为这是一段不好看的代码…:(。。。此服务的"使用者"不知道有效的root/child的正确组合,并且可能传递错误的组合。

我想把它"吐"成几种方法,比如:DoMyThingsForRootType1(EnumChildActionForType1 child),但从长远来看,在我的情况下,这并不容易改变。我解释了原因:如果我的服务签名不断变化,我需要更新所有实时客户端的服务协议。。。。更新源代码并重新部署。。。。目前,客户端可以通过更改一些app.settings值来手动进行这些更改。

在这一点上,我提供了一张正确组合的纸,但在我看来这也是垃圾。

也许我只是从一个角度来看待这件事,不知何故,我无法扩大我的视野。。。这就是为什么我想听听你们的反馈。。。

有更好的设计模式吗?至少以某种方式通知服务的客户端有效的组合。。。。

谢谢。

枚举组合的设计模式(方法参数)

如果可以使用接口或基类而不是枚举,则可以利用面向对象的功能来确定如何评估组合。这允许您以易于理解和维护的方式对根/子操作功能进行分组。然而,它有些冗长:

    public interface IChildAction
    {
    }
    public class ChildAction1 : IChildAction
    {
    }
    public class ChildAction2 : IChildAction
    {
    }
    public class ChildAction3 : IChildAction
    {
    }
    public abstract class BaseRootAction
    {
        public virtual bool Process(ChildAction1 action)
        {
            return false;
        }
        public virtual bool Process(ChildAction2 action)
        {
            return false;
        }
        public virtual bool Process(ChildAction3 action)
        {
            return false;
        }
    }
    public class RootAction1 : BaseRootAction
    {
        public override bool Process(ChildAction1 action)
        {
            Console.WriteLine("Root action 1, Child action 1");
            return true;
        }
        public override bool Process(ChildAction2 action)
        {
            Console.WriteLine("Root action 1, Child action 2");
            return true;
        }
    }
    public class RootAction2 : BaseRootAction
    {
        public override bool Process(ChildAction1 action)
        {
            Console.WriteLine("Root action 2, Child action 1");
            return true;
        }
        public override bool Process(ChildAction3 action)
        {
            Console.WriteLine("Root action 2, Child action 3");
            return true;
        }
    }
    public class RootAction3 : BaseRootAction
    {
        public override bool Process(ChildAction2 action)
        {
            Console.WriteLine("Root action 3, Child action 2");
            return true;
        }
    }
    public bool DoMyThings(BaseRootAction rootAction, IChildAction childAction)
    {
        return rootAction.Process((dynamic)childAction);
    }

我还包括我的完整性测试方法:

    private void Test()
    {
        List<BaseRootAction> rootActions = new List<BaseRootAction>() { new RootAction1(), new RootAction2(), new RootAction3()};
        List<IChildAction> childActions = new List<IChildAction>() { new ChildAction1(), new ChildAction2(), new ChildAction3()};
        foreach (BaseRootAction rootAction in rootActions)
        {
            foreach (IChildAction childAction in childActions)
            {
                bool result = DoMyThings(rootAction, childAction);
                Console.WriteLine(String.Format("Processed '{0}' / '{1}': Result = {2}", rootAction.GetType().Name, childAction.GetType().Name, result));
            }
        }
    }

如果需要枚举,则可以从中派生操作类,但解决方案会变得更加臃肿。

第一步可以是在任何情况下都调用一个方法。

case EnumRootAction.Type1:
    switch(child){
        case EnumChildAction.Ac1: 
            Type1_Ac1();
            break;

第二步可以是使用委托矩阵而不是函数。

    delegate bool DoMyThingsAction();
    DoMyThingsAction[,] doMyThings;
    public void InitializeDoMyThings()
    {
        doMyThings = new DoMyThingsAction[10, 10]; // number of actions and type
        doMyThings[0, 0] = Type1_Ac1;
        // and so on
    }

要称之为

    doMyThings[(int)root, (int)child];

如果您不喜欢doMyThings调用中的强制转换为int,可以使用不同的结构并覆盖索引器。

另一种选择是使用反射来获得方法(我不喜欢它(

public bool DoMyThings(EnumRootAction root, EnumChildAction child){
       return GetType().GetMethod(string.Format("{0}_{1}", root, child).Invoke(this);
}

编辑
查看程序时,如果没有方法,则应返回false。在矩阵的情况下,可以在调用之前检查是否有函数,在反射的情况下可以检查GetMethod是否返回null。

您可以应用访问者模式。您的RootAction是访问者,ChildAction是元素。以下是代码,请注意OOP中重载的功能有多强大:

List<RootAction_Visitor> visitors = new List<>();
visitors[EnumRootAction.Type1] = new RootAction1_Visitor();
visitors[EnumRootAction.Type2] = new RootAction2_Visitor();
List<ChildAction_Element> elements = new List<>();
elements[EnumChildAction.Ac1] = new ChildAction1_Element();
elements[EnumChildAction.Ac2] = new ChildAction2_Element();
elements[EnumChildAction.Ac3] = new ChildAction3_Element();
public void doMyThing(EnumRootAction root, EnumChildAction child) {
    elements[child].accept( visitors[root] );
}
//=======================================================================
// Visitors
abstract class RootAction_Visitor {
    public void visit(ChildAction1_Element childAction) { /*.. reject by default ...*/ }
    public void visit(ChildAction2_Element childAction) { /*.. reject by default ...*/ }
    public void visit(ChildAction3_Element childAction) { /*.. reject by default ...*/ }
}
class RootAction1_Visitor : RootAction_Visitor {
    public override void visit(ChildAction1_Element childAction) {
        // ... do something
    }
    public override void visit(ChildAction2_Element childAction) {
        // ... do something
    }
}
class RootAction2_Visitor : RootAction_Visitor {
    public override void visit(ChildAction1_Element childAction1) {
        // ... do something
    }
    public override void visit(ChildAction3_Element childAction3) {
        // ... do something
    }
}
//=======================================================================
// Elements
interface ChildAction_Element {
    void accept(RootAction_Visitor visitor);
}
class ChildAction1_Element : ChildAction_Element {
    public void accept(RootAction_Visitor visitor) { visitor.visit(this); }
}
class ChildAction2_Element : ChildAction_Element {
    public void accept(RootAction_Visitor visitor) { visitor.visit(this); }
}
class ChildAction3_Element : ChildAction_Element {
    public void accept(RootAction_Visitor visitor) { visitor.visit(this); }
}

因为您的客户端和服务器通过网络进行通信(进程间通信(。无法确保客户端发送的数据有效,我们需要验证数据并拒绝错误的请求。不过,您应该提供另一个服务,这样客户端就可以在向您的主服务请求之前知道哪些操作组合是正确的。

p/S:在RootActionChildAction之间进行选择,即Visitor,即Element,取决于它们的稳定性。如果RootAction是不稳定的,即会出现新的RootAction,那么它们应该是Visitors