作者godfat (godfat 真常)
看板Ruby
标题[心得] multi-method/dispatch
时间Thu Dec 21 22:55:16 2006
所谓 multi-method/dispatch 为动态决议两种以上真实型别的手段
在一般的物件导向程式语言中,仅仅支援 single-method/dispatch,
亦即 ooo.xxx(); 我们会说,我们 send 了 xxx 这个 message 给这个 ooo,
但这个 ooo 是什麽我们不知道,我们只知道他会依照 xxx 这个 message
来唤起正确的 method, 亦即 ooo 真实型别的 method.
於是我们可以不在乎 ooo 的真实型别,却能相信他能找到正确的 method.
尽管我相当不认同「一般所谓物件导向的三样特性」,不过依照这个说法的话,
这正是该三样特性的第三样:polymorphism, 多形。
p.s. 我个人认为 dynamic binding 动态连结会是比较好的说法,anyway, off-topic
而如果我们需要决议两种以上的型别呢?
例如:
unit.walk_to(dist);
假设每个 unit 都有不同的特性,所以在走到各种不同的 dist 上都会有
不同的效果。其实这可以用一个很简单的查表法来取得所需的数值,例如:
UnitA UnitB UnitC
TerrainA 100 200 300
TerrainB 50 700 12
TerrainC 777 666 999
除了数字以外,也可以把所有要的资料打包起来放到这个 table 中。
不过这样做的话,就必须维持各种不同的 id, 而且最重要的是,
如果所需的不只是资料不同,而是整个结果都完全不同呢?
例如当 UnitA 碰上 TerrainA 时,其实是要踩到地雷的,那不单单只是资料问题
我们希望的应该是,写下 unit.walk_to(dist); 时,除了 unit 是已知型别外,
dist 也应该要是已知型别,walk_to 才会知道到底该怎麽做。
在不支援 multi-method/dispatch 的程式语言中,一般我们可能会这样写:
C++:
void UnitA::walk_to(Terrain* dist){
if( TerrainA* terrain = dynamic_cast<TerrainA*>(dist) ){
// do something with UnitA v.s. TerrainA
}
else if( TerrainB* terrain = dynamic_cast<TerrainB*>(dist) ){
// do something with UnitA v.s. TerrainB
}
else{
// do something with UnitA v.s.
// terrain which you don't care who it is
}
}
Java:
void walk_to(Terrain dist){
if( dist instance_of TerrainA ){
TerrainA terrain = (TerrainA)dist;
// do something with UnitA v.s. TerrainA
}
else if( dist instance_of TerrainB ){
TerrainB terrain = (TerrainB)dist;
// do something with UnitA v.s. TerrainB
}
else{
// do something with UnitA v.s.
// terrain which you don't care who it is
}
}
也就是,一个个去检查他们到底是什麽型别。当然,这绝对是个很暴力的做法,
就像我们极力去避免的 switch case 一样,最大的缺点就是 error-prone.
我看过好几个 C++ 的解决方法,Loki 的做法大概有两个,一个就是像上面的
暴力法,不过是自动化的,所以可以确保不会 error-prone. 缺点就是你很容易
碰到很可怕的编译问题,这边不继续讨论。
另一个做法就是建立一个 map(Hash),用 type_info 做索引,对应到一个注册
好的 method. 用起来大概像这样:
dispatcher(unit, dist);
於是 dispatcher 用 typeid 取得 unit 和 dist 的 type_info,
再由这两个 type_info 组成的 key 去寻找当初注册好的 method.
本文的重点就是这个方法,请看到这个:
http://rubyforge.org/projects/multi/
这是 Ruby 的 multi-method/dispatch 的 lib, 他的实作法就跟这个做法类似
不过在讲这个重点前,再来看看另外一个实作法,我觉得相当有趣。
简单地说,当我们说:「unit.walk_to(dist)」时,是否可以获得 unit 的真实
型别?那麽,何不故技重施,以便获得第二个真实型别?
避免写太长不好阅读,待续…
--
『风车』が廻り続ける度に 『美しき』幻想が静かに纺がれ
『焔』の揺らめきの外に 『腕』を伸ばす愚かな者达 -《Roman》5th Story
『宝石』をより多く掴もうと 『朝と夜』の狭间を彷徨い続ける Track 10
『星屑』の砂の煌めきにも 『葡萄酒』の仄甘い陶酔を魅せ 黄昏の贤者
『贤者』が忌避する槛の中から 『伝言』の真意を彼等に问うだろう
『天使』が别れを告げし时 『地平线』は第五の物语を识る
--
※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 61.217.103.191