更通用的访客模式

时间:2008-08-28 09:40:19

标签: c++ design-patterns visitors

如果我的问题太长而且技术性很强,我很抱歉,但我认为其他人对此感兴趣非常重要

我一直在寻找一种方法来清楚地将一些软件内部结构与它们在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

这种模式的问题在于我必须事先知道将实现哪些派生类型,因此(将维基百科和我的代码中的内容放在一起)我们将会有以下几种:

struct Visitor 
{
  virtual void visit(ParameterLimitedInt& wheel) = 0;
  virtual void visit(ParameterAnyInt& engine) = 0;
  virtual void visit(ParameterFilename& body) = 0;
};

是否有任何解决方案可以通过任何其他方式获得此行为,而无需提前知道所有具体类型并且无需派生原始访问者?


编辑: Dr. Pizza's solution seems the closest to what I was thinking,但问题仍然是相同的,并且该方法实际上依赖于dynamic_cast,我试图避免作为一种(即使是弱的)RTTI方法

也许最好不要引用访问者模式并清理我们的思想来考虑某些解决方案。目的只是具有以下功能:

Widget* CreateWidget(const Parameter& p)

对每个“具体”参数的行为不同,而不会丢失其类型的信息

5 个答案:

答案 0 :(得分:4)

对于Vistor的通用实现,我建议Loki VisitorLoki library的一部分。

答案 1 :(得分:1)

我使用this(“非循环访客”)效果很好;它可以在不改变现有类的情况下在层次结构中添加新类。

答案 2 :(得分:0)

如果我理解正确的话......

我们有一个可以使用不同硬件选项的对象。为方便起见,我们使用了Device的抽象接口。设备有一堆可以在某些事件上触发的函数。使用方法相同,但Device的各种实现方式要么具有完全充实的功能,要么立即返回。为了让生活更轻松,这些功能无效,并在出现问题时抛出异常。

答案 3 :(得分:0)

为了完整起见:

当然完全可以为对象编写自己的多方法指针表实现,并在运行时手动计算方法地址。 Stroustrup有一个关于实现多方法主题的paper(尽管在编译器中)。

我不会真的建议任何人这样做。使实现运行良好非常复杂,使用它的语法可能非常笨拙并且容易出错。如果其他一切都失败了,这可能仍然是要走的路。

答案 4 :(得分:0)

我无法理解您的要求。但病态 - 用我自己的话说 - 我理解的情况是:

  • 你有抽象的Parameter类,它最终被子类化为一些具体的类(例如:ParameterLimitedInt)。

  • 您有一个单独的GUI系统,它将以通用方式传递这些参数,但问题是它需要呈现特定于参数类的具体类型的GUI组件。

    < / LI>
  • 这些限制是你不想做RTTID,并且不想编写代码来处理每种可能类型的具体参数。

  • 您可以使用访客模式。

根据您的要求,以下是我处理这种情况的方法:

我会实现访问者模式,其中accept()返回一个布尔值。基类Parameter类将实现虚拟的accept()函数并返回false。

Parameter类的具体实现将包含accept()函数,这些函数将调用访问者的visit()。他们会回归真实。

访问者类将使用模板化的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,您知道参数的具体类型尚未实现访问者模式(如果有其他逻辑,您希望根据具体情况进行处理)。如果访问者模式中的assert()触发,则因为它没有访问您已实现专门化的参数类型。

所有这一切的一个缺点是不受支持的访问仅在运行时捕获。