void accept(CarElementVisitor *visitor) {
visitor->visit(this);
}
}
在这里,我们将一个对象传递给一个向它添加操作的函数。在这里,它违反了封装。它是好的面向对象设计吗?
最佳答案
如果通过对象内部发生变化时可能直接受影响的函数计数来衡量封装,那么该模式会减少 ConcreteElement
的封装。实现 Element::accept(Visitor)
时至少减少 1界面。如果ConcreteElement
,封装可能会进一步减少。的接口(interface)需要扩展以提供操作以实现 ConcreteVisitor
的功能.
模式本身既不好也不坏,因为通常是上下文导致模式有利或不利。因此,设计师应该考虑模式的意图、动机、适用性和后果。访问者模式的目的是允许定义新的操作而不改变操作所在的类。虽然不能保证,但它可能会增加 Element
的相对封装。 -family 与将操作添加到 Element
相比本身。
例如,考虑操作想要检查 CarElement
的液位的情况。对象。没有访客,CarElement
接口(interface)声明了 check_fluid_levels()
功能:
CarElement { check_fluid_levels() }
Break: CarElement { check_fluid_levels() }
WindshieldWiper: CarElement { check_fluid_levels() }
Wheel: CarElement { check_fluid_levels() }
for e in car:
e.check_fluid_levels()
虽然检查某些
CarElement
的液位可能是有意义的。喜欢 Break
和 WindshieldWiper
,它可能对所有 CarElement
都没有意义对象,例如 Wheel
.尽管如此,Wheel
的相对封装由于check_fluid_levels()
已减少在 CarElement
上声明界面。另一方面,如果使用访问者模式,那么只有检查液位有意义的元素才会提供
check_fluid_levels()
。功能。CarElement { accept(Visitor) }
Break: CarElement { accept(Visitor); check_fluid_levels() }
WindshieldWiper: CarElement { accept(Visitor); check_fluid_levels() }
Wheel: CarElement { accept(Visitor) }
FluidChecker: Visitor { ... }
FluidChecker checker
for e in car:
e.accept(checker)
而
accept()
的相对封装减少 1函数,相对封装比将操作添加到 CarElement
更好。 .考虑添加一个新操作来检查 CarElement
的排放水平s。在这种情况下,以上都不是 CarElement
s 将提供一个有意义的 check_emissions()
功能。对于访问者,新操作不会影响封装级别;另一方面,如果将操作添加到 CarElement
,封装级别会发生变化。 .这是一张图表,列出了可以访问
Element's
的函数的数量。内部,使用上述示例和两个操作(检查液位和检查排放):CarElement 访客
操作操作
休息 2 2
挡风玻璃雨刮器 2 2
轮子 2 1
关于design-patterns - 访问者模式和违反封装,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18470104/