【发布时间】:2016-04-04 06:26:38
【问题描述】:
我目前有一个“WorldObject”类,它包含一个指向其 b2Body 的指针,以及几个方法和变量。
但是让 WorldObject 成为 b2Body 的派生类不是更聪明吗?
处理这类事情的一般方法是什么?总是为类中最重要的对象创建一个派生类,还是只创建一个新的并让所有东西都具有属性?
这些可能性的优缺点是什么?
谢谢。
【问题讨论】:
我目前有一个“WorldObject”类,它包含一个指向其 b2Body 的指针,以及几个方法和变量。
但是让 WorldObject 成为 b2Body 的派生类不是更聪明吗?
处理这类事情的一般方法是什么?总是为类中最重要的对象创建一个派生类,还是只创建一个新的并让所有东西都具有属性?
这些可能性的优缺点是什么?
谢谢。
【问题讨论】:
没有最好的方法。这一切都取决于上下文:
拥有指向b2Body 的指针实现了组合关系,即WorldObject 具有b2Body。
使WorldObject 派生自b2Body 实现继承,并且仅当WorldObject 是b2Body 时才应这样做。这意味着原则上你可以用WorldObject 对象做你可以用b2Boby 做的所有事情(以及更多)。
经验法则是选择preferring composition over inheritance。除非你真的有明显的继承。
在您的情况下,b2Body 是刚体。现在问你一个问题:你的对象是一个身体吗?或者你的对象有一个身体?
更实用:这种设计的优点是它把Box2D引擎的选择封装在你的WorldObject中。如果稍后您使用另一个 API 将您的软件移植到另一个物理引擎,这可以让您更好地控制影响。如果您改用继承,则底层引擎 API 将显着影响您自己的软件的设计,并且有一天很难迁移到另一个引擎。
【讨论】:
通常最好进行组合而不是过度使用继承。
另请注意,您通常会创建一个调用 b2World::CreateBody() 的主体,并返回一个 b2Body*,并且无法进行继承。
为了避免内部b2Body* 的生命周期问题,您可以使用智能指针。我使用这个定义:
class BodyDeleter
{
public:
BodyDeleter()
:m_world(NULL)
{}
BodyDeleter(b2World *world)
:m_world(world)
{}
void operator()(b2Body *body)
{
if (m_world)
m_world->DestroyBody(body);
}
private:
b2World *m_world;
};
typedef std::unique_ptr<b2Body, BodyDeleter> b2BodyPtr;
那么,在我的WorldObject:
class WorldObject
{
protected:
WorldObject(b2World *world)
:m_body(NULL, m_world)
{}
b2BodyPtr m_body;
};
然后,在WorldObject 的实际子类中:
class SpaceShip : public WorldObject
{
public:
SpaceShip(b2World *world)
:WorldObject(world)
{
//initialize bodydef and whatever
m_body.reset(world->CreateBody(...));
}
};
【讨论】:
您必须更喜欢组合而不是继承。组合避免fragile base class problem。
与方法调用不同,继承违反封装
子类依赖于它们的超类,这些超类可能会被更改,从而破坏子类中的功能
使用组合和转发而不是继承,尤其是在存在实现包装类的适当接口时。
在为继承设计类时使用继承
我建议你阅读 Joshua bloch 的“Effective Java”一书的第 16 条“偏好组合优于继承”
【讨论】: