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

C0M*_*M37 27 java design-patterns visitor open-closed-principle

来自维基百科:

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

根据我的理解,访问者模式是一种强大的技术,可以遍历使用双重调度实现相同接口的相似但不同的对象.在我的一个Java示例中,我创建了一组形成树结构的复合对象,这些对象的每个特定实现都实现了可访问的接口.访问者界面具有用于每个可访问对象的方法,并且具体访问者实现对每个这些情况执行的操作.

我试图解决的问题是,如果我要在复合结构中添加一个也实现可访问的新实现,那么我需要重新打开访问者界面并将该情况添加到它,也迫使我修改访问者的每个实现.

虽然这很好,但我还是需要这样做(如果访问者无法理解,那么访问者增加了什么好处?)但是在学术层面上,这不会违反开放封闭原则吗?这不是设计模式的核心原因之一吗?试图显示转换到这种模式的合理理由而不是维护switch语句来结束所有switch语句,但是每个人都认为代码将是相同的,每种情况的方法而不是交换机块,只是分解并且更难阅读.

rep*_*mer 15

模式适用于某些情况.从GoF书籍(第333页):

使用访客模式时

  • [...]

  • 定义对象结构的类很少改变,但是你经常想要在结构上定义新的操作.更改对象结构类需要重新定义所有访问者的界面,这可能是昂贵的.如果对象结构类经常更改,那么在这些类中定义操作可能更好.

如果经常更改构成结构的对象的类,则可能难以维护Visitor类层次结构.在这种情况下,可能更容易在构成结构的类上定义操作.


das*_*ght 10

GoF之一John Vlissides在他的Patterns Hatching一书中写了一篇关于这个主题的精彩章节.他讨论了扩展层次结构与保持访问者完整性不相容的问题.他的解决方案是访问者和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);
}
Run Code Online (Sandbox Code Playgroud)

添加此修改后,您的访问者不再违反开放 - 关闭原则,因为它可以扩展而无需修改其源代码.

我在几个项目中尝试了这种混合方法,并且工作得相当好.我的主类层次结构是在一个单独编译的库中定义的,不需要更改.当我添加新的实现时Visitable,我修改我的Visitor实现以期望在他们的visitOther方法中使用这些新类.由于访问者和扩展类都位于同一个库中,因此这种方法非常有效.

PS还有一篇名为Visitor Revisited的文章正是在讨论这个问题.作者得出结论,人们可以回到enum基于双重调度,因为原始访客模式并没有比enum基于调度的调度有显着改进.我不同意作者,因为如果你的继承层次结构的大部分是可靠的,并且期望用户在这里和那里提供一些实现,那么混合方法在可读性方面提供了显着的好处; 抛弃一切是没有意义的,因为我们可以相对容易地将几个类放入层次结构中.

  • @C0M37 我尝试做类似的事情(即两个级别的访问者),但很快就失控了,所以我放弃了我的实验,回到“带有转义条款的访问者”。 (2认同)
  • 听说过 [非循环访问者](http://stackoverflow.com/a/11437892/31158)?来自 [c2.com](http://c2.com/cgi/wiki?AcyclicVisitor):_"非循环访问者模式允许将新函数添加到现有的类层次结构中,而不会影响这些层次结构,也不会创建依赖循环是 GangOfFour 访客模式所固有的。”_ (2认同)