使用继承的构建器模式

Builder Pattern using Inheritance

我有 CommonRequestBuilderSpecificRequestBuilder,看起来像这样。

public class CommonRequestBuilder 
{
    protected readonly BigRequest _request;

    public CommonRequestBuilder()
    {
         _request = new BigRequest();
    }

    public CommonRequestBuilder WithExtras()
    {
        // add extra stuff to _request
        return this;
    }

    public BigRequest Build()
    {
        return _request;
    }
}

public class SpecificRequestBuilder : CommonRequestBuilder
{
    public SpecificRequestBuilder WithDetails()
    {
        // add some stuff to _request
        return this;
    }

    public BigRequest Build()
    {
        return _request;
    }
}

此模式的问题在于,如果我像这样使用 SpecificRequestBuilder:

_specificRequestBuilder.WithExtras().WithDetails(); // WithDetails() is not found

在上面的代码中 WithDetails() 无法解析,因为我正在从 WithExtras() 获取基数 class。我可以重新安排方法使其工作,但有没有办法更新 classes 以便任何订单都有效?

您可以在基类上声明 WithDetails 为可被覆盖的虚方法。

public virtual CommonRequestBuilder WithDetails()

Build 也必须在要覆盖的基础上是虚拟的

public BigRequest Build()

您会遇到派生的 class 上的 WithDetails 无法返回 SpecialRequestBuilder 的问题。

我会创建一个可以由各种实现实现的接口。除非坚持使用 CommonRequestBuilder(或 BaseRequestBuilder)工作得很好。

public interface IRequestBuilder
{
    IRequestBuilder WithExtras();
    IRequestBuilder WithDetails();
    BigRequest Build();
}

public class CommonRequestBuilder : IRequestBuilder
{
    // ...
}

public class SpecificRequestBuilder : CommonRequestBuilder, IRequestBuilder
{
    // ...
}

您必须在 SpecificRequestBuilder 中使用 new 关键字才能使用不同 return 类型的方法:

public class SpecificRequestBuilder : CommonRequestBuilder
{
    public SpecificRequestBuilder WithDetails()
    {
        // add some stuff to _request
        return this;
    }


    public new SpecificRequestBuilder WithExtras()
    {
        return (SpecificRequestBuilder)base.WithExtras();
    }
}

在线演示:https://dotnetfiddle.net/tU4hEM

在 C# 9.0(这是最新版本,所以请确保您拥有它,它与 VS 16.8.2 一起提供)中,您可以使用 covariant overrides,它非常适合这里。这将允许您进行此类更改:

只需将方法 WithExtras 设为虚拟方法,并在基础 class 中使用 SpecificRequestBuilder 类型的 return 覆盖它,如下所示

public class CommonRequestBuilder
{
    protected readonly BigRequest _request;

    public CommonRequestBuilder()
    {
        _request = new BigRequest();
    }

    public virtual CommonRequestBuilder WithExtras()
    {
        // add extra stuff to _request
        return this;
    }

    public BigRequest Build()
    {
        return _request;
    }
}

public class SpecificRequestBuilder : CommonRequestBuilder
{
    public override SpecificRequestBuilder WithExtras()
    {
        base.WithExtras();
        return this;
    }
    public SpecificRequestBuilder WithDetails()
    {
        return this;
    }

    public BigRequest Build()
    {
        return _request;
    }
}

现在声明

_specificRequestBuilder.WithExtras().WithDetails();

变得完全有效。

就我个人而言,我会实施真正的多态方法。

请求构建器应该是接口的,那么构建器的具体类型是什么并不重要:

interface IRequestBuilder
{
    IRequestBuilder WithExtras();
    IRequestBuilder WithDetails();
    BigRequest Build();
}

您仍然可以提供默认实现:

public class CommonRequestBuilder : IRequestBuilder
{
    protected readonly BigRequest _request;

    public CommonRequestBuilder()
    {
         _request = new BigRequest();
    }

    public virtual IRequestBuilder WithExtras()
    {
        // add extra stuff to _request
        return this;
    }
    
    // Default implementation
    IRequestBuilder IRequestBuilder.WithDetails() => this;

    public virtual BigRequest Build()
    {
        return _request;
    }
}

并使其更具体:

public class SpecificRequestBuilder : CommonRequestBuilder
{
    public IRequestBuilder WithDetails()
    {
        // add some stuff to _request
        return this;
    }
}

现在您可以使用任何您喜欢的实现方式:

IRequestBuilder builder = new SpecificRequestBuilder().WithExtras().WithDetails();

CommonRequestBuilderWithDetails() 的默认实现有一个额外的好处,即在处理 CommonRequestBuilder 变量时不允许调用该方法,因为它什么都不做。