NSObject の +poseAsClass: をシミュレートする


Matz にっき 経由で、「ダイナミック Objective-C 第12回:ポージングで乗っ取り」。ここで紹介されている Cocoa/ObjC の poseAsClass: の作用が面白いので、同じようなもの(#poseAs:)を SqueakSmalltalk で実装してみるテスト。#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' "


めでたし、めでたし。(よい子は真似をしないでね!)