【发布时间】:2016-03-08 18:27:04
【问题描述】:
我需要开发一个 C++ 解决方案来表示具有特征的对象,其中对象和特征由不同的对象表示,但关联的实际实现是在派生类中实现的,该派生类存在以封装外部实现。我知道这种事情是典型的继承相关问题,所以我想对正确的解决方案提出意见。实现部分应该被视为一种 API 边界——用户代码不应该看到它,或者只看到一次以选择实现。
这是一个例子:
#include <cstdio>
// External implementation 1
class SomeShape {};
class SomeBody { public: SomeShape *shape; };
// External implementation 2
class OtherShape {};
class OtherBody { public: OtherShape *shape; };
//////////////
class Shape
{
public:
virtual const char *name() { return "Shape"; }
};
class Body
{
public:
virtual void setShape(Shape *s) = 0;
};
class Factory
{
public:
virtual Shape *makeShape() = 0;
virtual Body *makeBody() = 0;
};
//////////////
class AShape : public Shape
{
public:
SomeShape *someShape;
virtual const char *name() { return "AShape"; }
};
class ABody : public Body
{
protected:
SomeBody *someBody;
AShape *shape;
public:
ABody() { someBody = new SomeBody; }
virtual void setShape(Shape *s)
{
shape = static_cast<AShape*>(s);
printf("Setting shape: %s\n", s->name());
someBody->shape = shape->someShape;
}
};
class AFactory : public Factory
{
public:
virtual Shape *makeShape()
{ return new AShape(); }
virtual Body *makeBody()
{ return new ABody(); }
};
//////////////
class BShape : public Shape
{
public:
OtherShape *otherShape;
virtual const char *name() { return "BShape"; }
};
class BBody : public Body
{
protected:
OtherBody *otherBody;
BShape *shape;
public:
BBody() { otherBody = new OtherBody; }
virtual void setShape(Shape *s)
{
shape = static_cast<BShape*>(s);
printf("Setting shape: %s\n", s->name());
otherBody->shape = shape->otherShape;
}
};
class BFactory : public Factory
{
public:
virtual Shape *makeShape()
{ return new BShape(); }
virtual Body *makeBody()
{ return new BBody(); }
};
因此,上面的作用是允许用户实例化Body和Shape对象,这些对象的存在是为了管理关联的底层实现SomeShape/SomeBody或OtherShape/OtherBody。
然后,执行这两种实现的主要功能可能是,
int main()
{
// Of course in a real program we would return
// a particular Factory from some selection function,
// this should ideally be the only place the user is
// exposed to the implementation selection.
AFactory f1;
BFactory f2;
// Associate a shape and body in implementation 1
Shape *s1 = f1.makeShape();
Body *b1 = f1.makeBody();
b1->setShape(s1);
// Associate a shape and body in implementation 2
Shape *s2 = f2.makeShape();
Body *b2 = f2.makeBody();
b2->setShape(s2);
// This should not be possible, compiler error ideally
b2->setShape(s1);
return 0;
}
因此,我在这里不满意的部分是 setShape() 中的 static_cast<> 调用,因为它们建立在假设已传入正确的对象类型,而没有任何编译时类型检查。同时,setShape() 可以接受任何Shape,而实际上这里应该只接受派生类。
但是,如果我希望用户代码在 Body/Shape 级别而不是 ABody/AShape 或 @987654337 级别上运行,我看不出如何进行编译时类型检查@/BShape 级别。但是,将代码切换为 ABody::setShape() 只接受 AShape* 会使整个工厂模式变得无用,一方面,还会迫使用户代码知道正在使用哪个实现。
此外,A/B 类似乎是在 Some/Other 之上的额外抽象级别,它们的存在只是为了在编译时支持它们,但这些并不打算公开到 API,那么有什么意义...它们仅用作一种阻抗匹配层,迫使 SomeShape 和 OtherShape 进入 Shape 模具。
但是我的替代选择是什么?可以使用一些运行时类型检查,例如 dynamic_cast<> 或 enum,但如果可能的话,我正在寻找更优雅的东西。
你会如何用另一种语言做到这一点?
【问题讨论】:
-
我认为,根据我对您所说的理解,使用 dynamic_cast 可能是最简单的选择。我认为在使用运行时多态性时,您将无法进行任何编译时类型检查。
-
我在想一种解决方案可能是将 makeShape() 从工厂移动到 Body(),返回一个 已经 关联的对象.. 但确切的解决方案仍然逃避我。也不确定它的普遍性。我问这个问题是因为我觉得在这里可以做一些聪明的事情......程序员痒......;)但也许我走错了路。
-
嗯,这是一个有趣的想法。如果您修改 Factory 以具有一个返回 body 和 shape 对的虚拟方法怎么办?返回的主体在哪里与返回的形状相关联?
-
不错,明天我会尝试这个想法。我没有提到给定的身体可能有多种形状,但这是一个开始..
-
我在这里想到了另一个想法,我认为这更符合我的目标。我将尝试使用一组非常基本的类来表示 body->shape 映射的结构,而不是建立一个将实现表示为派生类的层次结构。在第二遍中,我将遍历此结构并生成对应的“后端”结构,使用特定实现来镜像所需的关系。我只是想弄清楚当结构更新时这是否需要反向指针来提高效率..
标签: c++ inheritance abstract-factory