访问者模式是将域枚举重构为类的最佳方式吗?

时间:2010-08-28 12:17:16

标签: c# design-patterns enums polymorphism visitor-pattern

如果我们想要将枚举(包含在域层中)重构为多态类,那么使用“简单”抽象方法可能是个坏主意,如果我们要重构的所有switch和if语句都在其他层内(就像业务或表示层一样),因为我们最终可能会在域层内引用这些层:

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 个答案:

答案 0 :(得分:5)

您可以为具有后备方法的访问者提供默认实现:

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