访客模式和违反封装

时间:2013-08-27 15:46:10

标签: design-patterns

void accept(CarElementVisitor *visitor) {
        visitor->visit(this);
    }
}

这里我们将一个对象传递给一个向它添加操作的函数。这里,它违反了封装。它是面向对象的好设计吗?

2 个答案:

答案 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

虽然检查BreakWindshieldWiper等某些CarElement的液位可能是有意义的,但对所有Wheel物体来说可能没有意义,例如为Wheel。然而,由于在check_fluid_levels()接口上声明CarElementcheck_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);
}