Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How create Fluent Interface in C# with some limitation for some methods?

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.

like image 925
HamedFathi Avatar asked Apr 26 '16 10:04

HamedFathi


2 Answers

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

like image 147
Macke Avatar answered Nov 07 '22 19:11

Macke


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.

like image 36
Meirion Hughes Avatar answered Nov 07 '22 21:11

Meirion Hughes