gauche.mop.propagate
- スロットアクセスの伝播 ¶スロットの:allocation
オプションに:propagated
を追加するメタクラスを提供します。
アロケーションが:propagated
であるスロットへのアクセスは、
指定された他のオブジェクトのスロットへのアクセスにリダイレクトされます。
コンポジットしたオブジェクトの外部インタフェースを簡便に保ちたい時に便利です。
内部オブジェクトの持つスロットへのアクセスを、
親オブジェクトのスロットであるかのように見せられるからです。
説明よりまず例を示したほうがわかりやすいでしょう。
長方形の領域を表す<rect>
という抽象クラスがある時、
それを継承ではなくコンポジションによって<viewport>
クラスを作ることを
考えます。簡単な方法は次のようになるでしょう:
(define-class <rect> () ((width :init-keyword :width) (height :init-keyword :height))) (define-class <viewport> () ((dimension :init-form (make <rect>)) ;; ... other slots ... ))
この定義だと、viewportの高さや幅にアクセスした場合、
(~ viewport'dimension'width)
のように
常に<rect>
オブジェクトを経由しなければならなくなります。
これは煩わしいだけでなく、viewportクラスのユーザが、
viewportがどう構成されているかを知っていなければならないということでもあります
(必ずしも悪いことではないですが、時にはそれを隠したいこともあります)。
gauche.mop.propagate
を使うと、
<viewport>
クラスにwidth
とheight
スロットを定義して、
それ経由で内部の<rect>
クラスへアクセスさせることができます。
(use gauche.mop.propagate) (define-class <rect> () ((width :init-keyword :width) (height :init-keyword :height))) (define-class <viewport> (<propagate-mixin>) ((dimension :init-form (make <rect>)) (width :allocation :propagated :propagate 'dimension :init-keyword :width) (height :allocation :propagated :propagate 'dimension :init-keyword :height)))
:allocation
に:propagated
が指定されると、
そのスロットは<viewport>
インスタンス内にはアロケートされません。
そのスロットへのアクセスは、:propagate
スロットオプションで指定される
スロット(上の例ではdimension
スロット)に格納されたオブジェクトへと
リダイレクトされます。
仮想スロットの一種とも言えますが、アクセス手続きを書く必要がありません。
こうすると、width
とheight
スロットが<viewport>
クラスの
スロットであるかのように扱えます。init-keyword
による初期化も行えます。
(ただ、:init-form
や:init-value
は使えません。
初期値は実体を持つオブジェクトで指定してください)。
gosh> (define vp (make <viewport> :width 640 :height 480)) vp gosh> (d vp) #<<viewport> 0xc5a1e0> is an instance of class <viewport> slots: dimension : #<<rect> 0xc5a130> width : 640 height : 480 gosh> (set! (~ vp'width) 800) #<undef> gosh> (~ vp'width) 800
以下の2つのクラスがこれを可能にしています。
通常、ユーザは<propagate-mixin>
クラスを継承するだけで充分です。
{gauche.mop.propagate
}
スロットアロケーションに:propagated
を追加します。
propagatedが指定されたスロットは、実体を持つオブジェクトを指定する
:propagate
スロットオプションを持たねばなりません。
アロケーションが:propagated
スロットであるにもかかわらず
:propagate
スロットオプションが指定されていない場合は、エラーが投げられます。
:propagate
スロットオプションは、シンボルもしくは2つのシンボルからなる
リストを取ります。
単なるシンボルであった場合、それは内部オブジェクトを格納しているスロットの名前です。 このスロットへのアクセスは、指定されたスロットが保持する内部オブジェクトの同名の スロットへのアクセスにリダイレクトされます。
二つのシンボルからなるリストであった場合、例えばそれを(X Y)
とすると、
このスロットへのアクセスは(slot-ref (slot-ref obj X) Y)
になります。
propagatedスロットをinit-keyword
で初期化可能にしたい場合、
propagateするオブジェクトをpropagatedスロットより前に置くようにしてください。
スロットの初期化はデフォルトでスロットの出現順に行われます。
内部オブジェクトの実体が先に作られていないと、そのスロットに初期値を設定できません。
{gauche.mop.propagate
}
これは簡便のためのmixinクラスです。
クラス定義で:metaclass <propagate-meta>
を指定するかわりに、
このmixinクラスを継承すれば、propagatedスロットが使えるようになります。