面向对象设计问题,Liskov替换原理

时间:2010-07-04 19:55:16

标签: c++ oop

我正在设计OO框架,我面临以下问题。

让我们说在框架中我有一个 Shape 接口,用户可以自由地实现和扩展(添加新功能) Shape 接口来创建自己的数字,例如 Square Circle 。要使这些新对象可用,用户必须将它们注册到 ShapeFactory 中,指定形状(字符串)和对象的名称。

此外,该框架提供了一个名为 ShapeWorker 的接口,它定义了以下功能:

class ShapeWorker
{
public:
  void processShape( Shape& shape ) = 0;
};

用户可以自由地实现 ShapeWorker 界面来制作特定的形状工作者,例如: SquareWorker CircleWorker 。要使这些新对象可用,用户必须将它们注册到 WorkerFactory 中,指定形状(字符串)和对象的名称。

在某个时刻,框架给定一个表示形状名称的字符串,通过使用 ShapeFactory 创建一个新的 Shape ,之后(在其他地方)代码)使用具有相同形状名称的 WorkerFactory 创建一个新的 ShapeWorker 。然后调用 processShape ,提供之前创建的 Shape 实例。

[ ... ]
Shape* myShape = shapeFactory.create( shapeName );
[ ... ]
ShapeWorker* myWorker = workerFactory.create( shapeName );
myWorker->processShape( *myShape );
[ ... ]

重点是,这样做,我强制用户实现 SquareWorker ,从 Shape Square <进行向下转换/ em>进入 processShape 函数,以便访问完整的 Square 界面:

class SquareWorker
{
public:
  void processShape( Shape& shape )
  {
     Square& square = dynamic_cast< Square& >( shape );
     // using Square interface
  }
};

这违反了Liskov替代原则。

现在,这种做法是错误的吗?什么是更好的解决方案?请注意,我不想将processShape实现为 Shape 的成员函数。

我希望描述已经足够清楚了。

提前感谢您的帮助。

西莫

1 个答案:

答案 0 :(得分:5)

除非你的形状有一个必须由工人使用的通用界面,否则这种方法对我来说似乎完全正确。形状工作者或多或少地专注于特定形状,因此具有关于它所处理的类的知识。使用适用于所有形状的通用界面会更好,但是你无法将所需的全部内容放入其中,它最终会完全混乱。向下倾斜是解决这个问题的正确方法。

使用模板可以帮助您:您可以为所有工作人员创建基类

template <class T> 
class BaseShapeWorker : ShapeWorker
{
public:
  void processShape( Shape& shape )
  {
     T& specificShape = dynamic_cast< T& >( shape );
     processShape( specificShape )
  }
protected:
  virtual void processShape( T& shape ) = 0;
};

这不需要实施者了解这种向下倾斜,也可能通过提供一些经常重复使用的功能来简化实施。