我觉得这个问题的答案非常简单,但我找不到它。所以这里:
假设您有以下类:
class Base;
class Child : public Base;
class Displayer
{
public:
Displayer(Base* element);
Displayer(Child* element);
}
此外,我有Base* object
可能指向类Base
的实例或类Child
的实例。
现在我想根据Displayer
指向的元素创建一个object
,但是,我想选择正确版本的构造函数。正如我目前所拥有的,这将实现这一点(我在这里使用C ++有点模糊,但我认为这是最清晰的方式)
object->createDisplayer();
virtual void Base::createDisplayer()
{
new Displayer(this);
}
virtual void Child::createDisplayer()
{
new Displayer(this);
}
但这样做有问题:
Base
和Child
是应用程序系统的一部分,而Displayer
是GUI系统的一部分。我想独立于Application系统构建GUI系统,因此很容易替换GUI。这意味着Base
和Child
不应该知道Displayer
。但是,我不知道如何在不让Application类了解GUI的情况下实现这一目标。
我错过了一些非常明显的事情,还是我在尝试一些不可能的事情?
编辑:我在原始问题中错过了部分问题。这一切都在GUI代码中发生得相当深刻,提供了这个GUI独有的功能。这意味着我希望Base
和Child
类根本不知道该调用 - 不只是隐藏它们到调用的内容
答案 0 :(得分:3)
这似乎是双重调度的经典场景。避免双重调度的唯一方法是切换应避免的类型(if( typeid(*object) == typeid(base) ) ...
)。
您可以做的是使回调机制通用,以便应用程序不必知道GUI:
class app_callback {
public:
// sprinkle const where appropriate...
virtual void call(base&) = 0;
virtual void call(derived&) = 0;
};
class Base {
public:
virtual void call_me_back(app_callback& cb) {cb.call(*this);}
};
class Child : public Base {
public:
virtual void call_me_back(app_callback& cb) {cb.call(*this);}
};
然后你可以使用这样的机器:
class display_callback : public app_callback {
public:
// sprinkle const where appropriate...
virtual void call(base& obj) { displayer = new Displayer(obj); }
virtual void call(derived& obj) { displayer = new Displayer(obj); }
Displayer* displayer;
};
Displayer* create_displayer(Base& obj)
{
display_callback dcb;
obj.call_me_back(dcb);
return dcb.displayer;
}
层次结构中的每个类都必须有一个app_callback::call()
函数,每次向层次结构添加类时,都必须为每个回调添加一个函数。
因为在你的情况下,只使用base&
进行调用也是可能的,当你忘记在回调类中重载这些函数之一时,编译器不会抛出错误。它只会调用一个base&
。那很糟。
如果需要,可以使用CRTP将每个类的call_me_back()
的相同代码移动到私有继承的类模板中。但是,如果你只有六个类,它并没有真正增加那么多的清晰度,它需要读者理解CRTP。
答案 1 :(得分:0)
让应用程序在系统代码上设置工厂界面。这是一个被黑客攻击的方式来做到这一点。显然,将此更改应用于您自己的首选项和编码标准。在某些地方,我在类声明中内联函数 - 只是为了简洁。
// PLATFORM CODE
// platformcode.h - BEGIN
class IDisplayer;
class IDisplayFactory
{
virtual IDisplayer* CreateDisplayer(Base* pBase) = 0;
virtual IDisplayer* CreateDisplayer(Child* pBase) = 0;
};
namespace SystemDisplayerFactory
{
static IDisplayFactory* s_pFactory;
SetFactory(IDisplayFactory* pFactory)
{
s_pFactory = pFactory;
}
IDisplayFactory* GetFactory()
{
return s_pFactory;
}
};
// platformcode.h - end
// Base.cpp和Child.cpp实现“CreateDisplayer”方法如下
void Base::CreateDisplayer()
{
IDisplayer* pDisplayer = SystemDisplayerFactory::GetFactory()->CreateDisplayer(this);
}
void Child::CreateDisplayer()
{
IDisplayer* pDisplayer = SystemDisplayerFactory::GetFactory()->CreateDisplayer(this);
}
//在您的应用程序代码中,执行以下操作:
#include "platformcode.h"
class CDiplayerFactory : public IDisplayerFactory
{
IDisplayer* CreateDisplayer(Base* pBase)
{
return new Displayer(pBase);
}
IDisplayer* CreateDisplayer(Child* pChild)
{
return new Displayer(pChild);
}
}
然后在应用初始化的早期某个地方(主要或WinMain),请说明以下内容:
CDisplayerFactory* pFactory = new CDisplayerFactory();
SystemDisplayFactory::SetFactory(pFactory);
这将使您的平台代码不必知道“显示器”的杂乱细节,您可以稍后实现IDisplayer的模拟版本,以独立于渲染系统测试Base和Child。
此外,IDisplayer(方法未显示)成为平台代码公开的接口声明。您对“Displayer”的实现是一个继承自IDisplayer的类(在您的应用程序代码中)。