C# 条件生成器方法链接 Fluent 接口
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10046629/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Conditional Builder Method Chaining Fluent Interface
提问by Ken Burkhardt
I was wondering what would be the best way to implement a .Whencondition in a fluent interfaceusing method chainingin a Builderobject?
我想知道使用对象中的方法链在流畅的接口中实现.When条件的最佳方法是什么?Builder
For instance how would I implement the .WithSkill()and .When()methods in the following example:
例如,我将如何在以下示例中实现.WithSkill()和.When()方法:
var level = 5;
var ninja = NinjaBuilder
.CreateNinja()
.Named("Ninja Boy")
.AtLevel(level)
.WithShurikens(10)
.WithSkill(Skill.HideInShadows)
.When(level > 3)
.Build()
Update- A sample solution can be found here.
更新- 可以在此处找到示例解决方案。
采纳答案by Chris Shain
What I'd do is have NinjaBuilderkeep the operations as a list of delegates, rather than applying them, and only apply them when .Buildis called. This would allow you to make them conditional:
我要做的是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.RemoveAt(builderActions.Length - 1);
return this;
}
}
Of course, this assumes that the condition is constant at the time of builder creation. If you want to make it non-constant, you could do something like this instead:
当然,这假设条件在构建器创建时是恒定的。如果你想让它变得非常量,你可以做这样的事情:
public NinjaBuilder When(Func<Boolean> condition) {
var oldAction = builderActions[builderActions.Length - 1];
builderActions[builderActions.Length - 1] = n => { if (condition()) { oldAction(n); } }
return this;
}
If you want Whenbe somewhat more compiler checked, you can make builderActions protected and do something like 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 => { if (condition()) { oldAction(n); } }
return this;
}
}
and have the original operations return a ConditionalNinjaBuilder:
并让原始操作返回一个 ConditionalNinjaBuilder:
public ConditionalNinjaBuilder WithShurikens(int numShirukens) {
builderActions.Add(n=>n.Shirukens = numShirukens);
return new ConditionalNinjaBuilder(this);
}
That way you can only call .Whenafter first calling another method. This has the additional advantage/complication of potentially allowing for nested/compounded conditionals, too. Yikes.
这样你只能.When在第一次调用另一个方法后调用。这也有可能允许嵌套/复合条件的额外优势/复杂性。哎呀。
回答by Jord?o
You could have a conditional optional parameter in your method that is trueby default:
您的方法中可以有一个条件可选参数,true默认情况下:
.WithSkill(Skill.HideInShadows, when: level > 3)
This will of course be very specific to the WithSkillmethod:
这当然将非常特定于该WithSkill方法:
public NinjaBuilder WithSkill(Skill skill, bool when = true) {
if (!when) return this;
// ...
}
You could add it to other methods that you want to be conditional too.
您也可以将它添加到其他您想要有条件的方法中。
Another option is to have a method that nests the conditional parts of the builder:
另一种选择是使用一种方法来嵌套构建器的条件部分:
public NinjaBuilder When(bool condition, Action<NinjaBuilder> then) {
if (condition) then(this);
return this;
}
Then you can write it like this:
然后你可以这样写:
.When(level > 3,
then: _ => _.WithSkill(Skill.HideInShadows))
Or like this:
或者像这样:
.When(level > 3, _=>_
.WithSkill(Skill.HideInShadows)
)
This is more generic and can be used with any methods of the builder.
这是更通用的,可以与构建器的任何方法一起使用。
You can even add an optional "else":
你甚至可以添加一个可选的“else”:
public NinjaBuilder When(bool condition, Action<NinjaBuilder> then, Action<NinjaBuilder> otherwise = null) {
if (condition) {
then(this);
}
else if (otherwise != null) {
otherwise(this);
}
return this;
}
Or, as a "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 ...
This is of course a way to create "if" statements as method calls. Other ways could also work:
这当然是一种创建“if”语句作为方法调用的方法。其他方法也可以:
.When(level > 3) // enter "conditional" context
.WithSkill(Skill.HideInShadows)
.End() // exit "conditional" context
In this case, the builder keeps track whether it should ignore any method calls that are done in a "conditional" context if the condition is false. Whenwould enter the context, Endwould exit it. You could also have an Otherwise()call to mark the "else" context. Interestingly enough, you could also cover other statements like this, like loops:
在这种情况下,如果条件为假,构建器会跟踪是否应该忽略在“条件”上下文中完成的任何方法调用。When将进入上下文,End将退出它。您还可以Otherwise()调用来标记“else”上下文。有趣的是,您还可以涵盖其他类似的语句,例如循环:
.Do(times: 10) // add 10 shurikens
.AddShuriken()
.End()
In this case, the calls made in "loop" context must be recorded and played-back the desired number of times when Endis called.
在这种情况下,必须记录在“循环”上下文中进行的调用,并在调用时播放所需的次数End。
So, contexts are a kind of statethe builder can be at; they change how it operates. You could also nest contexts, using a stack to keep track of them. And you should check if certain call are valid at certain states and maybe throw exceptions if they aren't.
因此,上下文是构建器可以处于的一种状态;他们改变了它的运作方式。您还可以嵌套上下文,使用堆栈来跟踪它们。您应该检查某些调用在某些状态下是否有效,如果不是,则可能抛出异常。
回答by Mike Hofer
You could consider writing overloaded versions of With, and in the second, take a Where as an argument:
您可以考虑编写 With 的重载版本,在第二个中,将 Where 作为参数:
var level = 5;
var ninja = NinjaBuilder
.CreateNinja()
.Named("Ninja Boy")
.AtLevel(level)
.WithShurikens(10)
.WithSkill(Skill.HideInShadows, Where.Level(l => l > 3))
.Build()
Of course, this is predicated on the notion that you're going to write Where as a separate object entirely, that essentially looks like this:
当然,这是基于您将把 Where 完全作为一个单独的对象来编写的概念,它本质上是这样的:
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(); }
}
回答by payo
I have a solution to interface chaining; the only problem with my solution is that it grows in complexity (scale) with every new method you want to support. But, it makes for a really awesome API for the user.
我有一个接口链接的解决方案;我的解决方案的唯一问题是,随着您想要支持的每一种新方法,它的复杂性(规模)都会增加。但是,它为用户提供了一个非常棒的 API。
Let us consider that you have 3 methods, A, B, and C, and you want to use them in a chain.
让我们考虑一下您有 3 个方法,A、B 和 C,并且您想在一个链中使用它们。
Let us also consider that you don't want to be able to call any method more than once.
让我们也考虑一下您不希望能够多次调用任何方法。
e.g.
例如
new Builder().A().B().C(); // OK
new Builder().A().B().A(); // Not OK
This can be accomplished with some serious awesomeness:
这可以通过一些严重的令人敬畏的方式来完成:
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 this with Chris Shain's awesomeness to use a stack of actions!
然后,将其与 Chris Shain 的精彩结合使用一系列动作!
I decided to implement it. Note that you can't call any method twice now with this chaining solution. I put your Whenmethod as an extension method.
我决定实施它。请注意,您现在无法使用此链接解决方案两次调用任何方法。我把你的When方法作为扩展方法。
Here is the calling 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 is my Ninja and Skill classes:
这是我的忍者和技能课程:
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:
这是 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;
}
}
And the rest of this code is just overhead to make the conversions and calls work:
此代码的其余部分只是使转换和调用工作的开销:
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 { }

