流畅的界面设计和代码气味

时间:2010-03-17 02:47:44

标签: c# oop fluent-interface

public class StepClause
{
    public NamedStepClause Action1() {}

    public NamedStepClause Action2() {}
}

public class NamedStepClause : StepClause
{
    public StepClause Step(string name) {}
}

基本上,我希望能够做到这样的事情:

var workflow = new Workflow().Configure()
    .Action1()
    .Step("abc").Action2()
    .Action2()
    .Step("def").Action1();

因此,一些“步骤”被命名,而另一些则没有。

我不喜欢的是StepClause知道它的派生类NamedStepClause。

我尝试了几件让我坐得更好的事情。 我试图将事情移到接口但是然后问题刚刚从具体移到接口 - INamedStepClause仍然需要从IStepClause派生,而IStepClause需要返回INamedStepClause才能调用Step()。 我也可以将Step()作为完全独立类型的一部分。然后我们没有这个问题,我们有:

var workflow = new Workflow().Configure()
    .Step().Action1()
    .Step("abc").Action2()
    .Step().Action2()
    .Step("def").Action1();

哪个好,但我想尽可能选择步骤命名。

我在SO here上发现了另一篇看起来很有趣且很有希望的帖子。 你有什么看法?我认为最初的解决方案是完全不可接受的,还是它?

顺便说一句,这些动作方法将采用谓词和仿函数,我认为我不想采用额外的参数来命名那里的步骤。

对我而言,重点是只在一个地方和一个地方定义这些行动方法。因此,使用泛型和扩展方法的参考链接的解决方案似乎是迄今为止最好的方法。

1 个答案:

答案 0 :(得分:9)

我会给你两个选择。

选项A

var a = new A.NamedStepClause();

a.Action1()
    .Step("abc").Action2()
    .Action2()
    .Step("def").Action1();


namespace A
{
    public class StepClause<SC> where SC : StepClause<SC>
    {
        public SC Action1() { return null; }
        public SC Action2() { return null; }
    }

    public class NamedStepClause : StepClause<NamedStepClause>
    {
        public NamedStepClause Step(string name) { return null; }
    }
}

选项B

var b = new B.StepClause();

b.Action1()
    .Step("abc").Action2()
    .Action2()
    .Step("def").Action1();

namespace B
{
    public class StepClause
    {
        public StepClause Action1() { return null; }
        public StepClause Action2() { return null; }
    }

    public static class StepClauseExtensions
    {
        public static StepClause Step(this StepClause @this, string name)
        { return null; }
    }
}

这两个选项都可以编译并为您提供您正在寻找的流畅界面。我更倾向于使用选项A,因为它可以让您访问该类的内部工作。使用扩展方法意味着您可能需要对类进行某种外部访问,从而破坏封装。

祝你好运!