Беглый дизайн интерфейса и запах кода

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 () частью совершенно отдельного типа. Тогда у нас нет этой проблемы, и мы бы:

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

что нормально, но я хотел бы сделать шаг-именование необязательным, если это возможно.

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

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

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

1 ответов


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

Вариант 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, поскольку он дает вам доступ к внутренней работе класса. Использование методов расширения означает, что вам может потребоваться предоставить какой-то внешний доступ к вашему классу, тем самым нарушив инкапсуляцию.

удачи!