1. 什么是Multi-methods.
在阐述这个概念之前,我们先看一下什么是多态(Polymorphisn)。多态是面向对
象程序设计的一个重要的特征, 多态是允许你将父对象的指针(或者引用)设置成为它的子对象的技术,赋值之后,该父对象指针(或者引用)就可以根据当前赋
值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。多态性在C++中都是通过虚函数
(Virtual Function) 实现的。 例如:
class Parent
......{
public:
virtual void Test() = 0;
}
class Sub1: public Parent
......{
public:
void Test()
......{
cout<<"HI, this is sub1"<<endl;
}
}
class Sub2: public Parent
......{
void Test()
......{
cout<<"HI, this is sub2"<<endl;
}
}
Parent* p1 = new Sub1();
Parent* p2 = new Sub2();
p1->Test(); //call the Test method of class Sub1;
p2->Test();//call the Test method of class Sub1;
在c
++中,多态性的内涵是:通过virtual function技术,真实的函数调用完全依赖于对象的真实类型(这就是late
binding)。我们的问题是:virtual function调用可不可以依赖于两个或者两个以上的对象?这就是multi-methods.
例子:
//method declaration
bool intersect(const Shape&, const Shape&,)
......{
//...
}
bool intersect(const Rectangle&, const Circle&,)
......{
//...
}
class Shape
......{
//...
}
class Rectangle: public Shape
......{
//...
}
class Circle: public Shape
......{
//...
}
Shape* s1 = new Rectangle();
Shape* s2 = new Circle();
//while calling the following method, what happend?
//the actual function which will be called is bool intersect(const Shape&, const Shape&,), not bool intersect(const Rectangle&, const Circle&,)
// but what we want is the latter, not former
intersect(*s1,*s2);
//maybe you can call like this, which will meet our requirement, but, unfortunately , it violats the principle: we should program based on interface not implementation.
intersect(*(dynamic_cast<Rectangle*>(s1)),*(dynamic_cast<Circle*>(s2)));
2. C++ committee曾经考虑的解决方案(摘自 The Design and Evolution of C++, by Stroustrup)
c++从语言自身来支持multi-method:
//solution 1:
(s1@s2)->intersect(); //rather than intersect(s1,s2);
//solution 2:
bool intersect(virtual const Shape&,virtual const Shape&);
bool intersect(virtual const Rectangle&,virtual const Circle&) //overrides
...{
}
或许,C++在将来的某个版本会支持multi-method.
3. workarounds for multi-method.
3.1 用double dispatch设计模式解决。
double dispatch(双分派)设计模式是指:在选择一个方法的时候,不仅仅要根据消息接收者(receiver)的运行时型别(Run time type),还要根据参数的运行时型别(Run time type)。接下来我们用double dispatch来解决一下上面的那个问题:
class Shape
......{
//...
virtual bool intersect(const Shape&) const = 0;
virtual bool intersect(const Rectangle&) const = 0;
virtual bool intersect(const Circle&) const =0;
}
class Rectangle: public Shape
......{
//...
bool intersect(const Shape& s) const
......{
return s.intersect(*this); // *this is a Rectangle and calling which intersect method totally depends on the real type of s!
}
bool intersect(const Rectangle&) const
......{
//...
}
bool intersect(const Circle&) const
......{
//...
}
}
class Circle: public Shape
......{
//...
bool intersect(const Shape& s) const
......{
return s.intersect(*this); // *this is a Circle and calling which intersect method totally depends on the real type of s!
}
bool intersect(const Rectangle&) const
......{
//...
}
bool intersect(const Circle&) const
......{
//...
}
}
对于double dispatch, 最大的问题是:这样的设计与类的继承结构高度耦合,当类的继承结构改变后,会影响到现有的代码。从上面的例子可以看到:Shape 类是不应该意识到它的子类的存在。不过当前的主流面向对象程序设计语言(C++/Java/C#等)都并不支持multi-method, 从这个意义上说:double dispatch还是一个合理,有效地解决方案
3.2 另一个可选的方案是RTTI。
bool intersect(const Shape& s1, const Shape& s2)
...{
//the follwing code, can implement by using index table, the key is the type_id of the real object(such as typeid(s1) and typeid(s2))
//the query result is the pointer to function (such as the pointer to function: bool intersect(const Rectangle&, const Circle&).
if( typeid(s1)==typeid(Rectangle) && typeid(s1)==typeid(Rectangle))
...{
intersect(*(dynamic_cast<Rectangle*>(s1)),*(dynamic_cast<Circle*>(s2)));
}
else if(...)
...{
//...
}
}
bool intersect(const Rectangle&, const Circle&,)
...{
//...
}
class Shape
.........{
//...
}
class Rectangle: public Shape
.........{
//...
}
class Circle: public Shape
.........{
//...
}
Shape* s1 = new Rectangle();
Shape* s2 = new Circle();
//as a result , for the caller, the code is based on interface not implementation
intersect(*s1,*s2);