标签: double-dispatch

C++:对访问者模式的怀疑

我知道访客模式是什么以及如何使用它; 这个问题是不是这个的副本一个.


我有一个库,我把大部分可重复使用的代码放在我写的,并链接到我的大多数项目.

我经常需要为某些类添加功能,但不将这些新功能添加到库中.让我用一个真实的例子:

在这个lib中,我有一个类Shape,继承自CircleShape,PolygonShapeCompositeShape.

我现在正在开发一个图形应用程序,我需要渲染它们Shape,但不想render在核心Shape类中放置虚函数,因为我使用的一些项目Shape不进行任何渲染,而其他图形项目可以使用不同的渲染引擎(我在这个项目中使用Qt,但对于我使用OpenGL的游戏,因此该render函数需要不同的实现).

最着名的方法是使用访客模式,当然,这会让我心中产生一些疑问:

任何类的任何库都需要像我Shape一样扩展.大多数公共图书馆(大约所有公共图书馆)都没有为访客模式提供任何支持; 为什么?我为什么要?

访问者模式是一种在C++中模拟Double Dispatching的方法.它在C++中不是原生的,需要显式实现,使得类接口更复杂:我认为applyVisitor函数不应该与我的类函数处于同一级别,我认为这就像打破抽象.

明确的向上铸造Shapedynamic_cast比较昂贵,但对我来说,它看起来像一个清晰的解决方案.


所以我该怎么做?在我的所有库类中实现Double Dispatching?如果图书馆提供的Shape不是我的,但在互联网上找到了一些GPL库怎么办?

c++ double-dispatch visitor-pattern

9
推荐指数
1
解决办法
2810
查看次数

如何在不使用switch语句的情况下基于两个对象的类型在Scala/Java中调用正确的方法?

我目前正在Scala开发一个游戏,我有许多实体(例如GunBattery,Squadron,EnemyShip,EnemyFighter),它们都是从GameEntity类继承的.游戏实体通过事件/消息系统向游戏世界和彼此广播感兴趣的事物.有许多EventMesssages(EntityDied,FireAtPosition,HullBreach).

目前,每个实体receive(msg:EventMessage)对其响应的每种消息类型具有更具体的接收方法(例如receive(msg:EntityDiedMessage)).一般receive(msg:EventMessage)方法只是一个switch语句,它根据消息类型调用适当的receive方法.

随着游戏的开发,实体和消息列表(以及哪些实体将响应哪些消息)是流动的.理想情况下,如果我希望游戏实体能够接收新的消息类型,我只希望能够为响应编写逻辑代码,而不是那样做,并且必须更新匹配语句.

我认为有一种想法是将接收方法从游戏实体层次结构中拉出来,并且有一系列函数,比如def receive(e:EnemyShip,m:ExplosionMessage)和def,receive(e:SpaceStation,m:ExplosionMessage)但这会使问题复杂化,因为现在我需要一个匹配语句来覆盖消息游戏实体类型.

这似乎与DoubleMultiple dispatch 的概念有关,也许与Visitor模式有关,但我在绕着它缠绕时遇到了一些麻烦.我本身并不是在寻找OOP解决方案,但是如果可能的话我想避免反思.

编辑

做一些更多的研究,我认为我正在寻找的是类似Clojure的东西defmulti.

你可以这样做:

(defmulti receive :entity :msgType)

(defmethod receive :fighter :explosion [] "fighter handling explosion message")
(defmethod receive :player-ship :hullbreach []  "player ship handling hull breach")
Run Code Online (Sandbox Code Playgroud)

scala double-dispatch

9
推荐指数
1
解决办法
1787
查看次数

C++ Double Dispatch for Equals()

想象一下,我有抽象的基类 Shape,派生类CircleRectangle.

class Shape {};
class Circle : public Shape {};
class Rectangle : public Shape {};
Run Code Online (Sandbox Code Playgroud)

我需要确定两个形状是否相等,假设我有两个Shape*指针.(这是因为我有两个实例,vector<Shape*>我想看看它们是否具有相同的形状.)

建议的方法是双重调度.我想出的就是这个(这里大大简化了,所以形状等于同一类型的所有其他形状):

class Shape {
public:
    virtual bool equals(Shape* other_shape) = 0;
protected:
    virtual bool is_equal(Circle& circle) { return false; };
    virtual bool is_equal(Rectangle& rect) { return false; };
    friend class Circle;    // so Rectangle::equals can access Circle::is_equal
    friend class Rectangle; // and vice versa
};

class Circle : public Shape …
Run Code Online (Sandbox Code Playgroud)

c++ oop inheritance double-dispatch

8
推荐指数
2
解决办法
1144
查看次数

我需要问一下对象类,但这是一个不好的做法.这种情况的替代方案?

我在扩展应用程序时遇到了麻烦.这是一个考勤记录系统.目前,每位员工都通过具有QR码的卡记录出勤率.现在他们想要添加指纹识别,直到他们要求两种形式的识别必须在系统中共存时才会出现问题.因此,系统必须能够感知员工的QR,以及他的指纹.

我有以下课程:

通过.equalsTo(id)方法在QrIdStrategy中修复它的方法是:

equalsTo(id){
  if (id == isKindOf (QrIdStrategy))
    if (this.getEmployeeId () == id.getEmployeeId ())
      return true;

  return false;
}
Run Code Online (Sandbox Code Playgroud)

但我明白,询问一个对象的类是一个不好的做法,并不想这样做.我该如何解决?

我想到了访问者模式,但我仍然有同样的问题来比较两个不同类型的类(因为系统可以扫描这两种类型中的任何一种)

在此输入图像描述

uml design-patterns double-dispatch visitor-pattern

7
推荐指数
1
解决办法
357
查看次数

Pharo的双重派遣

有人可以用Smalltalk解释Pharo 4.0中双重调度的过程吗?我是Smalltalk的新手并且很难掌握这个概念,因为与Smalltalk相比,它在Java中的实现方式非常不同.如果有人可以通过一个例子解释它,将会非常有帮助.

smalltalk double-dispatch pharo

7
推荐指数
1
解决办法
986
查看次数

没有RTTI的C++双重调度"可扩展"

没有人知道的方式在C正确处理双调度++ 而不使用RTTI和dynamic_cast的<>,并且也是溶液,其中,所述类层次是可扩展的,即基类可以从进一步得到,它的定义/实施不需要知道的吗?
我怀疑没有办法,但我很高兴被证明是错的:)

c++ polymorphism overloading double-dispatch rtti

6
推荐指数
2
解决办法
2828
查看次数

双重调度和模板类

我有一个C++代码,我比较了从一个普通的母类派生的不同类Foo.如果两个类的类型不同,则总是进行比较false.否则,它会比较某些特定于该类的内部数据.

我的代码看起来像这样:

class Bar;
class Baz;

class Foo
{
public:
    virtual bool isSame( Foo* ) = 0;
    virtual bool isSameSpecific( Bar* ){ return false; }
    virtual bool isSameSpecific( Baz* ){ return false; }
};

class Bar : public Foo
{
public:
    bool isSame( Foo* foo){ return foo->isSameSpecific(this); }
    bool isSameSpecific( Bar* bar){ return bar->identifier == identifier; }

    int identifier;
};

// and the same for Baz...
Run Code Online (Sandbox Code Playgroud)

这个伟大的工程(我认为这是一个双重分派),我可以比较Bar,并Baz只用指针Foo.

但现在出现了问题.我必须添加一个模板类:

template< typename …
Run Code Online (Sandbox Code Playgroud)

c++ templates double-dispatch

6
推荐指数
1
解决办法
2149
查看次数

访问者选择如何遍历的访问者模式

据我了解,在访问者模式的典型规范中,被访问的对象决定如何遍历,一般只支持一种遍历顺序。(参见,例如,这里这里。)

是否有相同使用双重分派的名称,但访问者可以决定如何遍历对象层次结构?在我的应用程序中,一个非常异构的文档模型类型集合正在向访问者推送,例如导出操作。但是,要说各种处理器(访问者)都应该以广度优先顺序遍历似乎有些僵硬。其中一些可能只关注模型的一个子集,或者可能需要以特定的顺序处理模型的各个部分。

我担心以非标准方式使用访问者模式中的名称会混淆其他开发人员。我的建议有名字吗?

我还会问是否有理由不让访问者控制遍历,以防万一我遗漏了通常的访问者公式中的一些智慧。如果可能相关,该应用程序是 Java 语言。

design-patterns double-dispatch visitor

6
推荐指数
1
解决办法
2241
查看次数

使用Java的双向可扩展层次结构

我的问题是以尽可能可扩展的方式为不同的消息实现不同的行为.我知道访问者模式,我知道双重调度,但我似乎无法找到一个解决方案,这让我感到困惑(至少不在java的范围内).

我的情况如下:

我有一个消息层次结构:

消息层次结构

路由器接口的层次结构,每个都定义了自己的消息类型的路由方法:

路由器接口层次结构

我想实现类似于此:

履行

能够添加和删除路由某些消息的功能,以及轻松更改某些消息的路由策略.

问题是,没有开关转换我的消息,我不想做,我不能选择相应的功能的界面,因为像

CompositeRouter comp = new AllRouter(...//new Router instances);
MessageBase msg = new DerivedMessage();
msg.process(comp);
Run Code Online (Sandbox Code Playgroud)

会导致java选择重载 <runtime message-type>.process(Router)

在编译时,在运行时,为相应的路由器对象调用它.所以我似乎无法在编译时选择正确的process()调用.我也不能反过来这样做,因为comp.route(msg)

将被解决<dynamic router-type>.route(MessageBase).

我可以写一个游客,它选择从CompositeRouter的正确方法,但为此我必须定义为所有的MessageTypes定义了前面的各路线的方法,哪一种失败的目的访问者接口,因为这意味着我每当我添加一个新的DerivedMessage时,都必须重写访问者.

有没有办法实现这一点,以便消息和路由器都是可扩展的,或者在给定当前的java特性的情况下它是否没有希望?

编辑1:

我忘记提到的是我有4或5个其他情况,它们与Router-hierarchy 几乎相同,所以我有点想避免使用Reflection进行方法查找,因为我害怕运行时成本.

回复评论:

  1. @ aruisdante关于@ bot的建议的假设是正确的.我不能覆盖,因为如果我覆盖路由(MessageBase),我将松开运行时类型的MessageBase.

  2. @aruisdante和@geceo:我知道我可以这样做 - 这就是我对"switch-casting"的意思(MessageBase有一个MessageType字段) - 但我有11个实际的消息类和~6个位置代码我需要它,这将是一个巨大的痛苦实施 - 以及维护明智.

java oop double-dispatch

6
推荐指数
1
解决办法
358
查看次数

(嵌套?)多个调度[访客模式]

我在应用程序架构中遇到了障碍.我刚刚开始使用访问者模式在抽象对象上执行特定的算法,这些抽象对象在运行时我不知道.我的问题是我的算法也取决于嵌套抽象类型的类型.

让我说明一下我的意思:

我有一个抽象的DataSource类.从这里我实现了concerete DataSourceReference和DataSourceExplicit类.我还有一个抽象的Report类(反序列化的元数据),我从中实现具体的Report类ReportTypeA和ReportTypeB.创建这些对象时,它们的DataSource可以是任何扩展的DataSource类.

我需要两者,实际的Report类型和DataSource类型,所以我可以相应地执行.我可以使用访问者模式获取协同报告类型,但不知道如何在之后/也为DataSource执行相同操作.

访问报告后我无法访问DataSource,因为我将失去报告的具体类型(因为您必须让它接受基本报告类型:Accept(SomeDataSourceVisitor d,MetaReport m) - 或者为每个可能的报告类型重载,这违背了访客模式的目的.看到我的问题?

有任何想法吗?我不想使用动态,因为它不需要新报表类型的开发人员确保调度程序(访问者)支持新报表.

现行代码:

public abstract class DataSource
{
}

public class DataSourceReference : DataSource
{
    // reference thing(s)
}

public class DataSourceExplicit : DataSource
{
    // explicit thing(s)
}

public abstract class Report
{
    // some shared Report attribute(s)
    // ...

    public DataSource DataSource { get; set; }

    public abstract FinalReport Execute(IReportExecutionDispatcher d);
}

public class ReportA : Report
{
    // ReportA specific attribute(s)
    // ... …
Run Code Online (Sandbox Code Playgroud)

architecture abstraction multiple-dispatch double-dispatch visitor-pattern

5
推荐指数
1
解决办法
673
查看次数