Matz にっき 経由で、「ダイナミック Objective-C 第12回:ポージングで乗っ取り」。ここで紹介されている Cocoa/ObjC の poseAsClass: の作用が面白いので、同じようなもの(#poseAs:)を Squeak の Smalltalk で実装してみるテスト。#become: を使うので、ちょっと反則気味…ですが、今のところ数はそう多くない“ Smalltalk にできて Ruby にできないコト”のひとつ、かも。
Behavior >> poseAs: originalClass originalClass == self superclass ifFalse: [^ self]. originalClass superclass: (self superclass: originalClass superclass). originalClass become: self
たとえば、A のサブクラスに B 、B のサブクラスに C1 と C2 があったとき。それぞれに、次のようなメソッドを定義します。
A >> message ^ 'A'
B >> message ^ super message, ' > B'
C1 >> message ^ super message, ' > C1'
C2 >> message ^ super message, ' > C2'
このとき、B 、C1 、C2 のインスタンスの挙動はそれぞれ次のようになります。
B new message " => 'A > B' " C1 new message " => 'A > B > C1' " C2 new message " => 'A > B > C2' "
他方で、B のサブクラスに PseudoB を作り、このクラスにも #message を定義します。
PseudoB >> message ^ super message, ' > PseudoB'
もちろん、この時点ではまだ、PseudoB の存在により B や、C1 や C2 のインスタンスがその振る舞いに影響を受けることはありません。以上を踏まえて、次の式を評価します。
PseudoB poseAs: B
すると、あ〜ら不思議。以降、B 、C1 、C2 のインスタンスの挙動は(既存のものも含め)それぞれ、次のように変化します。
B new message " => 'A > B > PseudoB' " C1 new message " => 'A > B > PseudoB > C1' " C2 new message " => 'A > B > PseudoB > C2' "
めでたし、めでたし。(よい子は真似をしないでね!)