2012-04-06 54 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