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

发布于 2024-08-25 19:05:21 字数 1152 浏览 6 评论 0原文

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

没关系,但如果可能的话,我想让步骤命名可选。

我在此处找到了另一篇文章,它看起来很有趣并且很有前途。 你有什么意见?我认为原来的解决方案是完全不可接受的,是吗?

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

对我来说,这一切的重点是仅在一处定义这些操作方法。因此,引用链接中使用泛型和扩展方法的解决方案似乎是迄今为止最好的方法。

public class StepClause
{
    public NamedStepClause Action1() {}

    public NamedStepClause Action2() {}
}

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

Basically, I want to be able to do something like this:

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

So, some "steps" are named and some are not.

The thing I do not like is that the StepClause has knowledge of its derived class NamedStepClause.

I tried a couple of things to make this sit better with me.
I tried to move things out to interfaces but then the problem just moved from the concrete to the interfaces - INamedStepClause still need to derive from IStepClause and IStepClause needs to return INamedStepClause to be able to call Step().
I could also make Step() part of a completely separate type. Then we do not have this problem and we'd have:

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

Which is ok but I'd like to make the step-naming optional if possible.

I found this other post on SO here which looks interesting and promising.
What are your opinions? I'd think the original solution is completely unacceptable or is it?

By the way, those action methods will take predicates and functors and I don't think I want to take an additional parameter for naming the step there.

The point of it all is, for me, is to only define these action methods in one place and one place only. So the solutions from the referenced link using generics and extension methods seem to be the best approaches so far.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

街角卖回忆 2024-09-01 19:05:21

我给你两个选择。

选项 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,因为它可以让您了解类的内部运作方式。使用扩展方法意味着您可能需要对您的类进行某种外部访问,从而破坏封装。

祝你好运!

I'll give you two options.

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

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

Both options compile and give you the fluent interface that you're looking for. I'm more inclined to go with option A as it gives you access to the inner workings of the class. Using extension methods means you may need to give some sort of external access to your class thus breaking encapsulation.

Good luck!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文