Свободный дизайн интерфейса и запах кода - PullRequest
6 голосов
/ 17 марта 2010
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 здесь , который выглядит интересным и многообещающим. Каковы ваши мнения? Я думаю, что оригинальное решение совершенно неприемлемо или это?

Кстати, эти методы действия будут принимать предикаты и функторы, и я не думаю, что мне нужен дополнительный параметр для обозначения там шага.

Смысл всего этого для меня состоит в том, чтобы определять эти методы действия только в одном месте и только в одном месте. Таким образом, решения из ссылочной ссылки, использующие обобщенные методы и методы расширения, пока кажутся лучшими подходами.

1 Ответ

9 голосов
/ 17 марта 2010

Я дам вам два варианта.

Вариант А

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

Обе опции компилируются и дают вам свободный интерфейс, который вы ищете. Я более склонен использовать вариант А, поскольку он дает вам доступ к внутренним работам класса. Использование методов расширения означает, что вам может потребоваться предоставить некоторый внешний доступ к вашему классу, что нарушит инкапсуляцию.

Удачи!

...