2012-04-06 64 views
46

我想知道什麼是執行使用方法在Builder對象鏈接一個流暢的界面一個.When條件最好的方法是什麼?條件構建器方法鏈接流利的接口

比如我將如何實現在下面的例子中.WithSkill()和​​方法:

var level = 5; 

var ninja = NinjaBuilder 
    .CreateNinja() 
    .Named("Ninja Boy") 
    .AtLevel(level) 
    .WithShurikens(10) 
    .WithSkill(Skill.HideInShadows) 
     .When(level > 3) 
    .Build() 

更新 - 可將樣品溶液中找到here

回答

70

我會做的是有NinjaBuilder保持業務爲代表的列表,而不是將它們,只適用於他們,當.Build被調用。這可以讓你使它們成爲有條件的:

public class NinjaBuilder { 
    List<Action<Ninja>> builderActions = new List<Action<Ninja>>(); 

    public Ninja Build() { 
     var ninja = new Ninja(); 
     builderActions.ForEach(ba => ba(ninja)); 
     return ninja; 
    } 

    public NinjaBuilder WithShurikens(int numShirukens) { 
     builderActions.Add(n=>n.Shirukens = numShirukens); 
     return this; 
    } 

    public NinjaBuilder When(Boolean condition) { 
     if (!condition) // If the condition is not met, remove the last action 
      builderActions.Remove(builderActions.Length - 1); 
     return this; 
    } 
} 

當然,這裏假設條件在構建器創建時是恆定的。如果你想讓它不恆定,你可以做這樣的事情,而不是:

public NinjaBuilder When(Func<Boolean> condition) { 
     var oldAction = builderActions[builderActions.Length - 1]; 
     builderActions[builderActions.Length - 1] = n => condition() ? oldAction(n) : n; 
     return this; 
    } 

如果你想When有所更多的編譯器檢查,可以使保護builderActions,做這樣的事情:

public class ConditionalNinjaBuilder : NinjaBuilder { 
    public ConditionalNinjaBuilder(NinjaBuilder wrappedBuilder) {    
     // Since someone might call .WithShirukens on the wrapping 
     // builder directly, we should make sure that our actions 
     // list is the same instance as the one in our wrapped builder 
     builderActions = wrappedBuilder.builderActions; 
    } 

    public ConditionalNinjaBuilder When(Func<Boolean> condition) { 
     var oldAction = builderActions[builderActions.Length - 1]; 
     builderActions[builderActions.Length - 1] = n => condition() ? oldAction(n) : n; 
     return this; 
    } 
} 

,並有原始操作返回ConditionalNinjaBuilder:

public ConditionalNinjaBuilder WithShurikens(int numShirukens) { 
     builderActions.Add(n=>n.Shirukens = numShirukens); 
     return new ConditionalNinjaBuilder(this); 
    } 

這樣,你只能先通話後稱.When另一種方法。這也具有可能允許嵌套/複合條件的附加優點/複雜性。讓人驚訝。

+2

請注意,您現在明確地假設何時可以使用'When' - 只在您的條件之後。您不強制執行此操作,因此可能會出現錯誤。此外,您當前的方法簽名是錯誤的 - 每個應該返回一個'NinjaBuilder',否則這不是一個流暢的接口 – BrokenGlass 2012-04-06 17:49:22

+0

修復了方法sigs。我會刺戳明確的。 – 2012-04-06 17:50:35

+0

讓我知道它是怎麼回事 - 我過去使用不同的接口(和相應的擴展方法)來執行順序,我很樂意看到更優雅的東西 – BrokenGlass 2012-04-06 17:59:45

3

你可以在你的方法,有條件的可選參數,它是true默認:

.WithSkill(Skill.HideInShadows, when: level > 3) 

當然,這將是非常具體的WithSkill方法:

public NinjaBuilder WithSkill(Skill skill, bool when = true) { 
    if (!when) return this; 
    // ... 
} 

您可以添加它到其他想要成爲條件的方法。

另一種選擇是有一個巢建設者的條件部分的方法:

public NinjaBuilder When(bool condition, Action<NinjaBuilder> then) { 
    if (condition) then(this); 
    return this; 
} 

然後,你可以寫這樣的:

.When(level > 3, 
    then: _ => _.WithSkill(Skill.HideInShadows)) 

或者這樣:

.When(level > 3, _=>_ 
    .WithSkill(Skill.HideInShadows) 
) 

這是更通用的,可用於建設者的任何方法。

你甚至可以添加一個可選的「其他人」:

public NinjaBuilder When(bool condition, Action<NinjaBuilder> then, Action<NinjaBuilder> otherwise = null) { 
    if (condition) { 
    then(this); 
    } 
    else if (otherwise != null) { 
    otherwise(this); 
    } 
    return this; 
} 

,或作爲"mixin"

public interface MBuilder {} 
public static class BuilderExtensions { 
    public static TBuilder When<TBuilder>(this TBuilder self, bool condition, Action<TBuilder> then, Action<TBuilder> otherwise = null) 
    where TBuilder : MBuilder 
    { 
    if (condition) { 
     then(self); 
    } 
    else if (otherwise != null) { 
     otherwise(self); 
    } 
    return self; 
    } 
} 

public class NinjaBuilder : MBuilder ... 

這當然是一種方式來創建「如果」語句的方法調用。其他方法也可以工作:

.When(level > 3) // enter "conditional" context 
    .WithSkill(Skill.HideInShadows) 
.End() // exit "conditional" context 

在這種情況下,製造商都可以跟蹤它是否應該忽視在「條件」方面所做如果條件是假的任何方法調用。 When將進入上下文,End將退出它。您還可以調用Otherwise()來標記「其他」上下文。有趣的是,你還可以涵蓋其他語句這個樣子,像循環:

.Do(times: 10) // add 10 shurikens 
    .AddShuriken() 
.End() 

在這種情況下,在「循環」中所提的呼叫必須被記錄和回放時End被調用次數所需數量。

所以,上下文是一種狀態建造者可以在;他們改變它的運作方式。您也可以嵌套上下文,使用堆棧來跟蹤它們。而且你應該檢查某些電話在某些州是否有效,如果不是,可能會引發異常。

5

,你可以考慮寫隨着重載版本,並在第二,採取一凡作爲參數:

var level = 5; 
var ninja = NinjaBuilder  
    .CreateNinja() 
    .Named("Ninja Boy") 
    .AtLevel(level) 
    .WithShurikens(10) 
    .WithSkill(Skill.HideInShadows, Where.Level(l => l > 3)) 
    .Build() 

當然,這個的前提是,你要在哪裏寫的概念一個單獨的對象完全,基本上是這樣的:

public sealed static class Where 
{ 
    public bool Defense (Func<int, bool> predicate) { return predicate(); } 
    public bool Dodge (Func<int, bool> predicate) { return predicate(); } 
    public bool Level (Func<int, bool> predicate) { return predicate(); } 

} 
8

我有一個接口鏈接解決方案;我的解決方案唯一的問題是,它隨着您想要支持的每種新方法的複雜性(規模)而增長。但是,它爲用戶帶來了非常棒的API。

讓我們考慮你有3種方法,A,B和C,並且你想在一個鏈中使用它們。

讓我們也考慮一下,您不希望能夠多次調用任何方法。

例如

new Builder().A().B().C(); // OK 
new Builder().A().B().A(); // Not OK 

這可以通過一些嚴重的迷死人來完成:

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> { } 

,然後與克里斯沙恩的迷死這個混合使用的動作堆!

我決定實施它。請注意,您現在無法使用此鏈接解決方案調用任何方法兩次。我把你的When方法作爲擴展方法。

下面是調用代碼:

int level = 5; 
    var ninja = NinjaBuilder 
     .CreateNinja() 
     .Named("Ninja Boy") 
     .AtLevel(level) 
     .WithShurikens(10) 
     .WithSkill(Skill.HideInShadows) 
      .When(n => n.Level > 3) 
     .Build(); 

這是我的忍者技能類:

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 
} 

這是NinjaBuilder類:

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; 
    } 
} 

並在此休息代碼只是用於使轉換和調用工作的開銷:

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); } 

// level one reductions 
public interface Sans_Named_NinjaBuilder : 
    AtLevel_NinjaBuilder<Sans_Named_AtLevel_NinjaBuilder>, 
    WithShurikens_NinjaBuilder<Sans_Named_WithShurikens_NinjaBuilder>, 
    WithSkill_NinjaBuilder<Sans_Named_WithSkill_NinjaBuilder>, 
    EmptyNinjaBuilder { } 
public interface Sans_AtLevel_NinjaBuilder : 
    Named_NinjaBuilder<Sans_Named_AtLevel_NinjaBuilder>, 
    WithShurikens_NinjaBuilder<Sans_AtLevel_WithShurikens_NinjaBuilder>, 
    WithSkill_NinjaBuilder<Sans_AtLevel_WithSkill_NinjaBuilder>, 
    EmptyNinjaBuilder { } 
public interface Sans_WithShurikens_NinjaBuilder : 
    Named_NinjaBuilder<Sans_Named_WithSkill_NinjaBuilder>, 
    AtLevel_NinjaBuilder<Sans_AtLevel_WithShurikens_NinjaBuilder>, 
    WithSkill_NinjaBuilder<Sans_WithShurikens_WithSkill_NinjaBuilder>, 
    EmptyNinjaBuilder { } 
public interface Sans_WithSkill_NinjaBuilder : 
    Named_NinjaBuilder<Sans_Named_WithSkill_NinjaBuilder>, 
    AtLevel_NinjaBuilder<Sans_AtLevel_WithSkill_NinjaBuilder>, 
    WithShurikens_NinjaBuilder<Sans_WithShurikens_WithSkill_NinjaBuilder>, 
    EmptyNinjaBuilder { } 

// level two reductions 
// Named 
public interface Sans_Named_AtLevel_NinjaBuilder : 
    WithShurikens_NinjaBuilder<Sans_Named_AtLevel_WithShurikens_NinjaBuilder>, 
    WithSkill_NinjaBuilder<Sans_Named_AtLevel_WithSkill_NinjaBuilder>, 
    EmptyNinjaBuilder { } 
public interface Sans_Named_WithShurikens_NinjaBuilder : 
    AtLevel_NinjaBuilder<Sans_Named_AtLevel_WithShurikens_NinjaBuilder>, 
    WithSkill_NinjaBuilder<Sans_Named_WithShurikens_WithSkill_NinjaBuilder>, 
    EmptyNinjaBuilder { } 
public interface Sans_Named_WithSkill_NinjaBuilder : 
    AtLevel_NinjaBuilder<Sans_Named_AtLevel_WithShurikens_NinjaBuilder>, 
    WithShurikens_NinjaBuilder<Sans_Named_WithShurikens_WithSkill_NinjaBuilder>, 
    EmptyNinjaBuilder { } 
// AtLevel 
public interface Sans_AtLevel_WithShurikens_NinjaBuilder : 
    Named_NinjaBuilder<Sans_Named_AtLevel_WithShurikens_NinjaBuilder>, 
    WithSkill_NinjaBuilder<Sans_AtLevel_WithShurikens_WithSkill_NinjaBuilder>, 
    EmptyNinjaBuilder { } 
public interface Sans_AtLevel_WithSkill_NinjaBuilder : 
    Named_NinjaBuilder<Sans_Named_AtLevel_WithSkill_NinjaBuilder>, 
    WithShurikens_NinjaBuilder<Sans_AtLevel_WithShurikens_WithSkill_NinjaBuilder>, 
    EmptyNinjaBuilder { } 
// WithShurikens 
public interface Sans_WithShurikens_WithSkill_NinjaBuilder : 
    Named_NinjaBuilder<Sans_Named_WithShurikens_WithSkill_NinjaBuilder>, 
    AtLevel_NinjaBuilder<Sans_AtLevel_WithShurikens_WithSkill_NinjaBuilder>, 
    EmptyNinjaBuilder { } 

// level three reductions 
// Named 
public interface Sans_AtLevel_WithShurikens_WithSkill_NinjaBuilder : 
    Named_NinjaBuilder<EmptyNinjaBuilder>, 
    EmptyNinjaBuilder { } 
// AtLevel 
public interface Sans_Named_WithShurikens_WithSkill_NinjaBuilder : 
    AtLevel_NinjaBuilder<EmptyNinjaBuilder>, 
    EmptyNinjaBuilder { } 
// WithShurikens 
public interface Sans_Named_AtLevel_WithSkill_NinjaBuilder : 
    WithShurikens_NinjaBuilder<EmptyNinjaBuilder>, 
    EmptyNinjaBuilder { } 
// WithSkill 
public interface Sans_Named_AtLevel_WithShurikens_NinjaBuilder : 
    WithSkill_NinjaBuilder<EmptyNinjaBuilder>, 
    EmptyNinjaBuilder { } 
+0

這是主要的真棒,特別是如果加上爲jOOQ寫的東西在http://blog.jooq.org/2012/01/05/the-java-fluent-api-designer-crash-course/它顯示如何使用接口實現任何常規語言。請注意,您不需要此接口爆炸,您只需要一個接口來處理由常規語言表示的狀態機中的每個轉換。 – 2014-05-02 16:45:24