“接口对象”模式。合理?

时间:2012-08-09 11:55:45

标签: c++ design-patterns

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

如下: 我有一组功能,我们称之为 ActionFoo ActionBar ActionZapper 。这些可能在实现上有所不同,但通常用于相同的事情。它们可以在一个序列中一起使用,也可以不一起使用(即,它们中的一些可以单独使用),但是在某些情况下它们确实是分组的。

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

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();

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

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

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

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

1 个答案:

答案 0 :(得分:1)

听起来你想要Chain of Responsibility模式

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时,不需要使用固定的strategies更改此基础对象,您可以以各种有趣和令人兴奋的方式映射操作,每个对象都有一个单一的责任这是一个很好的标准模式,所以每个人都知道发生了什么。

另一种选择是将序列与Action分开。有一个Action接口,其中包含三个继承它的Actions。然后有一个Sequence类,其中包含execute方法和List Action s

class Action { } 
class FooAction extends Action { }

class Sequence {
    List<Action> actions;
    void execute() {
       foreach (action : actions) action.execute();
    }
}