Является ли шаблон Visitor лучшим способом для рефакторинга перечислений доменов в классах? - PullRequest
2 голосов
/ 28 августа 2010

Если мы хотим реорганизовать перечисление (содержащееся в слое домена) в полиморфный класс, использование «простых» абстрактных методов может быть плохой идеей, если все переключатели и операторы, которые мы хотим реорганизовать, находятся внутри других слоев (например, бизнес или уровень представления), потому что мы могли бы в конечном итоге ссылаться на эти уровни внутри уровня домена:

public abstract class MyRefactoredEnum
{
    public abstract void DoSomething(IBusinnessObject aBizObject); //dependency to the biz. layer

    public abstract MvcHtmlString GetImgTag(); //dependency to presentation layer
}

(в приведенном выше примере у нас тоже может быть проблема "перекрестной ссылки")

Я обнаружил, что шаблон посетителей (http://en.wikipedia.org/wiki/Visitor_pattern) является правильным решением этой проблемы: на уровне домена мы определяем только интерфейс MyRefactoredEnum.IVisitor, а все остальные слои могут реализовывать своих посетителей. .

Единственная проблема: когда мы изменяем интерфейс MyRefactoredEnum.IVisitor (например, потому что мы добавили другой подкласс MyRefactoredEnum), мы должны модифицировать и перекомпилировать все проекты и решения, которые ссылаются на модель домена. Мы можем решить проблему, используя отражение (http://surguy.net/articles/visitor-with-reflection.xml), но оно может быть медленным ...

Есть ли лучший шаблон для рефакторинга перечисления?

PS: простите за мой ужасный английский:)

1 Ответ

5 голосов
/ 28 августа 2010

Вы можете предоставить реализацию по умолчанию для посетителей, у которой есть резервный метод:

abstract class Cheese
{
    public abstract void Visit(ICheeseVisitor visitor);
}

class Wensleydale : Cheese { ... }

class Gouda : Cheese { ... }

interface ICheeseVisitor
{
    void Visit(Wensleydale cheese);
    void Visit(Gouda cheese);
}

abstract class CheeseVisitor : ICheeseVisitor
{
    public virtual void Visit(Wensleydale cheese) { Default(cheese); }
    public virtual void Visit(Gouda cheese) { Default(cheese); }
    public virtual void Default(Cheese cheese) { }
}

Когда вы добавляете новые типы, библиотеки, созданные на основе более старой версии, будут использовать резервный метод, тогда как новыебиблиотеки могут переопределять новые перегрузки:

class Brie
{
    public override void Visit(ICheeseVisitor visitor)
    {
        visitor.Visit(this);
    }
}

interface ICheeseVisitor
{
    ...
    void Visit(Brie cheese);
}

abstract class CheeseVisitor : ICheeseVisitor
{
    ...
    public virtual void Visit(Brie cheese) { Default(cheese); }
    ...
}

Пример:

class CheeseImgVisitor : CheeseVisitor 
{
    private string src;

    public string Src
    {
        get { return this.src; }
    }

    public override void Visit(Wensleydale cheese)
    {
        this.src = "wensleydale.png";
    }

    public override void Visit(Gouda cheese)
    {
        this.src = "gouda.png";
    }

    public override void Default(Cheese cheese)
    {
        this.src = "generic_cheese.png";
    }
}
...