I have a solution for a chain of interfaces; the only problem with my solution is that it grows in complexity (scale) with each new method that you want to support. But it makes a really awesome API for the user.
Let's consider that you have 3 methods, A, B and C, and you want to use them in a chain.
Let's also consider that you do not want to be able to call any method more than once.
eg.
new Builder().A().B().C();
This can be achieved with some serious surprise:
public class Builder : A<Not_A>, B<Not_B>, C<Not_C>, Not_A, Not_B, Not_C, Not_AB, Not_BC, Not_AC, Empty { Not_AB A<Not_AB>.A() { return (Not_AB)A(); } Not_AC A<Not_AC>.A() { return (Not_AC)A(); } Empty A<Empty>.A() { return (Empty)A(); } public Not_A A() { return (Not_A)this; } Not_AB B<Not_AB>.B() { return (Not_AB)B(); } Not_BC B<Not_BC>.B() { return (Not_BC)B(); } Empty B<Empty>.B() { return (Empty)B(); } public Not_B B() { return (Not_B)this; } Not_AC C<Not_AC>.C() { return (Not_AC)C(); } Not_BC C<Not_BC>.C() { return (Not_BC)C(); } Empty C<Empty>.C() { return (Empty)C(); } public Not_C C() { return (Not_C)this; } } public interface Empty { } public interface A<TRemainder> { TRemainder A(); } public interface B<TRemainder> { TRemainder B(); } public interface C<TRemainder> { TRemainder C(); } public interface Not_A : B<Not_AB>, C<Not_AC> { } public interface Not_B : A<Not_AB>, C<Not_BC> { } public interface Not_C : A<Not_AC>, B<Not_BC> { } public interface Not_AB : C<Empty> { } public interface Not_BC : A<Empty> { } public interface Not_AC : B<Empty> { }
And then, mix it with Chris Shain's awesomeness to use the action stack!
I decided to implement it. Note that you cannot call any method twice with this solution chain. I put your When method as an extension method.
Here is the call code:
int level = 5; var ninja = NinjaBuilder .CreateNinja() .Named("Ninja Boy") .AtLevel(level) .WithShurikens(10) .WithSkill(Skill.HideInShadows) .When(n => n.Level > 3) .Build();
Here are my ninja classes and skills:
public class Ninja { public string Name { get; set; } public int Level { get; set; } public int Shurikens { get; set; } public Skill Skill { get; set; } } public enum Skill { None = 1, HideInShadows }
This is the NinjaBuilder class:
public class NinjaBuilder : NinjaBuilder_Sans_Named { public static NinjaBuilder CreateNinja() { return new NinjaBuilder(); } public Stack<Action<Ninja>> _buildActions; public NinjaBuilder() { _buildActions = new Stack<Action<Ninja>>(); } public override Ninja Build() { var ninja = new Ninja(); while (_buildActions.Count > 0) { _buildActions.Pop()(ninja); } return ninja; } public override void AddCondition(Func<Ninja, bool> condition) { if (_buildActions.Count == 0) return; var top = _buildActions.Pop(); _buildActions.Push(n => { if (condition(n)) { top(n); } }); } public override Sans_Named_NinjaBuilder Named(string name) { _buildActions.Push(n => n.Name = name); return this; } public override Sans_AtLevel_NinjaBuilder AtLevel(int level) { _buildActions.Push(n => n.Level = level); return this; } public override Sans_WithShurikens_NinjaBuilder WithShurikens(int shurikenCount) { _buildActions.Push(n => n.Shurikens = shurikenCount); return this; } public override Sans_WithSkill_NinjaBuilder WithSkill(Skill skillType) { _buildActions.Push(n => n.Skill = skillType); return this; } }
And the rest of this code is just the overhead to make conversions and calls:
public abstract class NinjaBuilderBase : EmptyNinjaBuilder, Named_NinjaBuilder<Sans_Named_NinjaBuilder>, AtLevel_NinjaBuilder<Sans_AtLevel_NinjaBuilder>, WithShurikens_NinjaBuilder<Sans_WithShurikens_NinjaBuilder>, WithSkill_NinjaBuilder<Sans_WithSkill_NinjaBuilder> { public abstract void AddCondition(Func<Ninja, bool> condition); public abstract Ninja Build(); public abstract Sans_WithSkill_NinjaBuilder WithSkill(Skill skillType); public abstract Sans_WithShurikens_NinjaBuilder WithShurikens(int shurikenCount); public abstract Sans_AtLevel_NinjaBuilder AtLevel(int level); public abstract Sans_Named_NinjaBuilder Named(string name); } public abstract class NinjaBuilder_Sans_WithSkill : NinjaBuilderBase, Sans_WithSkill_NinjaBuilder { Sans_Named_WithSkill_NinjaBuilder Named_NinjaBuilder<Sans_Named_WithSkill_NinjaBuilder>.Named(string name) { return (Sans_Named_WithSkill_NinjaBuilder)Named(name); } Sans_AtLevel_WithSkill_NinjaBuilder AtLevel_NinjaBuilder<Sans_AtLevel_WithSkill_NinjaBuilder>.AtLevel(int level) { return (Sans_AtLevel_WithSkill_NinjaBuilder)AtLevel(level); } Sans_WithShurikens_WithSkill_NinjaBuilder WithShurikens_NinjaBuilder<Sans_WithShurikens_WithSkill_NinjaBuilder>.WithShurikens(int shurikenCount) { return (Sans_WithShurikens_WithSkill_NinjaBuilder)WithShurikens(shurikenCount); } } public abstract class NinjaBuilder_Sans_WithShurikens : NinjaBuilder_Sans_WithSkill, Sans_WithShurikens_NinjaBuilder, Sans_WithShurikens_WithSkill_NinjaBuilder { Sans_Named_WithShurikens_WithSkill_NinjaBuilder Named_NinjaBuilder<Sans_Named_WithShurikens_WithSkill_NinjaBuilder>.Named(string name) { return (Sans_Named_WithShurikens_WithSkill_NinjaBuilder)Named(name); } Sans_AtLevel_WithShurikens_WithSkill_NinjaBuilder AtLevel_NinjaBuilder<Sans_AtLevel_WithShurikens_WithSkill_NinjaBuilder>.AtLevel(int level) { return (Sans_AtLevel_WithShurikens_WithSkill_NinjaBuilder)AtLevel(level); } Sans_Named_WithSkill_NinjaBuilder Named_NinjaBuilder<Sans_Named_WithSkill_NinjaBuilder>.Named(string name) { return (Sans_Named_WithSkill_NinjaBuilder)Named(name); } Sans_AtLevel_WithShurikens_NinjaBuilder AtLevel_NinjaBuilder<Sans_AtLevel_WithShurikens_NinjaBuilder>.AtLevel(int level) { return (Sans_AtLevel_WithShurikens_NinjaBuilder)AtLevel(level); } Sans_WithShurikens_WithSkill_NinjaBuilder WithSkill_NinjaBuilder<Sans_WithShurikens_WithSkill_NinjaBuilder>.WithSkill(Skill skillType) { return (Sans_WithShurikens_WithSkill_NinjaBuilder)WithSkill(skillType); } } public abstract class NinjaBuilder_Sans_AtLevel : NinjaBuilder_Sans_WithShurikens, Sans_AtLevel_NinjaBuilder, Sans_AtLevel_WithShurikens_NinjaBuilder, Sans_AtLevel_WithSkill_NinjaBuilder, Sans_AtLevel_WithShurikens_WithSkill_NinjaBuilder { EmptyNinjaBuilder Named_NinjaBuilder<EmptyNinjaBuilder>.Named(string name) { return Named(name); } Sans_Named_AtLevel_WithSkill_NinjaBuilder Named_NinjaBuilder<Sans_Named_AtLevel_WithSkill_NinjaBuilder>.Named(string name) { return (Sans_Named_AtLevel_WithSkill_NinjaBuilder)Named(name); } Sans_AtLevel_WithShurikens_WithSkill_NinjaBuilder WithShurikens_NinjaBuilder<Sans_AtLevel_WithShurikens_WithSkill_NinjaBuilder>.WithShurikens(int shurikenCount) { return (Sans_AtLevel_WithShurikens_WithSkill_NinjaBuilder)WithShurikens(shurikenCount); } Sans_Named_AtLevel_WithShurikens_NinjaBuilder Named_NinjaBuilder<Sans_Named_AtLevel_WithShurikens_NinjaBuilder>.Named(string name) { return (Sans_Named_AtLevel_WithShurikens_NinjaBuilder)Named(name); } Sans_AtLevel_WithShurikens_WithSkill_NinjaBuilder WithSkill_NinjaBuilder<Sans_AtLevel_WithShurikens_WithSkill_NinjaBuilder>.WithSkill(Skill skillType) { return (Sans_AtLevel_WithShurikens_WithSkill_NinjaBuilder)WithSkill(skillType); } Sans_Named_AtLevel_NinjaBuilder Named_NinjaBuilder<Sans_Named_AtLevel_NinjaBuilder>.Named(string name) { return (Sans_Named_AtLevel_NinjaBuilder)Named(name); } Sans_AtLevel_WithShurikens_NinjaBuilder WithShurikens_NinjaBuilder<Sans_AtLevel_WithShurikens_NinjaBuilder>.WithShurikens(int shurikenCount) { return (Sans_AtLevel_WithShurikens_NinjaBuilder)WithShurikens(shurikenCount); } Sans_AtLevel_WithSkill_NinjaBuilder WithSkill_NinjaBuilder<Sans_AtLevel_WithSkill_NinjaBuilder>.WithSkill(Skill skillType) { return (Sans_AtLevel_WithSkill_NinjaBuilder)WithSkill(skillType); } } public abstract class NinjaBuilder_Sans_Named : NinjaBuilder_Sans_AtLevel, Sans_Named_NinjaBuilder, Sans_Named_AtLevel_NinjaBuilder, Sans_Named_WithShurikens_NinjaBuilder, Sans_Named_WithSkill_NinjaBuilder, Sans_Named_WithShurikens_WithSkill_NinjaBuilder, Sans_Named_AtLevel_WithSkill_NinjaBuilder, Sans_Named_AtLevel_WithShurikens_NinjaBuilder { EmptyNinjaBuilder WithSkill_NinjaBuilder<EmptyNinjaBuilder>.WithSkill(Skill skillType) { return (EmptyNinjaBuilder)WithSkill(skillType); } EmptyNinjaBuilder WithShurikens_NinjaBuilder<EmptyNinjaBuilder>.WithShurikens(int shurikenCount) { return (EmptyNinjaBuilder)WithShurikens(shurikenCount); } EmptyNinjaBuilder AtLevel_NinjaBuilder<EmptyNinjaBuilder>.AtLevel(int level) { return (EmptyNinjaBuilder)AtLevel(level); } Sans_Named_AtLevel_WithShurikens_NinjaBuilder AtLevel_NinjaBuilder<Sans_Named_AtLevel_WithShurikens_NinjaBuilder>.AtLevel(int level) { return (Sans_Named_AtLevel_WithShurikens_NinjaBuilder)AtLevel(level); } Sans_Named_WithShurikens_WithSkill_NinjaBuilder WithShurikens_NinjaBuilder<Sans_Named_WithShurikens_WithSkill_NinjaBuilder>.WithShurikens(int shurikenCount) { return (Sans_Named_WithShurikens_WithSkill_NinjaBuilder)WithShurikens(shurikenCount); } Sans_Named_WithShurikens_WithSkill_NinjaBuilder WithSkill_NinjaBuilder<Sans_Named_WithShurikens_WithSkill_NinjaBuilder>.WithSkill(Skill skillType) { return (Sans_Named_WithShurikens_WithSkill_NinjaBuilder)WithSkill(skillType); } Sans_Named_AtLevel_WithShurikens_NinjaBuilder WithShurikens_NinjaBuilder<Sans_Named_AtLevel_WithShurikens_NinjaBuilder>.WithShurikens(int shurikenCount) { return (Sans_Named_AtLevel_WithShurikens_NinjaBuilder)WithShurikens(shurikenCount); } Sans_Named_AtLevel_WithSkill_NinjaBuilder WithSkill_NinjaBuilder<Sans_Named_AtLevel_WithSkill_NinjaBuilder>.WithSkill(Skill skillType) { return (Sans_Named_AtLevel_WithSkill_NinjaBuilder)WithSkill(skillType); } Sans_Named_AtLevel_NinjaBuilder AtLevel_NinjaBuilder<Sans_Named_AtLevel_NinjaBuilder>.AtLevel(int level) { return (Sans_Named_AtLevel_NinjaBuilder)AtLevel(level); } Sans_Named_WithShurikens_NinjaBuilder WithShurikens_NinjaBuilder<Sans_Named_WithShurikens_NinjaBuilder>.WithShurikens(int shurikenCount) { return (Sans_Named_WithShurikens_NinjaBuilder)WithShurikens(shurikenCount); } Sans_Named_WithSkill_NinjaBuilder WithSkill_NinjaBuilder<Sans_Named_WithSkill_NinjaBuilder>.WithSkill(Skill skillType) { return (Sans_Named_WithSkill_NinjaBuilder)WithSkill(skillType); } } public static class NinjaBuilderExtension { public static TBuilderLevel When<TBuilderLevel>(this TBuilderLevel ths, Func<Ninja, bool> condition) where TBuilderLevel : EmptyNinjaBuilder { ths.AddCondition(condition); return ths; } } public interface EmptyNinjaBuilder { void AddCondition(Func<Ninja, bool> condition); Ninja Build(); } public interface Named_NinjaBuilder<TRemainder> { TRemainder Named(string name); } public interface AtLevel_NinjaBuilder<TRemainder> { TRemainder AtLevel(int level);} public interface WithShurikens_NinjaBuilder<TRemainder> { TRemainder WithShurikens(int shurikenCount); } public interface WithSkill_NinjaBuilder<TRemainder> { TRemainder WithSkill(Skill skillType); }