I am currently writing a Fluent API (pretty new to this pattern). I am unsure what is the best practice around making sure that dependant data is set via previous method chaining prior to the final execution.
Given the below API.
public class Processor : IFluentProcessor
{
private string _connectionName = String.Empty;
private string _whereClause = String.Empty;
public IFluentProcessor ConnectionName(string connectionName)
{
_connectionName = connectionName;
return this;
}
public IFluentProcessor FilterClause(string whereClause)
{
_whereClause = whereClause;
return this;
}
public bool Execute(out string errorMessage)
{
errorMessage = String.Empty;
try
{
//Ideally i would like to make sure that these variables have been set prior to this execution
var client = new dbContext(_connectionName);
var items = client.Where(_whereClause).Select(m => m).ToList();
foreach (var item in items)
{
//process the items here.
}
return true;
}
catch (Exception ex)
{
errorMessage = ex.Message;
return false;
}
}
}
public interface IFluentProcessor
{
IFluentWorker ConnectionName(string connectionName);
IFluentProcessor FilterClause(string whereClause);
bool Execute(out string errorMessage);
}
Is there a way of insuring that the 'configuration' methods have been previously chained prior to calling the execute method. Rather than just validating the items within the Execute method.
For strict ordering, you could split the interfaces (foregive me the lack of inspiration when naming, you get the idea):
public interface IFluentProcessor1
{
IFluentProcessor2 ConnectionName(string connectionName);
}
public interface IFluentProcessor2
{
IFluentProcessor3 FilterClause(string whereClause);
}
public interface IFluentProcessor3
{
bool Execute(out string errorMessage);
}
Combine that with a private constructor + factory method to instantiate the fluent interface:
public class Processor : IFluentProcessor1, IFluentProcessor2, IFluentProcessor3
{
private string _connectionName = String.Empty;
private string _whereClause = String.Empty;
private Processor() {}
public static IFluentProcessor1 Create() { return new Processor(); }
// ...
}
This does fix the order in which the methods are called, it does not allow to switch the calls to FilterClause
and ConnectionName
:
string errorMessage;
var result = Processor.Create()
.ConnectionName("my connection")
.FilterClause("my filter")
.Execute(out errorMessage);