void accept(CarElementVisitor *visitor) {
visitor->visit(this);
}
}
这里我们将一个对象传递给一个向它添加操作的函数。这里,它违反了封装。它是面向对象的好设计吗?
答案 0 :(得分:4)
如果通过对象的内部变化可能直接受影响的函数计数来测量封装,那么模式将ConcreteElement
的封装减少至少1实现Element::accept(Visitor)
接口。如果需要扩展ConcreteElement
的接口以提供满足ConcreteVisitor
功能的操作,则可以进一步减少封装。
模式本身既不好也不坏,因为通常情境会导致模式有益或负担。因此,设计师应该考虑模式的意图,动机,适用性和后果。访问者模式的目的是允许定义新操作,而无需更改操作所在的类。虽然不能保证,但是与将Element
本身的操作添加到Element
族相比,它可能增加 CarElement
- 族的相对封装。
例如,考虑操作想要检查CarElement
个对象的液位的情况。如果没有访问者,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()
虽然CarElement
函数的相对封装减少了1,但相对封装比将操作添加到CarElement
更好。考虑添加新操作以检查CarElement
的排放水平。在这种情况下,上述check_emissions()
中没有一个会提供有意义的CarElement
函数。对于访问者来说,新操作不会占用封装级别;另一方面,如果将操作添加到Element's
,则封装级别会更改。
这是一个图表,列出了可以访问{{1}}内部的函数计数,使用上面的示例和两个操作(检查液位和检查排放):
CarElement Visitor operations operations Break 2 2 WindshieldWiper 2 2 Wheel 2 1
答案 1 :(得分:3)
我不认为这违反了封装,因为Visitor
不知道被访问对象的内部结构。
在你的汽车示例中,我们的Car对象知道它有4个轮子和一个汽油引擎,但访问者只知道我们告诉它的内容而不知道我们如何存储我们的数据:
void accept(CarElementVisitor *visitor) {
visitor->visit(wheel_1);
visitor->visit(wheel_2);
visitor->visit(wheel_3);
visitor->visit(wheel_4);
visitor->visit(gasEngine);
}
稍后我们可以在不更改访问者访问呼叫的情况下更改Car对象的实现:
void accept(CarElementVisitor *visitor) {
//wheels now stored in array
visitor->visit(wheels[0]);
visitor->visit(wheels[1]);
visitor->visit(wheels[2]);
visitor->visit(wheels[3]);
visitor->visit(gasEngine);
}