Reputation: 3969
See below codes :
new ConditionCreator()
.Add()
.Or()
.Add()
.And()
.Add()
I want to create a Fluent Interface for that But I need, after Add() method developer see Only Or() or And() and after one of these, see Only Add() method.
so no one can write a code like :
new ConditionCreator()
.Add()
.Add()
.Add()
.Or()
.And()
.Add()
.And()
.And()
I want to have a limitation for some methods can accept special methods and etc. I can write all methods in one class and return this for each one but that is not suitable !!!
Please guide me How write Advanced Fluent Interface class.
Upvotes: 8
Views: 1309
Reputation: 345
This seems to work.
public class ConditionCreator
{
private Decision decision;
public ConditionCreator() { decision = new Decision(this); }
public Decision Add() { return decision; }
public class Decision
{
private ConditionCreator creator;
public Decision(ConditionCreator creator) { this.creator = creator; }
public ConditionCreator And() { return creator; }
public ConditionCreator Or() { return creator; }
public Condition Create() { return new Condition(); }
}
}
And you're now restricted to patterns like this when you make the calls:
var condition = new ConditionCreator()
.Add()
.Or()
.Add()
.And()
.Add()
.Create();
Upvotes: 0
Reputation: 224
public class DoEqual
{
}
public interface ICanAddWhereValue
{
ICanAddWhereOrRun IsEqualTo(object value);
ICanAddWhereOrRun IsNotEqualTo(object value);
IBothEqual BothEqual ( object value );
}
public interface IBothEqual
{
DoEqual Excute();
}
public interface ICanAddWhereOrRun
{
ICanAddWhereValue Where(string columnName);
bool RunNow();
DoEqual Excute();
}
public interface ICanAddCondition
{
ICanAddWhereValue Where(string columnName);
bool AllRows();
}
namespace BuildAFluentInterface
{
public class WhereCondition
{
public enum ComparisonMethod
{
EqualTo,
NotEqualTo
}
public string ColumnName { get; private set; }
public ComparisonMethod Comparator { get; private set; }
public object Value { get; private set; }
public WhereCondition(string columnName, ComparisonMethod comparator, object value)
{
ColumnName = columnName;
Comparator = comparator;
Value = value;
}
}
}
using System.Collections.Generic;
namespace BuildAFluentInterface
{
public class DeleteQueryWithoutGrammar
{
private readonly string _tableName;
private readonly List<WhereCondition> _whereConditions = new List<WhereCondition>();
private string _currentWhereConditionColumn;
// Private constructor, to force object instantiation from the fluent method(s)
private DeleteQueryWithoutGrammar(string tableName)
{
_tableName = tableName;
}
#region Initiating Method(s)
public static DeleteQueryWithoutGrammar DeleteRowsFrom(string tableName)
{
return new DeleteQueryWithoutGrammar(tableName);
}
#endregion
#region Chaining Method(s)
public DeleteQueryWithoutGrammar Where(string columnName)
{
_currentWhereConditionColumn = columnName;
return this;
}
public DeleteQueryWithoutGrammar IsEqualTo(object value)
{
_whereConditions.Add(new WhereCondition(_currentWhereConditionColumn, WhereCondition.ComparisonMethod.EqualTo, value));
return this;
}
public DeleteQueryWithoutGrammar IsNotEqualTo(object value)
{
_whereConditions.Add(new WhereCondition(_currentWhereConditionColumn, WhereCondition.ComparisonMethod.NotEqualTo, value));
return this;
}
#endregion
#region Executing Method(s)
public void AllRows()
{
ExecuteThisQuery();
}
public void RunNow()
{
ExecuteThisQuery();
}
#endregion
private void ExecuteThisQuery()
{
// Code to build and execute the delete query
}
}
}
<br>
In Main Test with
public class myclass
{
private static void Main(string[] args)
{
DoEqual x3 =
DeleteQueryWithGrammar.DeleteRowsFrom("Account")
.Where("Admin")
.IsNotEqualTo("Admin")
.Where("Admin")
.BothEqual("X")
.Excute();
}
}
Upvotes: 0
Reputation: 26408
There is no real easy-way I know of to solve this. Perhaps T4 templating may help, but thus far I've always had to build-up the decision-tree, with an explicit interface at each node. For example; lets assume your decision tree is an infinite loop, then (implemented accordingly):
interface IStart<T>
{
IAndOr Add();
T End();
}
interface IAndOr<T>
{
IStart<T> And();
IStart<T> Or();
}
It gets difficult if you want a finite loop; say zero to two Adds:
interface IStart<T> : IFinish<T>
{
IAndOrFirst<T> Add();
}
interface IAndOrFirst<T>
{
ISecond<T> And();
ISecond<T> Or();
}
interface ISecond<T> : IFinish<T>
{
IAndOrSecond<T> Add();
}
interface IAndOrSecond <T>
{
IFinish<T> And();
IFinish<T> Or();
}
interface IFinish<T>
{
T End();
}
You can (explicitly) implement these in a single class that acts as the state machine:
class ConditionCreator <T> : IStart<T>, IFinish<T>, IAndOrFirst<T>, IAndOrSecond<T> {...}
where you'd return this
for Add()
And()
Or()
and maintain those state changes and order.
I'm hoping some answers this question with a better way that manually writing out each node.
Upvotes: 2
Reputation: 25680
To restrict things, you need to create and return one (of possibly several) "builder" objects that can do special operations, keeping a ref to the main class.
public class ConditionCreator
{
public ConditionCreator() { ... }
public SubConditionCreator Add() { ...; return new SubConditionCreator(this); }
internal ConditionCreator OnAdd() { ...; return this; };
internal ConditionCreator OnOr() { ...; return this; };
}
public class SubConditionCreator
{
private ConditionCreator _creator;
internal SubConditionCreator(ConditionCreator c) { _creator = c; }
public ConditionCreator And() { return _creator.OnAdd(); }
public ConditionCreator Or() { return _creator.OnOr(); }
}
Use internal access to restrict usage.
To avoid creating garbage, store a SubConditionCreator ref in main class
Upvotes: 4
Reputation: 5851
Consider returning an interface that contains only And()
and Or()
. For example:
public class ConditionCreator : IFluentAndOr
{
public IFluentAndOr And() { ... }
public IFluentAndOr Or() { ... }
}
public interface IFluentAndOr
{
IFluentAndOr And();
IFluentAndOr Or();
}
Upvotes: 0