这是一个设计模式吗?从setters返回



这个有名字吗:

class A
{
   A* setA() 
   {
       //set a
       return this; 
   }
   A* setB()
   {
       //set b
       return this;
   }
};

所以你可以做这样的事情:

A* a = new A;
a->setA()->setB();

使用这个有什么缺点吗?优点?

它被称为方法链接(FAQ链接(,通常使用引用而不是指针。

方法链接与命名参数习语(常见问题解答链接(密切相关,正如我现在所说,在发布了这个答案的初始版本后,请参阅Steve Jessop在他的答案中所讨论的内容。NPI习惯用法是一种简单的方法,可以提供大量默认参数,而不会将复杂性强加给构造函数调用。例如,这与GUI编程相关。

方法链接技术的一个潜在问题是,当您希望或需要将NPI习惯用法应用于继承层次结构中的类时。然后您发现C++不支持协变方法。这就是:当你让你的眼睛在类继承链中的类上下徘徊时,协变方法的定义涉及某种类型,对你来说,这种类型的特异性与它在类中定义的方式相同。

这与定义clone方法的问题大致相同,该方法在所有类中都有相同的文本定义,但必须在每个类中费力地重复才能获得正确的类型。

没有语言支持很难解决这个问题;这似乎是一个本质上复杂的问题,与C++类型的系统有一种冲突。我的"如何在C++98中进行键入的可选参数"博客文章链接到用于自动生成协变定义的相关源代码,以及我在多布斯博士期刊上写的一篇关于它的文章。也许我会在C++11中重新审视这一点,或者某个时候,因为复杂性和可能的脆性可能看起来比它的价值更大…

我以前听说过它被称为"方法链接",但我不会称它为设计模式。(有些人还谈到使用这个实现"流畅的界面"——不过我以前从未见过它这么叫,但Martin Fowler似乎早就写过了(

这样做不会损失太多——如果你不想那样使用它,你可以很高兴地忽略返回结果。

至于它值得做吗,我不太确定。在某些情况下,它可能相当神秘。然而,对于operator<<之类的基于流的IO,基本上需要。我想说,这是一个关于它如何与代码的其余部分相适应的呼吁——它对阅读它的人来说是预期的/显而易见的吗?

(正如Steve Jessop所指出的,这几乎总是通过引用而不是指针来完成的(

另一个常见的ish用法是"参数对象"。如果没有方法链接,它们的设置非常不方便,但有了它,它们可以是临时的。

代替:

complicated_function(P1 param1 = default1, P2 param2 = default2, P3 param3 = default3);

写入:

struct ComplicatedParams {
    P1 mparam1;
    P2 mparam2;
    P3 mparam3;
    ComplicatedParams() : mparam1(default1), mparam2(default2), mparam3(default3) {}
    ComplicatedParams &param1(P1 p) { mparam1 = p; return *this; }
    ComplicatedParams &param2(P2 p) { mparam2 = p; return *this; }
    ComplicatedParams &param3(P3 p) { mparam3 = p; return *this; }
};
complicated_function(const ComplicatedParams &params);

现在我可以称之为:

complicated_function(ComplicatedParams().param2(foo).param1(bar));

这意味着调用者不必记住参数的顺序。如果没有方法链接,则必须是:

ComplicatedParams params;
params.param1(foo);
params.param2(bar);
complicated_function(params);

我也可以称之为:

complicated_function(ComplicatedParams().param3(baz));

这意味着,在不必定义大量重载的情况下,我可以只指定最后一个参数,并将其余参数保留为默认值。

最后一个明显的调整是使complicated_function成为ComplicatedParams:的成员

struct ComplicatedAction {
    P1 mparam1;
    P2 mparam2;
    P3 mparam3;
    ComplicatedAction() : mparam1(default1), mparam2(default2), mparam3(default3) {}
    ComplicatedAction &param1(P1 p) { mparam1 = p; return *this; }
    ComplicatedAction &param2(P2 p) { mparam2 = p; return *this; }
    ComplicatedAction &param3(P3 p) { mparam3 = p; return *this; }
    run(void);
};
ComplicatedAction().param3(baz).run();

一个缺点是,如果你从a派生一个类,比如说:

class Foo : public A
{
public:
  Foo *setC()
  {
    // set C
    return this;
  }
};

那么你称之为二传手的顺序很重要。你需要先调用Foo上的所有setter:例如,这不起作用:

Foo f=new Foo();
f->setA()->setC();

而这将:

Foo f=new Foo();
f->setC()->setA();

它通常用于Boost,但大多数时候函数会返回引用:

A &setX()
{
    // ...
    return *this;
}

最新更新