I'm currently learning the builder pattern and want to require some sort of order/options of methods, and mainly make this visible in intellisense, so not (just) with exceptions on wrong implementations.
At first this doesn't seem too difficult when using interfaces, but i can't figure out how to it with the first method used, so for example always require builder.first()
, because the builder class needs to implement all the interfaces to make this work and therefore all methods are initially visible. Is it possible to "enforce" the first method apart from throwing an exception?
Example:
public interface IFirst
{
ISecond First();
}
public interface ISecond
{
IThird Second();
}
public interface IThird
{
int Third();
}
public class Builder : IFirst, ISecond, IThird
{
private int _counter = 0;
public ISecond First()
{
_counter++;
return this;
}
public IThird Second()
{
_counter++;
return this;
}
public int Third()
{
return _counter;
}
}
public class BuilderConsumer
{
public void Build()
{
var builder = new Builder();
// goal
var count = builder.First().Second().Third();
// currently also possible
var count2 = builder.Second().Third();
var count3 = builder.Third();
}
}
because the builder class needs to implement all the interfaces to make this work
Why does it need to implement all interfaces? The most natural solution would be use different classes:
public class BuilderFirst : IFirst
{
private int _counter = 0;
public ISecond First() => new BuilderSecond(_counter+1);
}
public class BuilderSecond : ISecond
{
private int _counter = 0;
public BuilderSecond(int counter) => _counter = counter;
public IThird Second() => new BuilderThird(_counter +1);
}
public class BuilderThird : IThird
{
private int _counter = 0;
public BuilderThird (int counter) => _counter = counter;
public int Third() => _counter;
}
This is a little bit more code, but it helps enforce the call order.
Splitting your builder into multiple classes like this can also be useful in other cases. A related example would be LINQ OrderBy that produces a IOrderedEnumerable<T>
, so that you can write:
myList
.OrderBy(p => p.Property1)
.ThenBy(p => p.Property2)