There are a lot of parameters I would like to configure for my Analysis object, so having some kind of composition helpers is a big thing to simplify proper use of it. Let’s say that we have only 3 parameters for the sake of clarity (there are a way more, but amount of code in C# is overwhelming):
public double WaterTableDepth { get; private set; }
public bool UeqProfile { get; private set; }
public bool UWProfile { get; private set; }
I would like to keep them in an immutable object with two ways to specify parameters:
- base new
Analysison an existing one overriding random parameters (builder scenario) - Consequently specify all the parameters without a chance to miss one (wizard scenario)
Builder scenario might look like this:
var analysis1 = Analysis.Default
.Customize()
.UseUeqProfile()
.IgnoreUWProfile()
.Build();
Or
var analysis2 = analysis1
.Customize()
.WithWaterTableDepth(200)
.UseUWProfile()
.Build();
So order/amount of parameters to override is not under C# control:
Wizard scenario requires all the parameters to be specified in the predetermined order:
var analysis3 = Analysis.Wizard
.WithWaterTableDepth(20)
.UseUeqProfile()
.IgnoreUWProfile()
.Build();
C# editor always suggests the next step:
Here is the implementation of the Analysis class:
public partial class Analysis
{
public static readonly Analysis Default = Wizard
.WithoutGroundWater()
.IgnoreUeqProfile()
.IgnoreUWProfile()
.Build();
Analysis()
{
}
Analysis(Analysis source)
{
WaterTableDepth = source.WaterTableDepth;
UeqProfile = source.UeqProfile;
UWProfile = source.UWProfile;
}
public double WaterTableDepth { get; private set; }
public bool UeqProfile { get; private set; }
public bool UWProfile { get; private set; }
}
Where builder API is:
public interface IAnalysisBuilder
{
IAnalysisBuilder WithWaterTableDepth(double value);
IAnalysisBuilder WithoutGroundWater();
IAnalysisBuilder UseUeqProfile();
IAnalysisBuilder IgnoreUeqProfile();
IAnalysisBuilder UseUWProfile();
IAnalysisBuilder IgnoreUWProfile();
Analysis Build();
}
And defined in Analysis as well:
public partial class Analysis : IAnalysisBuilder
{
public IAnalysisBuilder Customize() => this;
IAnalysisBuilder IAnalysisBuilder.WithoutGroundWater() =>
new Analysis(this) { WaterTableDepth = 1000000 };
IAnalysisBuilder IAnalysisBuilder.WithWaterTableDepth(double value) =>
new Analysis(this) { WaterTableDepth = value };
IAnalysisBuilder IAnalysisBuilder.IgnoreUeqProfile() =>
new Analysis(this) { UeqProfile = false };
IAnalysisBuilder IAnalysisBuilder.UseUeqProfile() =>
new Analysis(this) { UeqProfile = true };
IAnalysisBuilder IAnalysisBuilder.IgnoreUWProfile() =>
new Analysis(this) { UWProfile = false };
IAnalysisBuilder IAnalysisBuilder.UseUWProfile() =>
new Analysis(this) { UWProfile = true };
Analysis IAnalysisBuilder.Build() => this;
}
Here comes the wizard API:
public interface IAnalysisWaterTableDepth
{
IAnalysisUeqProfile WithWaterTableDepth(double value);
IAnalysisUeqProfile WithoutGroundWater();
}
public interface IAnalysisUeqProfile
{
IAnalysisUWProfile UseUeqProfile();
IAnalysisUWProfile IgnoreUeqProfile();
}
public interface IAnalysisUWProfile
{
IAnalysisWizard UseUWProfile();
IAnalysisWizard IgnoreUWProfile();
}
public interface IAnalysisWizard
{
Analysis Build();
}
And implementation (in the same class):
public partial class Analysis :
IAnalysisWaterTableDepth,
IAnalysisUeqProfile,
IAnalysisUWProfile,
IAnalysisWizard
{
public static readonly IAnalysisWaterTableDepth Wizard = new Analysis();
IAnalysisUeqProfile IAnalysisWaterTableDepth.WithoutGroundWater() =>
new Analysis(this) { WaterTableDepth = 1000000 };
IAnalysisUeqProfile IAnalysisWaterTableDepth.WithWaterTableDepth(double value) =>
new Analysis(this) { WaterTableDepth = value };
IAnalysisUWProfile IAnalysisUeqProfile.IgnoreUeqProfile() =>
new Analysis(this) { UeqProfile = false };
IAnalysisUWProfile IAnalysisUeqProfile.UseUeqProfile() =>
new Analysis(this) { UeqProfile = true };
IAnalysisWizard IAnalysisUWProfile.IgnoreUWProfile() =>
new Analysis(this) { UWProfile = false };
IAnalysisWizard IAnalysisUWProfile.UseUWProfile() =>
new Analysis(this) { UWProfile = true };
Analysis IAnalysisWizard.Build() => this;
}
It is easy to consume, ISP rules, but amount of implementation code is crazy...

