我正在设计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 的成员函数。
我希望描述已经足够清楚了。
提前感谢您的帮助。
西莫
答案 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;
};
这不需要实施者了解这种向下倾斜,也可能通过提供一些经常重复使用的功能来简化实施。