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

时间:2012-12-20 19:29:41

标签: java design-patterns visitor open-closed-principle

  

来自维基百科:

     

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

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

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

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

3 个答案:

答案 0 :(得分:13)

模式适用于某些情况。来自the GoF book(第333页):

  

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

  •   
  • 定义对象结构的类很少更改,但您经常需要在结构上定义新操作。改变了   对象结构类需要将接口重新定义为all   访客,这可能是昂贵的。如果是对象结构类   经常改变,然后定义操作可能更好   那些课程。

  •   

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

答案 1 :(得分:9)

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

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

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

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

答案 2 :(得分:1)

前两个答案很好。为了扩大观察范围,“ 模式适用于某些情况,”在两个维度上考虑OCP。

    当我们可以向现有层次结构中添加新类型时,
  1. 面向对象的代码已开放进行扩展。我们无法向现有类型添加新功能。
  2. 当我们可以向现有数据结构中添加新逻辑时,功能代码已开放扩展。我们无法通过现有功能传递新的数据结构。

此二分法称为expression problem。访客模式使我们可以交易OO可扩展的常规维度,作为回报,我们获得FP可扩展的维度。

要使游客与OCP和解,我们可以说该模式只是为扩展性打开了一个不同的维度。在某些情况下,可以在可扩展性维度上进行权衡。