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

时间:2011-11-16 10:12:13

标签: c++ design-patterns

这是否有名称:

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

所以你可以这样做:

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

使用它有什么缺点吗?优点

5 个答案:

答案 0 :(得分:9)

它被称为方法链接FAQ link),更常见的是引用,而不是指针。

方法链接与命名参数习惯FAQ link)密切相关,正如我现在发布此答案的初始版本后,请参阅Steve Jessop discusses in his answer。 NPI习惯用法是提供大量默认参数的一种简单方法,而不会强制构造函数调用的复杂性。例如,这与GUI编程有关。

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

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

没有语言支持,解决这个问题很难;它似乎是一个固有的复杂问题,与C ++类型系统发生冲突。我的“How to do typed optional arguments in C++98” blog post链接到相关的源代码,用于自动生成协变定义,以及我在Dobbs Journal博士上写的一篇文章。也许我会重新考虑一下C ++ 11,或者某个时候,因为复杂性和可能的​​脆弱性可能会显得比它的价值更高......

干杯&第h。,

答案 1 :(得分:4)

我之前听说它叫“method chaining”,但我不会称之为设计模式。 (有些人还谈到使用这个实现“fluent interface” - 我之前从未见过它,但是Martin Fowler似乎已经写了一段时间了)

通过这样做你不会失去太多 - 如果你不想那样使用它,你总是可以非常愉快地忽略返回结果。

至于值得做的事我不太确定。在某些情况下,它可能非常神秘。但是,对于基于流的IO,operator<<这样的事情基本上是 required 。我想说这是关于它如何与其余代码相匹配的一个电话 - 对于阅读它的人来说是期望/显而易见的吗?

(正如Steve Jessop指出的那样,几乎总是用引用完成,而不是指针)

答案 2 :(得分:3)

另一个常见的用法是使用“参数对象”。没有方法链接,它们设置起来非常不方便,但有了它,它们可能是临时的。

而不是:

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

答案 3 :(得分:2)

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

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

然后你调用setter的顺序很重要。你需要首先调用Foo上的所有setter:例如,这不起作用:

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

这将:

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

答案 4 :(得分:0)

它通常用于例如Boost,但大多数情况下函数返回引用:

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