【问题标题】:Design pattern with methods that return modified parent具有返回修改后的父级的方法的设计模式
【发布时间】:2017-07-07 10:02:48
【问题描述】:

假设我有一个如下所示的类:

class Data
{
    public Data()
    {
       // Stuff
    }

    public void Process1()
    {
       // Stuff
    }

    public void Process2()
    {
       // Stuff
    }
}

我有几个如何使用它的选项。最常见的如下:

var data = new Data();
data.Process1();
data.Process2();

我喜欢使用的一种替代方法需要我修改类:

class Data
{
    public Data()
    {
       // Stuff
    }

    public Data Process1()
    {
       // Stuff
       return this;
    }

    public Data Process2()
    {
       // Stuff
       return this;
    }
}

然后我可以这样使用它:

var data = new Data().Process1().Process2();

这种写作风格的类属于哪种设计模式(如果有的话)?

【问题讨论】:

  • 恕我直言,这不是 GOF 设计模式,它更像是简单的方法链...
  • @Fabjan 虽然语法非常简洁。
  • @Fabjan 是什么阻止您使用普通语法做同样的事情? var data = new Data(); data.Process1(); data.Process2(); data.Process1(); data.Process2(); // etc。任何和所有设计模式都必须负责任地使用才能有效。
  • 在我看来,流式接口上的方法应该返回源对象的新实例,其状态由该方法更新。否则很容易写出误导性的代码。 LINQ 正确地做到了——例如,在可枚举上调用 .Where 不会改变可枚举,而是返回一个新的。这是编写流畅界面的最佳方式。当你看到return this; 时,我会认为它是代码异味。

标签: c# oop design-patterns


【解决方案1】:

在我看来,流式接口上的方法应该返回源对象的新实例,其状态由该方法更新。否则很容易写出误导性的代码。

以这段代码为例:

public class Data
{
    public double Value { get; private set; }
    public Data(double value) { this.Value = value; }

    public Data Times2Add1()
    {
        this.Value = this.Value * 2.0 + 1.0;
        return this;
    }

    public Data Divide3()
    {
        this.Value = this.Value / 3.0;
        return this;
    }
}

如果我运行这个:

var x = new Data(5);
var y = x.Times2Add1();
var z = x.Divide3();

Console.WriteLine(x.Value);
Console.WriteLine(y.Value);
Console.WriteLine(z.Value);

我明白了:

3.66666666666667 3.66666666666667 3.66666666666667

但是,如果我这样写:

public class Data
{
    public double Value { get; private set; }
    public Data(double value) { this.Value = value; }

    public Data Times2Add1()
    {
        return new Data(this.Value * 2.0 + 1.0);
    }

    public Data Divide3()
    {
        return new Data(this.Value / 3.0);
    }
}

结果是

5 11 1.66666666666667

当然我可以写var w = x.Times2Add1().Divide3();,这给了我上面的3.66666666666667,但这次对我来说更有意义。它成为可测试的健壮代码。

LINQ 正确地做到了——例如,在一个可枚举对象上调用 .Where 不会改变该可枚举对象,而是返回一个新的。这是编写流畅界面的最佳方式。

当您看到return this; 时,即使这是一个流畅的界面,我也会认为它是代码异味。

【讨论】:

  • 如果你想像 LINQ 那样做,你必须用包装类包装源实例。除此之外,我同意你的看法
  • @SirRufo - 是的,您是对的,但前提是您希望延迟执行。
  • 你给出了一个非常简单的例子。在具有许多属性的大型类中,必须将它们中的每一个都复制到新实例中。几乎不是一个优雅的解决方案。除非我缺少什么。
  • @stybl - 这取决于你如何定义优雅。在我看来,没有副作用的解决方案更优雅。通常,当我有一个具有很多属性的对象时,我已经实现了一个私有克隆方法,该方法允许流畅的接口轻松复制很多属性,然后我只需更新需要更改的任何值。它变得相当简单。
  • fluent interface 不仅仅是方法链接。 return this 的每次出现不一定是一个流畅的界面。
【解决方案2】:

看看builder模式here,它在.net核心启动类中通过ConfigurationBuilder对象创建应用程序的配置,它们使用链式调用:

public Startup(IHostingEnvironment env)
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile("appsettings.json", 
        optional: true, reloadOnChange: true)
        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", 
        optional: true)
        .AddEnvironmentVariables();
    Configuration = builder.Build();
}

【讨论】:

    猜你喜欢
    • 2012-01-20
    • 1970-01-01
    • 1970-01-01
    • 2017-12-23
    • 2017-08-08
    • 1970-01-01
    • 1970-01-01
    • 2021-08-29
    • 1970-01-01
    相关资源
    最近更新 更多