更为通用的访问者模式
很抱歉,如果我的问题这么长时间又太技术性了,但是我认为这很重要,其他人会对它感兴趣
我正在寻找一种方法,可以将某些软件内部结构与它们在c ++中的表示形式清楚地分开
我有一个通用参数类(稍后存储在容器中)可以通过boost :: any类包含任何类型的值
我(大致)有这种基类(当然还有更多的东西)
class Parameter { public: Parameter() template typename<T> T GetValue() const { return any_cast<T>( _value ); } template typename<T> void SetValue(const T& value) { _value = value; } string GetValueAsString() const = 0; void SetValueFromString(const string& str) const = 0; private: boost::any _value; }
有两个级别的派生类:
第一层定义类型和字符串之间的转换(例如ParameterInt或者ParameterString)
第二层定义行为和实际创建者(例如,从ParameterInt派生ParameterAnyInt和ParameterLimitedInt或者从GenericString派生ParameterFilename)
根据实际类型,我想添加外部函数或者根据特定参数类型运行的类,而无需在基类中添加虚拟方法并且不进行奇怪的强制转换
例如,我想根据参数类型创建适当的gui控件:
Widget* CreateWidget(const Parameter& p)
当然,除非我使用RTTI或者自己实现(使用枚举和开关大小写),否则我无法从中理解真正的参数类型,但是,这不是正确的OOP设计解决方案。
经典的解决方案是"访客"设计模式http://en.wikipedia.org/wiki/Visitor_pattern
这种模式的问题在于,我必须事先知道将实现哪些派生类型,因此(将Wikipedia和我的代码写的东西放在一起),我们会有以下几种:
struct Visitor { virtual void visit(ParameterLimitedInt& wheel) = 0; virtual void visit(ParameterAnyInt& engine) = 0; virtual void visit(ParameterFilename& body) = 0; };
是否有解决方案以任何其他方式获得此行为,而无需事先了解所有具体类型并且无需派生原始访问者?
编辑:Pizza博士的解决方案似乎与我在想的最接近,但是问题仍然是相同的,并且该方法实际上依赖于dynamic_cast,我试图避免将其作为一种(即使是较弱的)RTTI方法
也许最好是在不引用访客模式的情况下思考一些解决方案并清理我们的思想。目的只是具有以下功能:
Widget* CreateWidget(const Parameter& p)
每个"具体"参数的行为都不同,而不会丢失其类型的信息
解决方案
回答
如果我正确理解...
我们有一个可以使用不同硬件选项的对象。为了促进这一点,我们使用了Device的抽象接口。设备具有一堆将在某些事件上触发的功能。用法相同,但设备的各种实现将具有完全完善的功能,或者只是立即返回。为了使生活更加轻松,这些功能无效,并在出现问题时引发异常。
回答
我已经使用了这个("非循环访问者")来取得良好的效果;它使得在一定程度上无需更改现有类的情况下就可以将新类添加到层次结构中。
回答
为了完整起见:
当然,完全有可能为对象编写一个自己的多方法指针表实现,并在运行时手动计算方法地址。 Stroustrup有一篇关于实现多重方法(尽管在编译器中)的论文。
我真的不建议任何人这样做。使实现的性能良好非常复杂,使用它的语法可能会很尴尬且容易出错。如果其他所有方法都失败了,那么这仍然是可行的方法。
回答
我无法理解要求。但是我用我自己的话说,因为这是我所理解的情况:
- 我们有抽象的Parameter类,该类最终被子类化为一些具体的类(例如:ParameterLimitedInt)。
- 我们有一个单独的GUI系统,该系统将以通用方式传递这些参数,但是要注意的是,它需要呈现特定于参数类的具体类型的GUI组件。
- 限制是我们不想执行RTTID,也不想编写代码来处理每种可能的具体参数类型。
- 我们愿意使用访客模式。
有了这些作为要求,以下是我将如何处理这种情况的方法:
我将实现visitor模式,其中accept()返回一个布尔值。基本Parameter类将实现虚拟的accept()函数并返回false。
然后,Parameter类的具体实现将包含accept()函数,该函数将调用访问者的visit()。他们会返回true。
visitor类将利用模板化的visit()函数,因此我们仅对我们希望支持的具体参数类型进行覆盖:
class Visitor { public: template< class T > void visit( const T& param ) const { assert( false && "this parameter type not specialised in the visitor" ); } void visit( const ParameterLimitedInt& ) const; // specialised implementations... }
因此,如果accept()返回false,则说明Parameter的具体类型尚未实现访问者模式(如果有其他逻辑,我们希望根据具体情况进行处理)。如果访问者模式中的assert()触发,则是因为它没有访问我们已经实现了专门化的Parameter类型。
所有这些缺点之一是,不支持的访问只能在运行时捕获。
回答
对于Vistor的通用实现,我建议使用Loki Visitor,它是Loki库的一部分。