For Development HEAD DRAFTSearch (procedure/syntax/module):

9.19 gauche.mop.propagate - スロットアクセスの伝播

Module: 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>クラスにwidthheightスロットを定義して、 それ経由で内部の<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スロット)に格納されたオブジェクトへと リダイレクトされます。 仮想スロットの一種とも言えますが、アクセス手続きを書く必要がありません。

こうすると、widthheightスロットが<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>クラスを継承するだけで充分です。

Class: <propagate-meta>

{gauche.mop.propagate} スロットアロケーションに:propagatedを追加します。 propagatedが指定されたスロットは、実体を持つオブジェクトを指定する :propagateスロットオプションを持たねばなりません。 アロケーションが:propagatedスロットであるにもかかわらず :propagateスロットオプションが指定されていない場合は、エラーが投げられます。

:propagateスロットオプションは、シンボルもしくは2つのシンボルからなる リストを取ります。

単なるシンボルであった場合、それは内部オブジェクトを格納しているスロットの名前です。 このスロットへのアクセスは、指定されたスロットが保持する内部オブジェクトの同名の スロットへのアクセスにリダイレクトされます。

二つのシンボルからなるリストであった場合、例えばそれを(X Y)とすると、 このスロットへのアクセスは(slot-ref (slot-ref obj X) Y)になります。

propagatedスロットをinit-keywordで初期化可能にしたい場合、 propagateするオブジェクトをpropagatedスロットより前に置くようにしてください。 スロットの初期化はデフォルトでスロットの出現順に行われます。 内部オブジェクトの実体が先に作られていないと、そのスロットに初期値を設定できません。

Class: <propagate-mixin>

{gauche.mop.propagate} これは簡便のためのmixinクラスです。 クラス定義で:metaclass <propagate-meta>を指定するかわりに、 このmixinクラスを継承すれば、propagatedスロットが使えるようになります。



For Development HEAD DRAFTSearch (procedure/syntax/module):
DRAFT