16

我有一门课,我必须多次调用一两个方法。当前返回的方法void。我在想,最好让它 return 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,一个是 5。但与此同时,一直创建新对象不是有点贵吗?第一个物体什么时候会死?第一种方法什么时候返回?或者?

无论如何,我喜欢返回this并认为我会使用它的那个,但我很想知道其他人对此案的看法。什么被认为是最佳实践。

4

8 回答 8

22

'return this' 风格有时被称为流畅的界面,是一种常见的做法。

于 2009-04-23T13:25:27.057 回答
6

我喜欢“流利的语法”,并会选择第二个。毕竟,对于那些对流利语法感到不舒服的人来说,你仍然可以将它用作第一个。

使像加法器这样的界面更易于使用的另一个想法:

public Adder Add(params int[] i) { /* ... */ }
public Adder Remove(params int[] i) { /* ... */ }

Adder adder = new Adder()
  .Add(1, 2, 3)
  .Remove(3, 4);

我总是尝试制作简短易读的界面,但很多人喜欢编写尽可能复杂的代码。

于 2009-04-23T13:26:50.173 回答
2

链接是一件好事,并且是某些框架的核心(例如 Linq 扩展和 jQuery 都大量使用它)。

是创建新对象还是return this取决于您期望初始对象的行为方式:

var a = new Adder();

var b = a.Add(4)
         .Remove(1)
         .Add(7)
         .Remove(3);

//now - should a==b ?

jQuery 中的链接将更改您的原始对象 - 它已返回 this。这是预期的行为 - 否则基本上会克隆 UI 元素。

Linq 中的链接将保持您的原始集合不变。这也是预期的行为 - 每个链接的函数都是一个过滤器或转换,并且原始集合通常是不可变的。

哪种模式更适合您的工作?

于 2009-04-23T13:36:15.660 回答
1

我认为对于简单的接口,“流利”接口非常有用,特别是因为它实现起来非常简单。流畅界面的价值在于,它消除了许多妨碍理解的多余绒毛。开发这样的界面可能需要很多时间,尤其是在开始涉及界面时。您应该担心接口的使用如何“读取”;在我看来,这种界面最引人注目的用途是它如何传达程序员的意图,而不是它节省的字符数量。

为了回答你的具体问题,我喜欢“return this”的风格。我对 fluent 接口的典型使用是定义一组选项。也就是说,我创建了一个类的实例,然后在实例上使用流利的方法来定义对象的期望行为。如果我有一个是/否选项(比如日志记录),我尽量不使用“setLogging(bool state)”方法,而是使用“WithLogging”和“WithoutLogging”两种方法。这需要更多的工作,但最终结果的清晰度非常有用。

于 2009-04-23T13:36:05.720 回答
0

考虑一下:如果你在 5 年后回到这个代码,这对你有意义吗?如果是这样,那么我想你可以继续。

但是,对于这个特定的示例,重载+and-运算符似乎会使事情变得更清晰并完成相同的事情。

于 2009-04-23T13:25:12.987 回答
0
  1. 对于您的具体情况,重载算术运算符可能是最好的解决方案。

  2. 返回this(Fluent 接口)是创建表达式的常见做法——单元测试和模拟框架经常使用它。Fluent Hibernate 是另一个例子。

  3. 返回一个新实例也可能是一个不错的选择。它允许您使您的类不可变——通常是一件好事,在多线程的情况下非常方便。但是,如果不变性对您没有用处,请考虑对象创建开销。

于 2009-04-23T13:31:00.587 回答
0

如果你称它为加法器,我会返回这个。但是,Adder 类包含答案有点奇怪。

您可能会考虑将其设为 MyNumber 并创建一个 Add() 方法。

理想情况下(恕我直言),这不会更改存储在您的实例中的数字,而是使用您返回的新值创建一个新实例:

class MyNumber
{
    ...

    MyNumber Add( int i )
    {
        return new MyNumber( this.Value + i );
    }
}
于 2009-04-23T13:31:18.073 回答
0

第二种和第三种解决方案之间的主要区别在于,通过返回一个新实例而不是这个实例,您可以“捕获”处于某种状态的对象并从该状态继续。

var a = new Adder() .Add(4);

var b = a.Remove(1);

var c = a.Add(7).Remove(3);

在这种情况下,b 和 c 都将在 a 中捕获的状态作为起点。我在阅读Steve Freeman 的 Tests Guided by Tests 的“Growing Object-Oriented Software ”中构建测试域对象的模式时遇到了这个习语;纳特普莱斯

关于您关于实例生命周期的问题:一旦调用 Remove 或 Add 返回,我希望它们有资格进行垃圾收集。

于 2009-04-23T18:51:27.573 回答