我有一个课,我必须多次相互调用一两种方法。方法当前返回void
。我在想,让它返回this
会更好,这样方法可以嵌套吗?或者这是非常非常非常糟糕的?或者,如果它返回一个相同类型的新对象会更好吗?或者您怎么看?作为一个例子,我创建了三个版本的加法器类:
// Regular
class Adder
{
public Adder() { Number = 0; }
public int Number { get; private set; }
public void Add(int i) { Number += i; }
public void Remove(int i) { Number -= i; }
}
// Returning this
class Adder
{
public Adder() { Number = 0; }
public int Number { get; private set; }
public Adder Add(int i) { Number += i; return this; }
public Adder Remove(int i) { Number -= i; return this; }
}
// Returning new
class Adder
{
public Adder() : this(0) { }
private Adder(int i) { Number = i; }
public int Number { get; private set; }
public Adder Add(int i) { return new Adder(Number + i); }
public Adder Remove(int i) { return new Adder(Number - i); }
}
第一个可以这样使用:
var a = new Adder();
a.Add(4);
a.Remove(1);
a.Add(7);
a.Remove(3);
另外两个可以这样使用:
var a = new Adder()
.Add(4)
.Remove(1)
.Add(7)
.Remove(3);
唯一的区别是第一种情况下的a
是new Adder()
,而后者则是最后一种方法的结果。
第一个我发现很快就会变得......一遍又一遍地写作很烦人。所以我想使用其他版本之一。
第三种方法与许多其他方法类似,例如许多String方法和IEnumerable扩展方法。我认为这有其积极的一面,因为你可以做var a = new Adder(); var b = a.Add(5);
之类的事情,然后有一个0和1是5.但同时,创建新对象并不是有点贵时间?什么时候第一个物体会死?当第一种方法返回时?或?
无论如何,我喜欢那个返回this
并认为我会使用它的人,但我很想知道其他人对此案的看法。什么是最佳实践。
答案 0 :(得分:19)
“返回此”样式有时称为fluent interface,这是一种常见做法。
答案 1 :(得分:6)
我喜欢“流利的语法”,并且会选择第二个。毕竟,对于那些对流利语法感到不舒服的人,你仍然可以将它作为第一个使用。
另一个想法是使加法器之类的界面更容易使用:
public Adder Add(params int[] i) { /* ... */ }
public Adder Remove(params int[] i) { /* ... */ }
Adder adder = new Adder()
.Add(1, 2, 3)
.Remove(3, 4);
我总是尝试制作简短易读的界面,但很多人喜欢编写尽可能复杂的代码。
答案 2 :(得分:2)
Chaining是一件好事,并且在某些框架中是核心(例如Linq扩展和jQuery都大量使用它)。
是否创建新对象或return this
取决于您对初始对象的行为方式:
var a = new Adder();
var b = a.Add(4)
.Remove(1)
.Add(7)
.Remove(3);
//now - should a==b ?
jQuery中的链接将改变你的原始对象 - 它已经返回了这个。 这是预期的行为 - 否则基本上会克隆UI元素。
Linq中的链接将保持原始集合不变。这也是预期的行为 - 每个链式函数都是过滤器或转换,原始集合通常是不可变的。
哪种模式更适合你正在做的事情?
答案 3 :(得分:0)
考虑一下:如果你在5年内回到这段代码,这对你有意义吗?如果是这样,那么我想你可以继续。
对于这个具体的例子,似乎重载+
和-
运算符会使事情变得更清晰并完成同样的事情。
答案 4 :(得分:0)
对于您的具体情况,重载算术运算符可能是最佳解决方案。
返回this
(Fluent界面)是创建表达式的常用做法 - 单元测试和模拟框架使用了很多。流利的Hibernate是另一个例子。
返回新实例也是一个不错的选择。它允许您使您的类不可变 - 通常是一个好东西,在多线程的情况下非常方便。但是,如果不可变性对你有用,请考虑对象创建开销。
答案 5 :(得分:0)
如果你称之为Adder,我会回复此事。但是,Adder类包含答案有点奇怪。
您可以考虑将其设置为MyNumber并创建Add() - 方法。
理想情况下(恕我直言),不会更改实例中存储的数字,而是使用新值创建一个新实例,并返回:
class MyNumber
{
...
MyNumber Add( int i )
{
return new MyNumber( this.Value + i );
}
}
答案 6 :(得分:0)
我认为对于简单的接口,“流畅”的界面非常有用,特别是因为它实现起来非常简单。流畅的界面的价值在于它消除了许多妨碍理解的外来绒毛。开发这样的界面可能需要花费很多时间,尤其是当界面开始涉及时。您应该担心接口的使用“如何读取”;在我看来,这种界面最引人注目的用途是它如何传达程序员的意图,而不是它节省的字符数量。
要回答您的具体问题,我喜欢“回归此”风格。我对fluent界面的典型用法是定义一组选项。也就是说,我创建了一个类的实例,然后在实例上使用fluent方法来定义对象的所需行为。如果我有一个是/否选项(比如记录),我尝试不使用“setLogging(bool state)”方法,而是使用两个方法“WithLogging”和“WithoutLogging”。这有点多了,但最终结果的清晰度非常有用。
答案 7 :(得分:0)
第二个和第三个解决方案之间的主要区别在于,通过返回一个新实例而不是这个实例,您可以“捕获”某个状态的对象并从中继续。
var a = new Adder() 。新增(4);
var b = a.Remove(1);
var c = a.Add(7) 卸下摆臂(3);
在这种情况下,b和c都以a为起点捕获状态。 我在阅读有关在Growing Object-Oriented Software, Guided by Tests by Steve Freeman; Nat Pryce中构建测试域对象的模式时遇到了这个习语。
关于你的实例的生命周期的问题:一旦调用Remove或Add返回,我就会认为它们对于垃圾收集是合格的。