访客模式如何不违反开放/关闭原则



来自Wikipedia:

这个想法是,一旦完成,类的实现只能修改为 正确错误;新的或更改的功能将需要创建不同的类。 该类可以通过继承从原始类重复使用编码

据我了解,访问者模式是一种强大的技术,可以通过使用Double Dispatch实现相同接口的相似但不同的对象。在我的Java示例之一中,我创建了一组形成树结构的对象集,这些对象的每个特定实现实现了可访问的接口。访问者界面都有针对每个可访问对象的方法,并且具体访问者为每种情况都实现了什么。

我试图让自己的问题是一个事实,如果我要在还可以访问的复合结构中添加新的实现,那么我需要重新打开访问者的界面并在其中添加该情况,还迫使我修改访客的每个实现。

虽然这很好,因为无论如何我都需要这样做(如果访客无法理解的话,您的访客有什么好处??这不是设计模式的核心原因之一吗?试图显示切换到此模式的正当理由,而不是维护开关语句以结束所有切换语句,但是每个人都认为代码无论如何都将是相同的,而对于每种情况而不是开关块,

模式适用于某些情况。摘自Gof Book(第333页):

时使用访客模式
  • [...]

  • 定义对象结构的类很少发生变化,但是您通常想定义结构上的新操作。更改 对象结构类需要将接口重新定义为所有 游客,这可能是昂贵的。如果对象结构类 经常更改,那么定义操作可能更好 那些类。

如果您经常更改组成结构的对象的类,则很难维护访问者类层次结构。在这种情况下,在构成结构的类上定义操作可能更容易。

gof之一,关于该主题的出色章节,他的模式孵化书籍。他讨论了一个关注,即扩展层次结构与保持访客完整不相容。他的解决方案是访问者和基于enum的方法(或基于类型的)方法之间的混合体,其中为访问者提供了 visitOther方法,由访问者在开箱即用的"基础"层次结构之外的所有类中调用。此方法为您提供了一种逃避方式,可以在访问者完成后处理添加到层次结构的类中。

abstract class Visitable {
    void accept(Visitor v);
}
class VisitableSubclassA extends Visitable  {
    void accept(Visitor v) {
        v.visitA(this);
    }
}
class VisitableSubclassB extends Visitable {
    void accept(Visitor v) {
        v.visitB(this);
    }
}
interface Visitor {
    // The "boilerplate" visitor
    void visitB(VisitableSubclassA a);
    void visitB(VisitableSubclassB b);
    // The "escape clause" for all other types
    void visitOther(Visitable other);
}

当您添加此修改时,您的访问者不再违反 open-close原理,因为它不需要修改其源代码就可以扩展。

我在几个项目上尝试了这种混合方法,而且效果还不错。我的主要类层次结构是在不需要更改的单独编译的库中定义的。当我添加Visitable的新实现时,我会修改我的Visitor实现,以期在其visitOther方法中期望这些新类。由于访问者和扩展课都位于同一库中,因此此方法效果很好。

P.S。还有另一篇名为访客的文章重新讨论了这个问题。作者得出的结论是,一个人可以返回基于enum的双重调度,因为原始访问者模式对基于enum的调度没有显着改进。我不同意作者,因为如果您的大部分继承层次结构都是牢固的,并且预计用户将在这里和那里提供一些实现,那么混合方法为可读性提供了重大好处;由于几个类,我们可以相对轻松地将所有内容扔掉,这是没有意义的。

前两个答案很棒。为了扩展观察," a模式适用于某些情况,"在二维中考虑OCP。

  1. 当我们可以在现有层次结构中添加新类型时,以对象为导向的代码开放。我们不能在现有类型中添加新功能。
  2. 当我们可以将新逻辑添加到现有数据结构中时,功能代码将用于扩展。我们无法通过现有功能传递新的数据结构。

这种二分法称为表达问题。访问者模式使我们可以交换OO可扩展的通常维度,作为回报,我们获得了FP可扩展的维度。

要将访问者与OCP调和,我们可能会说该模式只是为可扩展性打开了不同的维度。可扩展性维度的权衡适用于某些情况。

最新更新