|Lemon Home Page|

複数化の課題

パターン内の役割はテンプレートであり, アプリケーション内ではいくつものクラスがその役割を担うようなものは多数 ある. 例えば,Iterator パターンでは,サブクラスである ConcreteAggregate と ConcreteIterator は通常いくつもの具体的なクラス(SimpleList と SimpleListIterator とか,SkipList と SkipListIterator とか)に対応する. また,関数もクラスの中ではいくつもの関数と対応することがある. 例えば,AbstractFactory の createProduct 関数は,AbstractProduct の 種類に対応して複数の関数と対応する. このような複数のアプリケーション部品への対応を コード生成時に行なうことを複数化と呼び, 実現のために必要な記述に関して考える.

実際,バラバラに複製が行なわれるだけなら特に問題はない. Composite パターンの Leaf や Composite はこのような部品に当てはまる (といっても Operation 関数の関連は考慮する必要があるのだが). 問題となるのは関連を持ちながら複数化しなければならない場合で, 上記の Iterator の場合には ConcreteAggregate と ConcreteIterator の対 応がそれにあたる. また,AbstractFactory は,createProduct 関数と AbstractProduct 役割に 加えて,ConcreteFactory と ConcreteProduct との対応関係も持つ.


名前空間

複数化について話を進める前に, 複数化された要素間の関係を保存するための名前空間を 考える必要がある. これは,継承・全体部分・参照の関係および関数内での 参照を解決する必要があるためである. このために,パターンで定義された役割を一つのグループとし, 具体化をこの単位で行なうこととする. このグループを「システムラベルの名前空間(System Label Name Space)」と 呼んでいる. 実体としては,システムラベルと対応するクラスを対とする テーブルである. なお,現在関係や関数もここで管理されているが, 関数の場合は多少問題を残している. 本来は,パターン・役割・関数のそれぞれに名前空間が存在し, 解決されるべき対象からリンクが張られているべきであるが, 現在はパターンの名前空間で全てを賄おうとしているので無理がある.

複製時には,新規の名前空間が作成され, 共に複製されなければならない要素がシステムラベルとリンクされる. 他の要素は前の名前空間から引き継がれる. このようにして複製による名前の混乱を回避する.


制約と連鎖

ここでは, 要素の対応関係と継承関係(オーバーライドを含む)による制約から 複数化の連鎖が正しく起こるかどうかを確かめる. 例として,連鎖が複雑な AbstractFactory を用いる. AbstractFactory は AbstractFactory, ConcreteFactory, AbstractProduct および ConcreteProduct の4つの役割からなり, ConcreteFactory は AbstractFactory を継承し, ConcreteProduct は AbstractProduct を継承している. AbstractFactory は createProduct という抽象関数を持ち, ConcreteFactory がこれをオーバーロードしている. ConcreteFactory はこの関数を介して ConcreteProduct を生成している.

適当に記述すると,大体次のようになっている.

AbstractFactory {
  inherited by ConcreteFactory

  abstract method AbstractProduct createProduct()
}

ConcreteFactory {
  inherite AbstractFactory

  method AbstractProduct createProduct() {
    return new ConcreteProduct
  }
}

AbstractProduct {
  inherited by ConcreteProduct
}

ConcreteProduct {
  inherite AbstractProduct
}

継承の関係から,複数化に関して得られる制約は 「上位クラスが複数化するときは下位クラスは(全て)複数化される」 ということである. なお, 下位クラスが複数化されても特定の条件がない限り, 上位クラスは複数化されない. オーバーロードの関係から,複数化に関して得られる制約は 「オーバーロードされた関数が複数化されると, オーバーロードしている関数も複数化される.逆も同様である」 ということである.これは相互に対応している. 生成の関係は,必ずしも複数化の制約条件とはならないので, 別途記述が必要である. このため,ユーザが定義可能な制約条件を導入する必要がある.

AbstractFactory のためにユーザが記述しなければならない制約条件として, 次の2つが考えられる. 「AbstractFactory 内の createProsuct 関数が複数化された場合には, AbstractProduct が複数化される.逆も同様である」 「ConcreteFactory が複数化されると,ConcreteProduct が複数化される. 逆も同様である」 これらは,同時には起こらないものとする. 同時に起こるとすると,AbstractProduct の複数化に伴い継承により複数化さ れた ConcreteProduct の複数化が ConcreteFactory の複数化へと連鎖するこ とになる.何らかの区別をつけるということも考えられるが,とりあえず 「同時には起こらないもの」として話を進める.

以上のような複数化の制約を次のような式として列挙してみた. C[label](First, Second) は First が複数化されると Second が複数化される という意味である.

継承より(* は全てという意味)
  1. C[inherit1](AbstractFactory, ConcreteFactory*)
  2. C[inherit2](AbstractProduct, ConcreteProduct*)
オーバーライドから
  1. C[override1a](AbstractFactory.createProduct, ConcreteFactory*.createProduct)
  2. C[override1b](ConcreteFactory.createProduct, AbstractFactory.createProduct)
ユーザ定義の制約(userdef1 と userdef2 は排他)
  1. C[userdef1a](AbstractFactory.createProduct, AbstractProduct)
  2. C[userdef1b](AbstractProduct, AbstractFactory.createProduct)
  3. C[userdef2a](ConcreteFactory, ConcreteProduct)
  4. C[userdef2b](ConcreteProduct, ConcreteFactory)

これに従い,複数化が正しく行なわれるか確認してみる.なお, AbstractFactory は複数化は不可. このため開始点は,ConcreteFactory, AbstractProduct, ConcreteProduct, AbstractFactory.createProduct, ConcreteFactory.createProduct の5箇所となる.

  1. ConcreteFactory が開始点の場合

    ConcreteFactory が1番めにある制約は C[userdef2a](ConcreteFactory, ConcreteProduct) のみ. ConcreteProduct が1番めにある制約は C[userdef2b](ConcreteProduct, ConcreteFactory) のみだが,自分に戻ってくるので終了.

  2. AbstractProduct が開始点の場合

    AbstractProduct が1番めにある制約は, C[userdef1b](AbstractProduct, AbstractFactory.createProduct) と C[inherit2](AbstractProduct, ConcreteProduct*)の二つ. C[override1a](AbstractFactory.createProduct, ConcreteFactory*.createProduct) と下位クラスへ連鎖したあと,C[override1b] では AbstractFactory.createProduct に循環するので終了. 後者は,C[userdef2b] があるが,排他なので終了.

  3. ConcreteProduct が開始点の場合

    C[userdef2b](ConcreteProduct, ConcreteFactory) の一つのみ. ConcreteFactory が一番めにある C[userdef2a] は循環するのでここで 終了.

  4. AbstractFactory.createProduct が開始点の場合

    該当する制約は, C[override1a](AbstractFactory.createProduct, ConcreteFactory*.createProduct) と C[userdef1a](AbstractFactory.createProduct, AbstractProduct) の2つ.前者について, C[override1b](ConcreteFactory.createProduct, AbstractFactory.createProduct) は循環するので,終了. 後者について, C[inherit1](AbstractFactory, ConcreteFactory*) の次に, C[userdef2a](ConcreteFactory, ConcreteProduct) が該当するかと思われるが,[userdef1a] があるので終了 (優先順位か何かあるとしたほうがよい?).

  5. ConcreteFactory.createProduct が開始点の場合

    該当する制約は C[override1b](ConcreteFactory.createProduct, AbstractFactory.createProduct) の一つ.その次の AbstractFactory.createProduct には C[override1a](AbstractFactory.createProduct, ConcreteFactory*.createProduct) と C[userdef1a](AbstractFactory.createProduct, AbstractProduct) の2つが存在. 前者には自分以外も含まれているので生成.そのあと循環するので終了. 後者について, C[inherit1](AbstractFactory, ConcreteFactory*) の次に, C[userdef2a](ConcreteFactory, ConcreteProduct) が該当するかと思われるが,[userdef1a] があるので終了 (上と同様).

さて,ここでの最大の問題は, C[userdef1a] および C[userdef1b] と C[userdef2a] および C[userdef2b] が何故同時に適用されてはならないかで ある. これは,実際には ConcreteFactory.createProduct が中で呼び出している 関数(コンストラクタ ConcreteProduct())とそれが所属するクラス (ConcreteProduct から生成)との対応が余剰になってしま うことに由来している. これはコンストラクタに限らず(つまり生成関係に限らない), 呼び出しに一対一関係があるような場合には生じる問題だと思われる. そういう意味では, その呼び出し関係だけを特定化しておき, その関数を含むクラスの複数化には対応するクラスが複数化されるとするのが よいのかもしれない.


複数化のグループ

以上のような連鎖は実際,一つの複数化のグループとして定義することが 可能である. このようなグループのことをここでは複数化テンプレートと呼ぶことにする すなわち,AbstractFactory の例では,

CloningTemplate A (AbstractFactory::createProduct,
                   ConcreteFactory::createProduct,
                   AbstractProduct, ConcreteProduct)
CloningTemplate B (ConcreteFactory, ConcreteProduct)
というような2つの複数化テンプレートが定義可能である. 図で示すと次のようになる.

パターンの最初の複数化テンプレートを次のように具体化したとする. 便宜上,この具体化されたテンプレートを 「具体化された複数化テンプレート」の意味で ICT と呼ぶ. 特殊なのは,この ICT を複数含むような, 複製を行なっている側からみたグループである. いくつかの複製化テンプレートを含むパターンに対して 複製を行なう場合には, 他の複製化テンプレートからの生成物に影響を与える. つまり,共有されるような具体化された役割や関数を共有する 具体化された複製化テンプレートが生成され, 次の複製の時に連鎖を起こす. このような連鎖を起こすものをひとまとめにして 複製化グループと呼ぶことにする. 以下で CloningGroup として示されるものである.

InstantiatedPatternStructure WidgetFactory {
  InstantiatedRole WidgetFactory {
    inherited by MotifWidgetFactory

    InstantiatedRoleOperation createWindow()
  }

  InstantiatedRole MotifWidgetFactory {
    inherit WidgetFactory

    InstantiatedRoleOperation createWindow()
  }

  InstantiatedRole Window {
    inherited by MotifWindow
  }

  InstantiatedRole MotifWindow {
    inherit Window
  }

  SyslabelSpace 1 {
    AbstractFactory -> WidgetFactory
    ConcreteFactory -> MotifWidgetFactory
    AbstructProduct -> Window
    ConcreteProduct -> MotifWindow

    AbstractFactory::createProduct -> WidgetFactory::createWindow
    ConcreteFactory::createProduct -> MotifWidgetFactory::createWindow

    InstantiatedCloningTemplate A1 {
      WidgetFactory::createWindow,
      MotifWidgetFactory::createWindow,
      Window,
      MotifWindow
    }

    InstanitatedCloningTemplate B1  {
      MotifWidgetFactory,
      MotifWindow
    }
  }

  CloningGroup A1 {
    InstantiatedCloningTemplate A1
  }

  CloningGroup B1 {
    InstantiatedCloningTemplate B1
  }
}
図で示すと次のようになる.

ICT A と B の状態を図示すると,次のようになる.

データ構造的には下図のような状態である.

このような状態で CloningGroup は,次のようになる.

例えば, この状態で,CloningGroup B1 を選択し, 複製を行ない,PMWidgetFavtory と PMWindow に変えたときの 連鎖を考えてみる. ここで重要なのは, InstantiatedCloningTemplate B1 だけでなく InstantiatedCloningTemplate A1 も複製され, 複製されたクラスの置換えが起こっていることである. そして,ICT A1 と ICT A2 に共通項目があるために, 新規の CloningGroup A1 は ICT A1 と ICT A2 の両者を含むとする.

InstantiatedPatternStructure WidgetFactory {
  InstantiatedRole WidgetFactory {
    inherited by MotifWidgetFactory
    inherited by PMWidgetFactory

    InstantiatedRoleOperation createWindow()
  }

  InstantiatedRole MotifWidgetFactory {
    inherit WidgetFactory

    InstantiatedRoleOperation createWindow()
  }

  InstantiatedRole Window {
    inherited by MotifWindow
    inherited by PMMWindow
  }

  InstantiatedRole MotifWindow {
    inherit Window
  }

  InstantiatedRole PMWidgetFactory {
    inherit WidgetFactory

    InstantiatedRoleOperation createWindow()
  }

  InstantiatedRole PMWindow {
    inherit Window
  }

  SyslabelSpace 1 {
    AbstractFactory -> WidgetFactory
    ConcreteFactory -> MotifWidgetFactory
    AbstructProduct -> Window
    ConcreteProduct -> MotifWindow

    AbstractFactory::createProduct -> WidgetFactory::createWindow
    ConcreteFactory::createProduct -> MotifWidgetFactory::createWindow

    InstantiatedCloningTemplate A1 {
      WidgetFactory::createWindow,
      MotifWidgetFactory::createWindow,
      Window,
      MotifWindow
    }

    InstanitatedCloningTemplate B1  {
      MotifWidgetFactory,
      MotifWindow
    }
  }

  SyslabelSpace 2 {
    AbstractFactory -> WidgetFactory
    ConcreteFactory -> PMWidgetFactory
    AbstructProduct -> Window
    ConcreteProduct -> PMWindow

    AbstractFactory::createProduct -> WidgetFactory::createWindow
    ConcreteFactory::createProduct -> PMWidgetFactory::createWindow

    InstantiatedCloningTemplate A2 {
      WidgetFactory::createWindow,
      PMWidgetFactory::createWindow,
      Window,
      PMWindow
    }

    InstanitatedCloningTemplate B2  {
      PMWidgetFactory,
      PMWindow
    }
  }

  CloningGroup A1 {
    InstantiatedCloningTemplate A1
    InstantiatedCloningTemplate A2
  }

  CloningGroup B1 {
    InstantiatedCloningTemplate B1
  }

  CloningGroup B2 {
    InstantiatedCloningTemplate B2
  }
}
図で示すと次のようになる.

ICT A と B の状態を図示すると,次のようになる.

データ構造的には下図のような状態である.

このような状態で CloningGroup は,次のようになる.

この状態で連鎖を表現している CloningGroup A を選択して複製を行なうと, 重複を除いた命名されなければならない要素名は,展開して,

  CloningGroup A {
    WidgetFactory::createWindow,
    MotifWidgetFactory::createWindow,
    PMWidgetFactory::createWindow,
    Window,
    MotifWindow,
    PMWindow
  }
のようになる.要するに Window という項目で関連づけられた 要素群なわけであるが, この Window を ScrollBar と置き換えて複製することにする. と下記のような複製が行なわれ,全体として要素が名前空間の混乱もなく 複製されることが分かる.
InstantiatedPatternStructure WidgetFactory {
  InstantiatedRole WidgetFactory {
    inherited by MotifWidgetFactory
    inherited by PMWidgetFactory

    InstantiatedRoleOperation createWindow()
  }

  InstantiatedRole MotifWidgetFactory {
    inherit WidgetFactory

    InstantiatedRoleOperation createWindow()
    InstantiatedRoleOperation createScrollBar()
  }

  InstantiatedRole Window {
    inherited by MotifWindow
    inherited by PMWindow
  }

  InstantiatedRole MotifWindow {
    inherit Window
  }

  InstantiatedRole PMWidgetFactory {
    inherit WidgetFactory

    InstantiatedRoleOperation createWindow()
    InstantiatedRoleOperation createScrollBar()
  }

  InstantiatedRole PMWindow {
    inherit Window
  }

  InstantiatedRole ScrollBar {
    inherited by MotifScrollBar
    inherited by PMScrollBar
  }

  InstantiatedRole MotifWindow {
    inherit ScrollBar
  }

  InstantiatedRole PMScrollBar {
    inherit ScrollBar
  }

  SyslabelSpace 1 {
    AbstractFactory -> WidgetFactory
    ConcreteFactory -> MotifWidgetFactory
    AbstructProduct -> Window
    ConcreteProduct -> MotifWindow

    AbstractFactory::createProduct -> WidgetFactory::createWindow
    ConcreteFactory::createProduct -> MotifWidgetFactory::createWindow

    InstantiatedCloningTemplate A1 {
      WidgetFactory::createWindow,
      MotifWidgetFactory::createWindow,
      Window,
      MotifWindow
    }

    InstanitatedCloningTemplate B1  {
      MotifWidgetFactory,
      MotifWindow
    }
  }

  SyslabelSpace 2 {
    AbstractFactory -> WidgetFactory
    ConcreteFactory -> PMWidgetFactory
    AbstructProduct -> Window
    ConcreteProduct -> PMWindow

    AbstractFactory::createProduct -> WidgetFactory::createWindow
    ConcreteFactory::createProduct -> PMWidgetFactory::createWindow

    InstantiatedCloningTemplate A2 {
      WidgetFactory::createWindow,
      PMWidgetFactory::createWindow,
      Window,
      PMWindow
    }

    InstanitatedCloningTemplate B2  {
      PMWidgetFactory,
      PMWindow
    }
  }

  SyslabelSpace 3 {
    AbstractFactory -> WidgetFactory
    ConcreteFactory -> MotifWidgetFactory
    AbstructProduct -> ScrollBar
    ConcreteProduct -> MotifScrollBar

    AbstractFactory::createProduct -> WidgetFactory::createScrollBar
    ConcreteFactory::createProduct -> MotifWidgetFactory::createScrollBar

    InstantiatedCloningTemplate A3 {
      WidgetFactory::createScrollBar,
      MotifWidgetFactory::createScrollBar,
      Window,
      MotifWindow
    }

    InstanitatedCloningTemplate B3  {
      MotifWidgetFactory,
      MotifScrollBar
    }
  }

  SyslabelSpace 4 {
    AbstractFactory -> WidgetFactory
    ConcreteFactory -> PMWidgetFactory
    AbstructProduct -> ScrollBar
    ConcreteProduct -> PMScrollBar

    AbstractFactory::createProduct -> WidgetFactory::createScrollBar
    ConcreteFactory::createProduct -> PMWidgetFactory::createScrollBar

    InstantiatedCloningTemplate A4 {
      WidgetFactory::createScrollBar,
      PMWidgetFactory::createScrollBar,
      ScrollBar,
      PMScrollBar
    }

    InstanitatedCloningTemplate B4  {
      PMWidgetFactory,
      PMScrollBar
    }
  }

  CloningGroup A1 {
    InstantiatedCloningTemplate A1
    InstantiatedCloningTemplate A2
  }

  CloningGroup A2 {
    InstantiatedCloningTemplate A3
    InstantiatedCloningTemplate A4
  }

  CloningGroup B1 {
    InstantiatedCloningTemplate B1
    InstantiatedCloningTemplate B3
  }

  CloningGroup B2 {
    InstantiatedCloningTemplate B2
    InstantiatedCloningTemplate B4
  }
}
図で示すと次のようになる.

ICT A と B の状態を図示すると,次のようになる.

データ構造的には下図のような状態である.

手順をまとめると以下のようになる.

  1. 対象となる複製化グループ(CloningGroup)を選んでもらう

  2. 各要素に新しい名前を割り当ててもらい複製する

  3. この時各要素が属する名前空間を複製し, 複製した要素以外は前のものを引き継ぐ

  4. さらにその名前空間が他の複製化テンプレート (CloningTemplate) を 含む場合にはそのテンプレートも複製し, 複製された要素と対応づける.

    その具体化されたテンプレート (ICT) 間に要素の重複がある場合は, CloningGroup を拡張もしくは新規作成して集合を作る. 要素の重複がないような場合には,その ICT 一つを含むような CloningGroup を作成して具体化されたパターンに追加する.

  5. このようにして増えたり拡張した複数化グループを利用者に提示する


現在の課題

名前空間のところで述べたように, 役割が独立した名前空間を持たないことに誘因して, 関数のみで構成された複数化テンプレート(CloningTemplate) については上記のような 手続きではうまくいかない模様である. というか,関数のそのようなまとまりを表現する塊を用意する 必要があり,複数化テンプレートとの関係と手順に組み込んでいく必要がある. 関数のみで構成されるような複数化テンプレートとは,

CloningTemplate (Composite::Operation(),
                 Component::Operation(),
                 Leaf::Operation())
である.このような関数は Pree がフック関数とテンプレート関数と呼んでる ものに多い.Adapter のように関数名を変換する関数のようなものもある.こ の場合は名前が一致しない. 役割間をまたぐので,複数化テンプレートの定義自体はパターンのレベルで行 なわれるのが妥当である.


Mika Ohtsuki <mika@db.is.kyushu-u.ac.jp>
Last modified: Mon Dec 8 17:16:15 JST 1997