提问人:Luchian Grigore 提问时间:11/16/2011 更新时间:8/16/2020 访问量:587
这是一种设计模式 - 从 setter 返回此模式吗?
Is this a design pattern - returning this from setters?
问:
这个有名字吗:
class A
{
A* setA()
{
//set a
return this;
}
A* setB()
{
//set b
return this;
}
};
所以你可以做这样的事情:
A* a = new A;
a->setA()->setB();
使用它有什么缺点吗?优势?
答:
我以前听说过它被称为“方法链”之类的东西,但我不会称它为设计模式。(有些人还谈论使用它来实现“流畅的界面”——我以前从未见过它这样称呼,但 Martin Fowler 似乎在不久前写过它)
这样做不会损失太多 - 如果您不想那样使用它,您总是可以非常愉快地忽略返回结果。
至于是否值得做,我不太确定。在某些情况下,它可能非常神秘。但是,对于基于流的 IO 之类的事情,它基本上是必需的。我想说的是,这是对它如何与代码的其余部分相适应的呼吁 - 对于阅读它的人来说,这是预期的/显而易见的吗?operator<<
(正如史蒂夫·杰索普(Steve Jessop)所指出的,这几乎总是通过引用而不是指针来完成的)
例如,它通常用于 Boost,但大多数情况下,函数会返回引用:
A &setX()
{
// ...
return *this;
}
一个缺点是,如果你从 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();
评论
(std::ostringstream() << "hi" << 0).str()
template <typename T> std::ostringstream &operator<<(std::ostringstream &o, const T &t) { static_cast<std::ostream &>(o) << t; return o; }
ostream&
operator<<
std::ostringstream()
std::ostream& operator<<(std::ostream&, int);
const char*
另一个常见的用法是“参数对象”。如果没有方法链,它们设置起来非常不方便,但有了它,它们可能是临时的。
而不是:
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 ¶m1(P1 p) { mparam1 = p; return *this; }
ComplicatedParams ¶m2(P2 p) { mparam2 = p; return *this; }
ComplicatedParams ¶m3(P3 p) { mparam3 = p; return *this; }
};
complicated_function(const ComplicatedParams ¶ms);
现在我可以这样称呼它:
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 ¶m1(P1 p) { mparam1 = p; return *this; }
ComplicatedAction ¶m2(P2 p) { mparam2 = p; return *this; }
ComplicatedAction ¶m3(P3 p) { mparam3 = p; return *this; }
run(void);
};
ComplicatedAction().param3(baz).run();
它被称为方法链接(FAQ 链接),通常使用引用而不是指针来完成。
方法链接与命名参数习惯用语(FAQ 链接)密切相关,因为我现在在发布此答案的初始版本后,看到 Steve Jessop 在他的答案中进行了讨论。NPI 惯用语是一种提供大量默认参数的简单方法,而不会将复杂性强加到构造函数调用中。例如,这与 GUI 编程有关。
方法链接技术的一个潜在问题是,当您希望或需要将 NPI 惯用语应用于继承层次结构中的类时。然后你发现 C++ 不支持协变方法。这是什么:当你让你的眼睛在类继承链中向上或向下徘徊时,那么协变方法是一种定义涉及某种类型的方法,在你游荡的眼睛看来,这种类型在特定性上与它所定义的类相同。
这与定义方法的问题大致相同,该方法在所有类中都具有相同的文本定义,但必须在每个类中费力地重复才能获得正确的类型。clone
如果没有语言支持,解决这个问题是很困难的;它似乎是一个固有的复杂问题,与C++类型系统有一种冲突。我的“如何在 C++98 中键入可选参数”博客文章链接到用于自动生成协变定义的相关源代码,以及我在 Dobbs 博士杂志上写的一篇关于它的文章。也许我会在 C++11 或某个时候重新审视这一点,因为复杂性和可能的脆性可能看起来比它的价值更大......
评论
上一个:LL 是什么意思?
下一个:班级的大小限制是多少?
评论
*this
A&
A* a = new A; a->setA().setB()
A b; b.setA().setB();
b.setA()->setB();