Reputation: 1800
I have CommonRequestBuilder
and SpecificRequestBuilder
, that look like this.
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;
}
}
The issue with this pattern, is that if I use SpecificRequestBuilder like this:
_specificRequestBuilder.WithExtras().WithDetails(); // WithDetails() is not found
In above code WithDetails()
cannot be resolved because I am getting the base class from WithExtras()
. I can re-arrange the methods to make it work, but is there a way I can update the classes so that any order works?
Upvotes: 4
Views: 550
Reputation: 20363
Personally I would implement a true polymorphic approach.
A request builder should be interfaced, then it doesn't matter what the specific type of builder is:
interface IRequestBuilder
{
IRequestBuilder WithExtras();
IRequestBuilder WithDetails();
BigRequest Build();
}
You can still provide a default implementation:
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;
}
}
And make it more specific:
public class SpecificRequestBuilder : CommonRequestBuilder
{
public IRequestBuilder WithDetails()
{
// add some stuff to _request
return this;
}
}
Now you can use any implementation you like:
IRequestBuilder builder = new SpecificRequestBuilder().WithExtras().WithDetails();
The default implementation of WithDetails()
in CommonRequestBuilder
has the added benefit of not allowing that method to be called when you are dealing with a CommonRequestBuilder
variable, as it does nothing.
Upvotes: 1
Reputation: 37377
In C# 9.0 (which is latest release, so please make sure you have it, it is shipped with VS 16.8.2) you can use covariant overrides, which would perfectly fit here. This would allow you to make such changes:
Just make method WithExtras
virtual and override it in base class with return type of SpecificRequestBuilder
, like below
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;
}
}
Now statement
_specificRequestBuilder.WithExtras().WithDetails();
becomes completely valid.
Upvotes: 2
Reputation: 8743
You'll have to use the new
keyword in your SpecificRequestBuilder
to have a method with a different return type:
public class SpecificRequestBuilder : CommonRequestBuilder
{
public SpecificRequestBuilder WithDetails()
{
// add some stuff to _request
return this;
}
public new SpecificRequestBuilder WithExtras()
{
return (SpecificRequestBuilder)base.WithExtras();
}
}
Online demo: https://dotnetfiddle.net/tU4hEM
Upvotes: 2
Reputation: 4024
You can declare WithDetails on the base as a virtual method that can be overwritten.
public virtual CommonRequestBuilder WithDetails()
Build also has to be virtual on the base to be overwritten
public BigRequest Build()
You'll have the problem that WithDetails on the derived class cannot returnSpecialRequestBuilder.
I would create an interface that can be implemented by various implementations. Unless sticking to working with CommonRequestBuilder (or BaseRequestBuilder) works well enough.
public interface IRequestBuilder
{
IRequestBuilder WithExtras();
IRequestBuilder WithDetails();
BigRequest Build();
}
public class CommonRequestBuilder : IRequestBuilder
{
// ...
}
public class SpecificRequestBuilder : CommonRequestBuilder, IRequestBuilder
{
// ...
}
Upvotes: 0