【问题标题】:Adding operations on Model without adding code to Model在模型上添加操作而不向模型添加代码
【发布时间】:2014-07-30 14:12:06
【问题描述】:

假设我有Shape 对象的层次结构,每个对象都有自己的数据(折线有顶点列表,圆有中心和半径等)。

我希望能够对每个形状执行操作,例如绘制、捕捉到某个点、在特定点拆分为两个形状等。

一种方法是为每个操作向 Shape 接口添加一个方法。但是,在这种情况下,每次添加新操作时,我都必须修改我的模型界面。这对我来说听起来不正确。我想到了以下解决方案,并想在这里提出您的意见或其他解决方案。


我将在Shape接口中添加一个ShapeOperationsFactory的接口和以下方法:

class Shape
{
public:
    virtual ShapeOperationFactory* createShapeOperationsFactory() = 0;
};

class Circle : public Shape
{
public:
    virtual ShapeOperationsFactory* createShapeOperationsFactor();
};

ShapeOperationsFactory* Circle::createShapeOperationsFactory()
{
    return new CircleShapeOperationsFactory();
}

ShapeOperationsFactory 将能够创建一组特定于形状的操作类:

class ShapeOperationsFactory
{
public:
    virtual ShapeDrawer* createDrawer() = 0;
    virtual ShapeSnapper* createSnapper() = 0;
    virtual ShapeSplitter* createSplitter() = 0;
};

class CircleShapeOperationsFactory : public ShapeOperationsFactory
{
public:
    virtual ShapeDrawer* createDrawer();
    virtual ShapeSnapper* createSnapper();
    virtual ShapeSplitter* createSplitter();
}

ShapeDrawer* CircleShapeOperationsFactory::createDrawer()
{
    return new CircleShapeDrawer();
}

ShapeSnapper* CircleShapeOperationsFactory::createSnapper()
{
    return new CircleShapeSnapper();
}

ShapeSplitter* CircleShapeOperationsFactory::createSplitter()
{
    return new CircleShapeSplitter();
}

在这个实现中,Shape 接口在添加新操作时不会改变。对于新形状,我需要实现一个新的操作工厂和每个操作的类。对于新操作,我需要向操作工厂类添加一个方法,并为每个形状添加一个实现操作的类。

【问题讨论】:

  • 我看不出这个工厂的东西是如何解决这个问题的。如果你想添加一个新的虚拟操作(也就是说,当你只知道对象是一个Shape时你可以做的事情),每个工厂都需要将它添加到他们的ShapeOperationsFactory中。此外,由于工厂没有继承权,因此您失去了 OOP。如果你想给Shape添加操作并让所有派生类都使用这个操作,那么显然所有派生类都必须实现它。我想我可能错过了这里的问题?
  • 每个形状的工厂都继承自 ShapeOperationsFactory 类(我在代码中有错误,我对其进行了编辑)。有了这些工厂,我不需要在添加新操作时更改 Shape 及其继承的类
  • 但是您确实需要更改 ShapeOperationFactory 及其派生的东西,所以这不只是推迟问题吗?
  • 我认为这将有助于创建一个更加模块化的系统,并且我不必向 Shape 接口添加许多方法。它通常应该是模型,并且在其中执行繁重的逻辑似乎不正确。你怎么看?
  • 我仍然不确定我是否理解您的问题,但这是我的解释。 Shape 有很多派生类,Shape 的职责非常大。结果,有大量的虚拟接口函数必须进入Shape,这也可能意味着所有派生类都必须实现每个新的接口函数。除非您的意思是说并非所有派生类实际上都使用大量接口函数,否则您无法解决此问题?

标签: c++ oop design-patterns


【解决方案1】:

通过创建 Operator 类让你的类更加模块化,我认为这很棒,但这并不是真正的工厂。工厂通常涉及基于某些消息创建对象,例如在反序列化过程中。

对于您的情况,您可以在基类中拥有一个 Operator 成员,并在派生类的构造函数中将该成员分配给适当的 Operator 派生类。

【讨论】:

  • 我认为您可以将其称为抽象工厂,类似于 wiki 中描述的内容:en.wikipedia.org/wiki/Abstract_factory_pattern。在我的设计中,每个形状都实现了一个方法,该方法返回一个负责创建各种操作类的类。我相信这个类可以称为工厂
【解决方案2】:

解决方案可能是使用visitor design pattern。本设计模式的目的:

访问者设计模式是一种将算法与其操作的对象结构分离的方法。这种分离的实际结果是能够在现有对象结构中添加新操作而无需修改这些结构。这是遵循开闭原则的一种方式。

原理很简单:

你创建一个访问者类:

class Visitor
{
  public:
    virtual void visit(Circle*) = 0;
    virtual void visit(Polyline*) = 0;
    ...
};

你把这个方法加到Shape:

virtual void accept(class Visitor*) = 0;

然后你在每个Shape子类中实现这个方法。

void Circle::accept(Visitor *v)
{
    v->visit(this);
}

然后您必须为每个操作创建一个访问者:

class Drawer: public Visitor
{
  public:
    Drawer()
    {
    }
    void visit(Circle* c)
    {
        drawCircle(c);
    }
    void visit(Polyline*p)
    {
        drawPolyline(p);
    }
    ...
};

您还可以将每个访问方法委托给服务:(visit(Circle* c)CircleDrawer)。

    void visit(Circle* c)
    {
        circleDrawer->draw(c);
    }
    void visit(Polyline*p)
    {
        polylineDrawer->draw(p);
    }

如果你想添加一个操作,你必须创建一个新的访问者子类。

如果要添加形状,则必须为每个访问者添加一个新方法。


访问者协作者非常适合复合设计模式(在 gui 编程中大量使用)。访问者模式可以与复合模式一起使用。对象结构可以是复合结构。在这种情况下,在复合对象的接受方法的实现中,必须调用组件对象的接受方法。


注意: 我不是 C++ 程序员,请随意编辑并使代码在语法上正确。

【讨论】:

  • 是的,它也可以很好地工作。然而,我担心的是,如果我有很多形状,每个这样的访问者都会有很多代码。
  • 你是对的,这就是为什么我建议将每个访问方法委托给特定的服务。此外,在某些情况下(例如绘图)拥有一些集中式逻辑可能会很有趣。
  • 我认为这样的设计最终和我的建议非常相似。
  • 您遗漏了重要的一点。访问者允许在 Shape 层上添加新操作而不需要任何修改。
  • 你是对的。使用您的解决方案添加操作会导致修改 api,但添加形状会导致修改。对于访客,情况正好相反。如果访问的层次结构非常稳定,则必须使用访问者。设计通常是一个权衡问题。如果你需要参数,你必须在访问者构造时注入它们。
猜你喜欢
  • 1970-01-01
  • 2015-02-21
  • 1970-01-01
  • 2015-08-28
  • 1970-01-01
  • 2011-05-19
  • 2015-07-25
  • 1970-01-01
  • 2013-04-21
相关资源
最近更新 更多