使用继承实现建造者模式

4

我有一个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

在上述代码中,由于我从WithExtras()获取基类,所以无法解析WithDetails()。我可以重新排列方法使其工作,但是否有一种方法可以更新类,使得任何顺序都能工作?

转换为特定类型?((SpecificRequestBuilder)_specificRequestBuilder.WithExtras()).WithDetails() - MakePeaceGreatAgain
public static T WithDetails<T>(this T t) where T : CommonRequestBuilder { // 添加一些内容到 _request 中 return t; } - Stanislav
我在你的代码中看到了这个注释:// add extra stuff to _request。也许,你应该考虑使用装饰器模式。 - user3303864
4个回答

2

要使用new关键字在您的SpecificRequestBuilder中创建一个具有不同返回类型的方法:

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

2
是的,作为一种简单的转发方法,这是方法隐藏适用的情况之一。但不应该以这种方式扩展WithExtras,否则行为将取决于引用类型。 - Johnathan Barclay
我认为这是我的唯一选择。虽然不完美,但也能用。 - FailedUnitTest

2
在C# 9.0中(这是最新的版本,请确保您已经安装,它随VS 16.8.2一起提供),您可以使用协变重写,这将完美地适用于此处。这将允许您进行以下更改:
只需将方法WithExtras设置为虚拟,并在基类中用SpecificRequestBuilder作为返回类型覆盖它,如下所示。
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();

变得完全有效。

1
个人而言,我会实现一种真正的多态方法。
请求构建器应该被接口化,这样特定类型的构建器就无关紧要了:
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();

CommonRequestBuilder中,默认实现的WithDetails()具有额外的好处,即当您处理CommonRequestBuilder变量时,不允许调用该方法,因为它不起作用。请保留HTML标签。

我喜欢它。但是,因为我们正在尝试遵循开放/封闭原则,所以我不能使用这种方法。 - FailedUnitTest

0

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

public virtual CommonRequestBuilder WithDetails()

构建过程也必须在基础上虚拟才能被重写。

public BigRequest Build()

你会遇到一个问题,派生类中的WithDetails无法返回SpecialRequestBuilder。

我建议创建一个接口,可以由不同的实现来实现。除非坚持使用CommonRequestBuilder(或BaseRequestBuilder)已经足够好了。

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

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

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接