1

最近我一直在使用一些模式,但我不知道它是否真的很好。

内容如下:我有一组函数,我们称它们为ActionFooActionBarActionZapper。这些在实现上可能不同,但通常用于这些相同的事情。它们可以按顺序一起使用,也可以不一起使用(即其中一些可以单独使用),但在某些情况下,它们确实是组合在一起的。

如果我确实想按顺序使用它们,我通常有两个选择:

1)每次手动编写它们

2)创建类层次结构:

方法#1:

void SomeActionSequence1()
{
    ActionFoo1(1);
    ActionBar1("Moo");
    ActionZapper1("Moo", 42);
}

void SomeActionSequence2()
{
    ActionFoo4(1);
    ActionBar2("Moo");
    ActionZapper1("Moo", 42);
}

这有缺点:

1)我将无法存储状态,并且必须将大量参数传递给这些操作

2)我不会真的有一个连贯的界面,也不能轻松地使用自动补全

方法#2

class Base
{
    public:
    Base(){}
    virtual ~Base(){}
    virtual void ActionFoo(int) = 0;
    virtual void ActionBar(string) = 0;
    virtual void ActionZapper(string, int) = 0;
    void ExecuteActionSequence();
};

void Base::ExecuteActionSequence()
{
    ActionFoo(1);
    ActionBar("Moo");
    ActionZapper("Moo", 42);
}

Derived1 : public Base
{
    void ActionFoo(int){/*some inplementation*/};
    void ActionBar(string){/*some inplementation*/};
    void ActionZapper(string, int){/*some inplementation*/};
}

Derived2 : public Base
{
    void ActionFoo(int){/*some inplementation*/};
    void ActionBar(string){/*some inplementation*/};
    void ActionZapper(string, int){/*some inplementation*/};
}

并像这样使用它:

Base* actionSequence = new Derived1();
actionSequence->ExecuteActionSequence();

将使用正确的虚拟机,除了两件小事外,一切似乎都很好:

1) 可扩展性——我必须为每个复杂的动作编写一个类

2) 更重要的是——要么在这些类之间重复很多函数,要么我手上的层次树太复杂了

我有点用“接口对象”模式“规避”这两种方法的问题(注意,这个名字是我的,也许它有一个合适的名字)

我要做的是:

class InterfaceClass
{
    public:
    InterfaceClass(){};
    ~InterfaceClass(){};

    void ActionFoo(int i)
    {
        if(fooPlaceholder != 0)
            fooPlaceholder(i);
    }
    void ActionBar(string str)
    {
        if(barPlaceholder != 0)
            barPlaceholder(str);
    }
    void ActionZapper(string str, int i)
    {
        if(zapperPlaceholder != 0)
            zapperPlaceholder(str, i);
    };
    void ExecuteActionSequence();

    std::function<void(int)> fooPlaceholder;
    std::function<void(string)> barPlaceholder;
    std::function<void(string, int)> zapperPlaceholder;
};

void InterfaceClass::ExecuteActionSequence()
{
    ActionFoo(1);
    ActionBar("Moo");
    ActionZapper("Moo", 42);
}

在我的应用程序中,我这样做:

InterfaceClass complexAction;
complexAction.fooPlaceholder = ActionFoo;
complexAction.barPlaceholder = ActionBar;
complexAction.zapperPlaceholder = ActionZapper;

complexAction.ExecuteActionSequence();

请注意,ActionFooActionBarActionZapper是免费功能,但同时我在界面中使用它们。另外 - 我可以轻松地在这些函数的实现之间切换,即使在运行时(如果我需要这个)。

这种方法的优点是 - 不需要为新操作创建单独的类结构,并且没有Action*函数的代码重复。此外 - 所有函数只能在 complexAction 被初始化的地方被带到范围内。

缺点是,我认为,在InterfaceClass对象中使用哪个Action*函数并不明显。另外 - 没有能力对这样的类进行动态转换来确定它是什么。

我高度怀疑这些不仅是这种方法的缺点,所以我想对此发表评论。

4

1 回答 1

1

听起来你想要责任链模式

abstract class Action {
    Action child;
    Action(Action child) { this.child = child; }
    Action() { }

    void doAction(StateContext context);

    void execute(StateContext context) {
       if (child) child.execute(context); 
       doAction(context); 
    } 
}

class ZapAction extends Action {
    ZapAction(String theString, int theValue, Action child) { ... }
    void doAction(Context context) { context.setZap(theString); } 
}


Action actionSequenceAlpha = new ZapAction("", 1, new FooAction()); 
Action actionSequenceBeta = new FooAction(new BarAction(new ZapAction));

优点 - 当你添加一个新的动作时,不需要用一套固定的策略来改变这个基础对象,你可以以各种有趣和令人兴奋的方式映射动作,每个对象都有一个单一的职责,这是一个很好的标准模式所以每个人都知道发生了什么。

另一种选择是将序列与动作分开。有一个 Action 接口,三个 Action 继承它。然后有一个Sequence带有执行方法和一个列表Action的类

class Action { } 
class FooAction extends Action { }

class Sequence {
    List<Action> actions;
    void execute() {
       foreach (action : actions) action.execute();
    }
}
于 2012-08-09T12:14:32.890 回答