我对我正在努力解决的问题有疑问。希望你能忍受我。
想象一下,我有一个Object类,表示物理对象层次结构的基类。后来我继承它来创建一个Object1D,Object2D和Object3D类。这些派生类中的每一个都将具有一些特定的方法和属性。例如,3d对象可能具有下载渲染器要使用的3d模型的功能。
所以我有这样的事情:
class Object {};
class Object1D : public Object { Point mPos; };
class Object2D : public Object { ... };
class Object3D : public Object { Model mModel; };
现在我有一个名为Renderer的独立类,它只需要一个Object作为参数,然后渲染它:-) 以类似的方式,我想支持不同类型的渲染器。例如,我可以拥有每个对象可以依赖的默认值,然后为某种对象提供其他特定的渲染器:
class Renderer {}; // Default one
class Renderer3D : public Renderer {};
这就是我的问题。渲染器类需要将Object作为参数,例如在构造函数中,以便检索渲染对象所需的任何数据。
到目前为止一切顺利。但Renderer3D需要获取一个Object3D参数,以便不仅获得基本属性,还获得3d对象的特定属性。
构造函数看起来像这样:
CRenderer(Object& object);
CRenderer3D(Object3D& object);
现在我如何以通用方式指定它?或者更好的是,有没有更好的方法来设计它?
我知道我可以依赖RTTI或者类似的但我想尽可能避免这种情况,因为我觉得可能有更好的方法来解决这个问题。
提前致谢!
答案 0 :(得分:4)
处理这种耦合的一种方法是使用工厂。通用工厂创建对象和渲染器。 2D工厂创建2D对象和兼容的2D渲染器,3D工厂创建3D对象和兼容的3D渲染器。
abstract class Factory {
abstract Object createObject();
abstract Renderer createRenderer();
}
class 2DFactory {
Object createObject() { return new 2DObject(); }
Renderer createRenderer() { return new 2DRenderer(); }
}
// similarly for 3D
class Client {
Client(Factory factory) { ... }
void render() {
factory.createRenderer().render(factory.createObject());
}
}
因此,如果您不混淆不同工厂的产品,您可以保持客户端代码清洁。如果客户端代码只处理一个对象系列和渲染器一次,将它传递给合适的工厂是微不足道的,然后让它在不知道具体类型的情况下使用对象和渲染器。对此的一种变体是让对象通过工厂方法创建兼容的渲染器,如suggested by Jason。
另一种可能性是使用模板/泛型,但这是特定于语言的。您的实现语言看起来像C ++,所以我冒险朝这个方向努力。您可以使用模板专业化:
template<class T> class Renderer {
void render(T object) { ... }
};
template<> class Renderer<Object3D> {
void render(Object3D object) {
// render the 3D way
}
};
// similarly for 2D
答案 1 :(得分:3)
一种方法是允许Objects通过getRenderer()方法创建自己的渲染器,该方法返回相应类型的渲染器。这是Factory Method模式的一个示例。根据实现语言的功能,getRenderer方法可以是父Object类中的抽象方法,返回父Renderer类/接口的实例,特定呈现器扩展/实现。
答案 2 :(得分:1)
这可行吗?
template<typename T> class Renderer {};
class Renderer3D : public Renderer<Object3D> {};
答案 3 :(得分:1)
听起来你正在寻找双重调度模式。要使用它,可以向基类添加一个抽象方法,如下所示:
Dispatch(renderer)
Object的每个子类然后重写Dispatch并在提供的渲染器上为其类型调用适当的方法:
Object2D.Dispatch(renderer)
{
renderer.Render2D(self)
}
您还可以使用接口使其更通用。创建一个用于呈现每种类型对象的接口,并让您的调度方法检查是否支持正确的接口:
Object2D.Dispatch(renderer)
{
render2d = render as IRender2D
if render2d found
render2d.Render(self)
}
答案 4 :(得分:0)
这是另一种看待这个的方法。使渲染器成为对象的一部分。您可以为您创建的每个对象以及您将拥有的每个对象
分配正确类型的渲染器Render(){
myRenderer.Render(this)
}