Smalltalk で SimpleDelegator

日経 Linux 10 月号の「プログラミングのオキテ」にて、SimpleDelegator の簡易実装を引き合いに、Ruby のリフレクション機能の紹介。まつもとさん曰く“Ruby に負けないくらい動的”…な Smalltalk ではどんな具合になるのかなぁ、と試してみました。


SimpleDelegator およびそのインスタンス(a SimpleDelegator)の挙動のイメージとしてはこんなかんじ。

'string' size                         " => 6 "
(SimpleDelegator on: 'string') size   " => 6 "
'string' at: 4 put: $o; yourself                         " => 'strong' "
(SimpleDelegator on: 'string') at: 4 put: $o; yourself   " => 'strong' "

普通のメッセージ送信に対する応答を見る限り、両者の区別は付かないようにします(インスペクタも騙されちゃうけど…)。例外は、オーバーライドが許されていない class と、新たに設けた isSimpleDelegator などの送信時。

'string' class                         " => ByteString "
(SimpleDelegator on: 'string') class   " => SimpleDelegator "
'string' isSimpleDelegator                         " => false "
(SimpleDelegator on: 'string') isSimpleDelegator   " => true "


で、SimpleDelegator の定義は以下。スーパークラス(Object)で定義されているメソッドを undef するのではなく、スーパークラスを動的に切り換えている(Object ←→ nil)点が Ruby 版と大きく異なるところでしょうか。あまりに動的すぎて、Ruby に負けないくらい強力なリフレクション機能の出番すらなくなってしまったのはご愛敬? とりあえず、#respondsTo: だけ再定義しておきましたが、ここいらへんは目的により意見が分かれるところでしょう。

'From Squeak3.8 of ''5 May 2005'' [latest update: #6665] on 26 September 2005'!

!Object methodsFor: 'testing'!
isSimpleDelegator
	^ false
! !


ProtoObject subclass: #SimpleDelegator
	instanceVariableNames: 'delegatee'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Category-Delegating'.
SimpleDelegator superclass: nil!


!SimpleDelegator methodsFor: 'error handling'!
doesNotUnderstand: aMessage
	^ delegatee perform: aMessage selector withArguments: aMessage arguments
! !


!SimpleDelegator methodsFor: 'initialization'!
initialize: anObject
	delegatee := anObject
! !


!SimpleDelegator methodsFor: 'testing'!
isSimpleDelegator
	^ true
! !


!SimpleDelegator methodsFor: 'class membership'!
respondsTo: aSymbol 
	^ self class canUnderstand: aSymbol
! !


"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!

SimpleDelegator class
	instanceVariableNames: ''!


!SimpleDelegator class methodsFor: 'instance creation'!
on: anObject
	| delegator |
	self superclass: Object.
	delegator := self new initialize: anObject; yourself.
	self superclass: nil.
	^ delegator
! !


!SimpleDelegator class methodsFor: 'class initialization'!
initialize
	self superclass: nil
! !


SimpleDelegator initialize!

ワークスペースなどにコピペした後、選択し、シフト黄ボタンメニューから「file it in」を選ぶか、alt/cmd + shift + G でファイルインすると試せます。


参考:
Smalltalk Textbook 33 : EngiEncapsulator & EngiField