Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/305.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 访问者模式如何不违反开放/关闭原则? 来自维基百科:_Java_Design Patterns_Visitor_Open Closed Principle - Fatal编程技术网

Java 访问者模式如何不违反开放/关闭原则? 来自维基百科:

Java 访问者模式如何不违反开放/关闭原则? 来自维基百科:,java,design-patterns,visitor,open-closed-principle,Java,Design Patterns,Visitor,Open Closed Principle,其思想是,一旦完成,类的实现只能修改为 纠正错误;新的或更改的功能需要创建不同的类。 该类可以通过继承重用来自原始类的代码 据我所知,访问者模式是一种强大的技术,可以通过使用双重分派遍历实现相同接口的相似但不同的对象。在我的一个Java示例中,我创建了一组组成树结构的复合对象,这些对象的每个特定实现都实现了visitable接口。visitor接口对每个可访问对象都有一个方法,具体的visitor实现了对这些情况的处理 我想弄清楚的是,如果我要在复合结构中添加一个新的实现,该结构也实现了visi

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

据我所知,访问者模式是一种强大的技术,可以通过使用双重分派遍历实现相同接口的相似但不同的对象。在我的一个Java示例中,我创建了一组组成树结构的复合对象,这些对象的每个特定实现都实现了visitable接口。visitor接口对每个可访问对象都有一个方法,具体的visitor实现了对这些情况的处理

我想弄清楚的是,如果我要在复合结构中添加一个新的实现,该结构也实现了visitable,那么我需要重新打开visitor接口并将其添加到该接口中,这也迫使我修改visitor的每个实现


虽然这很好,因为我无论如何都需要这样做(如果访问者不能理解你的访问表,这对你有什么好处?)?这不是设计模式的核心原因之一吗?试图说明切换到此模式而不是维护一个switch语句来结束所有switch语句的正当理由,但每个人都认为代码无论如何都是一样的,每种情况下都有一个方法,而不是一个开关块,只是被分解了,更难阅读。

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

在需要时使用访问者模式

  • [……]

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


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

GoF之一约翰·维利塞德斯(John Vlissides)在他的书中就这一主题写了一章。他讨论了扩展层次结构与保持访问者完整性不兼容的问题。他的解决方案是访问者和基于
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
方法中预期这些新类。因为访问者和扩展类都位于同一个库中,所以这种方法非常有效


另外,还有一篇文章叫做“讨论这个问题”。作者得出结论,可以回到基于
enum
的双重分派,因为原始的访问者模式与基于
enum
的分派相比没有显著的改进。我不同意作者的观点,因为当您的继承层次结构的大部分都是可靠的,并且期望用户在这里或那里提供一些实现时,混合方法在可读性方面提供了显著的好处;因为有几个类,我们可以相对轻松地融入层次结构,所以没有必要把所有的东西都扔掉。

前两个答案很好。为了扩大观察,“一种模式适用于某些情况”,在两个维度上考虑OCP。
  • 当我们可以向现有的层次结构添加新类型时,面向对象的代码就可以扩展了。我们无法向现有类型添加新函数
  • 当我们可以向现有的数据结构添加新的逻辑时,功能代码就可以扩展了。我们无法通过现有函数传递新的数据结构
  • 这种二分法被称为二分法。访问者模式允许我们交换OO可扩展的常用维度,作为回报,我们获得FP可扩展的维度


    为了协调Visitor和OCP,我们可以说该模式只是为可扩展性打开了一个不同的维度。可扩展性维度的折衷适用于某些情况。

    因此,如果我有一个经常更改的对象结构,但实际上只有一个访问策略,那么继续使用访问者模式是没有意义的。你知道还有其他解决这个问题的好办法吗?结构中的每个对象是否都应该包含它自己的逻辑,这通常会出现在访问者中?将其设置为可接受的答案,特别是因为它解决了开-关原则问题,但在本线程中直接提到dasblinkenlight的答案,以供可能的替代实现。谢谢大家!我还要提到我还有另一个想法:实现某种访问者组合,处理不同的特定可访问类型,并简单地调用正确的vis,这有意义吗