MixJuice によるデザインパターン改善カタログ

Last updated: 2005-05-11

このデザインパターン改善カタログは、デザインパターンを使用する際に生じるさまざまな問題点が、MixJuice 言語によっていかに解決されるかをカタログ化してまとめたものである。このカタログは AspectJ 、 CLOS 、 Ruby などの open class の機能を持つ言語でも利用可能である。(詳しくは「他のプログラミング言語への応用」の節を参照のこと)

表1は、それぞれのデザインパターンの問題点と、 それを解決するために使用する MixJuice のプログラミング技法をまとめたものである。 この中で特に AdapterDecoratorVisitor を読むことをお勧めする。

表1 MixJuice によるデザインパターンの改善
デザインパターン 種別 解決される問題 使用する技法
導入 拡張 情報隠蔽 型安全 複雑
AbstractFactory 改善 p.98 アブストラクトメソッド追加
別解 p.98 N N 実装モジュール選択
Builder 改善 p.108 メソッド追加
別解 N 実装モジュール選択
FactoryMethod 別解 N p.121 実装モジュール選択
Prototype 改善 p.131 アブストラクトメソッド追加
Singleton 別解 p.138 メソッド拡張
Adapter 別解 p.153 p.152 スーパーインターフェース追加
Bridge 改善 N アブストラクトメソッド追加
別解 N N 実装モジュール選択
Composite
Decorator 改善 p.191 メソッド追加, アブストラクトメソッド追加
別解 p.191 p.190 クラス拡張
Facade 改善 p.201 名前空間分離
Flyweight
Proxy
ChainOfResponsibility 改善 N p.241 メソッド追加, アブストラクトメソッド追加
Command
Interpreter 改善 N p.265 アブストラクトメソッド追加
Iterator 改善 p.280 名前空間分離
Mediator
Memento 改善 p.307 名前空間分離
Observer 改善 N クラス拡張
State 改善 N アブストラクトメソッド追加
Strategy 別解 N 実装モジュール選択
TemplateMethod
Visitor 改善1 p.359 仕様モジュールと実装モジュールの分離
改善2 N p.358 アブストラクトメソッド追加
別解 N アブストラクトメソッド追加

注:表中のページ番号は「オブジェクト指向における再利用のためのデザインパターン( Erich Gamma 、 Richard Helm 、 Ralph Johnson 、 John Vlissides 、ソフトバンク パブリッシング)」(以下 『GoF 本』と呼ぶ)の中でその問題が指摘されているページを示す。 "N" は GoF 本内でその問題が指摘されていないことを示す。

以下の節では、本カタログ内で使われている用語等について説明する。

「改善」と「別解」について

「改善」は GoF パターンの改善であることを意味する。 MixJuice は、 GoF パターンをクラス構造を変化させることなく改善することができる。改善によって、 GoF パターンのメリットを全て引き継いだ上に、いくつかの問題点を解決することができる。

「別解」は対応する GoF パターンと異なったクラス構造を持つ解であることを意味する。オリジナルの GoF パターンと比較して、一般にメリットとデメリットがある。

解決される問題のカテゴリ

解決される問題は、以下の5つのカテゴリに分類される。

導入可能性の問題

オリジナルの GoF パターンにおいて、導入の際に既存のソースコードの編集が必要になるという問題。

拡張性の問題

オリジナルの GoF パターンは、ソースコードを編集しなければある種の拡張をサポートできないという問題。

情報隠蔽の問題

オリジナルの GoF パターンにおいて、一部の名前を public にせざるを得ないという問題。( Java のパッケージまたはネストしたクラスの機構によって解決できる場合もある。)

型安全性の問題

オリジナルの GoF パターンにおいて、サブクラスのメソッドを呼び出すためにダウンキャストが必要になる場合があるという問題。

複雑さの問題

オリジナルの GoF パターンのクラス構造が複雑で、間違いを起こしやすいという問題。

レイヤードクラス図

カタログ内のそれぞれの解は「レイヤードクラス図」と MixJuice の擬似コードを用いて説明している。

レイヤードクラス図は MixJuice の差分ベースモジュールによってどのようにプログラムが拡張されるかを表現するための記法である。

詳しくは レイヤードクラス図の提案を参照されたい。

GoF 本からの引用について

このカタログは GoF 本からの引用を含んでいる。 それぞれのデザインパターンの「目的」の説明は GoF 本からそのまま引用した。 それぞれのデザインパターンのクラス図は、 UML の最近の表記に従って書き直して引用した。

他のプログラミング言語への応用

このカタログ内の解の多くは AspectJ や CLOS 、 Ruby といった他の AOP(アスペクト指向プログラミング)言語 や open class 機構を持つ言語に応用できる。

MixJuice のプログラミング技法はそのような言語のプログラミング技法と対応している。表2は MixJuice 、 AspectJ 、 CLOS 、 Ruby の技法の対応を示している。

表2 言語と技法
技法AspectJCLOSRuby
メソッド拡張around advicealias/def
スーパーインターフェース追加declare parentsdefmethodinclude
フィールド追加introductionassignment
メソッド追加introductiondefmethoddynamic method definition
アブストラクトメソッド追加introductionuseless because dynamic typing
名前空間分離aspectpackage
仕様モジュールと実装モジュールの分離
実装モジュール選択aspect selectionrequire selection

いくつかの他の言語やシステムのドキュメントにはその言語がどのようにしてデザインパターンを不要にしているかどうかを説明している。以下のページを参考にされたい。

プログラミング言語 MixJuice のホームページ

MixJuice についての詳細は、このページを参照されたい。

Aspect-Oriented Design Pattern Implementations

AspectJ を使ってデザインパターンを実装し分析している。彼らのプログラミングスタイルは我々のものとは大きく異なる。彼らは AspectJ の open class (introduction) の機能をあまり使っていない。

Abstract Factory

パターンの目的

互いに関連したり依存し合うオブジェクト群を、その具象クラスを明確にせずに生成するためのインターフェースを提供する。

GoF パターンのクラス図

structure-GoF:abstractfactory

MixJuice 版 Abstract Factory (改善)

解決される GoF パターンの問題点

拡張性の問題
新しい種類の Product を追加するために、新しい生成メソッドを AbstractFactory と ConcreteFactory に追加しなければならない。(この問題は GoF 本の p.98, 10-15行目で指摘されている。)

使用するプログラミング技法

AbstractFactory に対するアブストラクトメソッド追加を用いる。

結果

構造と擬似コード

structure:abstractfactory_sample1

module original {
  define abstract class AbstractFactory {
    define abstract AbstractProductA createProductA();
    define abstract AbstractProductB createProductB();
  }
  define class ConcreteFactory1 extends AbstractFactory {
    AbstractProductA createProductA(){...}
    AbstractProductB createProductB(){...}
  }
  define class ConcreteFactory2 extends AbstractFactory {
    AbstractProductA createProductA(){...}
    AbstractProductB createProductB(){...}
  }

  define abstract class AbstractProductA {...}
  define class ProductA1 extends AbstractProductA {...}
  define class ProductA2 extends AbstractProductA {...}

  define abstract class AbstractProductB {...}
  define class ProductB1 extends AbstractProductB {...}
  define class ProductB2 extends AbstractProductB {...}

  define class Client {
    AbstractFactory factory;
    define void m1(){
      AbstractProductA a = factory.createProductA();
      AbstractProductB b = factory.createProductB();
    }
  }
}
module extension extends original {
  class AbstractFactory {
    define abstract AbstractProductC createProductC();
  }
  class ConcreteFactory1 {
    AbstractProductC createProductC(){...}
  }
  class ConcreteFactory2 {
    AbstractProductC createProductC(){...}
  }

  define abstract class AbstractProductC {...}
  define class ProductC1 extends AbstractProductC {...}
  define class ProductC2 extends AbstractProductC {...}

  class Client {
    define void m2(){
      AbstractProductC c = factory.createProductC();
    }
  }
}

MixJuice 版 Abstract Factory (別解)

解決される GoF パターンの問題点

拡張性の問題
新しい種類の Product を追加するために、新しい生成メソッドを AbstractFactory と ConcreteFactory に追加しなければならない。(この問題は GoF 本の p.98, 10-15行目で指摘されている。)
型安全性の問題
オリジナルの GoF パターンでは Product の特定のメソッドを呼び出すために AbstractProduct から Product へのダウンキャストを必要とする。(この問題は GoF 本で指摘されていない。)
複雑さの問題
GoF パターンのオリジナルのクラス構造は複雑である。(この問題は GoF 本で指摘されていない。)

使用するプログラミング技法

Product クラスに対して実装モジュール選択を用いる。

結果

構造と擬似コード

structure:abstractfactory_sample2

module products {
  define class ProductA {
    define abstract ProductA();
    ...
  }
  define class ProductB {
    define abstract ProductB();
    ...
  }
}

module products.implementation1 extends products {
  class ProductA {
    ProductA() {...}
    ...
  }
  class ProductB {
    ProductB() {...}
    ...
  }
}

module products.implementation2 extends products {
  class ProductA {
    ProductA() {...}
    ...
  }
  class ProductB {
    ProductB() {...}
    ...
  }
}

module client extends products {
  ... new ProductA() ...
  ... new ProductB() ...
}

Builder

パターンの目的

複合オブジェクトについて、その作成過程を表現形式に依存しないものにすることにより、同じ作成過程で異なる表現形式のオブジェクトを生成できるようにする。

GoF パターンのクラス図

structure-GoF:builder

MixJuice 版 Builder (改善)

解決される GoF パターンの問題点

拡張性の問題
ソースコードを編集することなく Builder と ConcreteBuilder に新しいメソッドを加えることができない。(この問題は GoF 本の p.108, 35行目 - p.109, 1-3行目で指摘されている。)

使用するプログラミング技法

ConcreteBuilder クラスに対するメソッド追加を用いる。

結果

構造と擬似コード

structure:builder_sample1

module original {
 define abstract class Builder {
   define void buildPartA(){}
   define void buildPartB(){}
 }
 define class ConcreteBuilder extends Builder {
   void buildPartA(){...}
   void buildPartB(){...}
 }
 define class Director {
   Builder builder;
   define void construct(){
     builder.buildPartA();
     builder.buildPartB();
   }
 }
}
module extension extends original {
 class Builder {
   define void buildPartC(){}
 }
 define class ConcreteBuilder2 extends Builder {
   void buildPartA(){...}
   void buildPartB(){...}
   void buildPartC(){...}
 }
 class Director {
   void construct(){
     original();
     builder.buildPartC();
   }
 }
}

MixJuice 版 Builder (別解)

解決される GoF パターンの問題点

複雑さの問題
GoF パターンのオリジナルのクラス構造は複雑である。

使用するプログラミング技法

Builder に対して実装モジュール選択を用いる。

結果

構造と擬似コード

structure:builder_sample2

module builder {
  define class Builder {
    // このクラスは Director の関数を持っている。
    define void construct() { ... }

    define void buildPartA() {}
    define void buildPartB() {}
    define void buildPartC() {}
  }
}

module builder.implementation1 extends builder {
  class Builder {
    void buildPartA() { ... }
    void buildPartB() { ... }
    void buildPartC() { ... }
  }
}

module builder.implementation2 extends builder {
  class Builder {
    void buildPartA() { ... }
    void buildPartB() { ... }
    void buildPartC() { ... }
  }
}

Factory Method

パターンの目的

オブジェクトを生成するときのインターフェースだけを規定して、実際にどのクラスをインスタンス化するかはサブクラスが決めるようにする。 Factory Method パターンは、インスタンス化をサブクラスに任せる。

GoF パターンのクラス図

structure-GoF:factorymethod

MixJuice 版 Factory Method (別解)

解決される GoF パターンの問題点

型安全性の問題
オリジナルの GoF パターンでは ConcreteProduct の特定のメソッドを呼び出すために Product から ConcreteProduct へのダウンキャストを必要とする。(この問題は GoF 本で指摘されていない。)
複雑さの問題
オリジナルの GoF パターンのクラス構造は複雑である。(この問題は GoF 本の p.121, 17-19行目で指摘されている。)

使用するプログラミング技法

Product と Creator に対して実装モジュール選択を用いる。

結果

構造と擬似コード

structure:factorymethod_sample1

module m {
  define class Product {
    define abstract Product();
  }
  define class Creator {
    define Product product;
    define void anOperation() { ... product = new Product(); ... }
  }
}

module m.implementation extends m {
  class Product {
    Product() {...}
    define void concreteMethod() {...}
  }
  class Creator {
    define void anotherOperation() {
      ...
      product.concreteMethod(); // キャストは必要ない。
      ...
    }
  }
}

Prototype

パターンの目的

生成すべきオブジェクトの種類を原型となるインスタンスを使って明確にし、それをコピーすることで新たなオブジェクトの生成を行う。

GoF パターンのクラス図

structure-GoF:prototype

MixJuice 版 Prototype (改善)

解決される GoF パターンの問題点

導入可能性の問題
オリジナルの GoF パターンはソースコードを編集しなければ導入することができない。(この問題は GoF 本の p.131, 1-5行目で指摘されている。)

使用するプログラミング技法

既存のクラスに対して "copy()" アブストラクトメソッド追加を用いる。

結果

構造と擬似コード

structure:prototype_sample1

module original {
  define abstract class AbstractProduct {...}
  define class ConcreteProduct1 extends AbstractProduct {...}
  define class ConcreteProduct2 extends AbstractProduct {...}
}

module extension extends original {
  class AbstractProduct {
    define abstract AbstractProduct copy();
  }

  class ConcreteProduct1 {
    AbstractProduct copy() {...}
  }

  class ConcreteProduct2 {
    AbstractProduct copy() {...}
  }
}

Singleton

パターンの目的

あるクラスに対してインスタンスが一つしか存在しないことを保証し、それに対してグローバルにアクセスするための方法を提供する。

GoF パターンのクラス図

structure-GoF:singleton

MixJuice 版 Singleton (別解)

解決される GoF パターンの問題点

複雑さの問題
オリジナルの GoF パターンは複雑で問題を起こしやすい。(この問題は GoF 本で指摘されていないが、スタティックメソッドの問題点は p.138, 24-29行目に示されていて、これはこの問題点が無ければスタティックメソッドが望ましいことを暗示している。)

使用するプログラミング技法

スタティックメソッドに対するメソッド拡張が可能ならば、拡張性の制約を解決できる。(ただし MixJuice の現在のバージョン 1.0 ではスタティックメソッドの拡張をサポートしていないので注意。)

結果

構造と擬似コード

structure:singleton_sample1

module original {
  define class C {
    define static void operation() {...}
    define static int data;
  }
}

module extension extends original {
  class C {
    static void operation() { ... original() ...}
  }
}

Adapter

パターンの目的

あるクラスのインターフェースを、クライアントが求める他のインターフェースへ変換する。 Adapter パターンは、インターフェースに互換性のないクラス同士を組み合わせることができるようにする。

GoF パターンのクラス図

Class adapter

structure-GoF:adapter1

Object adapter

structure-GoF:adapter2

MixJuice 版 Adapter (別解)

解決される GoF パターンの問題点

拡張性の問題
Adapter の潜在的な問題は、すべてのクライアントに対して透過性がないことである。適合されたオブジェクトは、もはや Adaptee クラスのインタフェースには従っていない。そのため、本来の Adaptee オブジェクトと同じように使用することはできなくなる。(この問題は GoF 本の p.153, 行目26-31 で指摘されている。)
複雑さの問題
クラス Adapter を用いてクラスやその全てのサブクラスを適合するために、それぞれのクラスは対応するクラスアダプタを必要とする。(この問題は GoF 本の p.152, 11-13行目で指摘されている。)

使用するプログラミング技法

Adapter クラスへのスーパーインターフェース追加を用いる。

結果

以下はこの対策におけるクラス Adapter やオブジェクト Adapter との間のトレードオフである。(GoF 本の p.152, 8行目以降のクラス Adapter とオブジェクト Adapter も参照されたい。)

構造と擬似コード

structure:adapter_sample1

module library {
  define class Adaptee {
    define void specificRequest() {...}
  }
  define class SubAdaptee extends Adaptee {...}
}
module application extends library {
  define interface Target {
    define void request();
  }
  class Adaptee implements Target {
    void request() { specificRequest(); }
  }
}

Bridge

パターンの目的

抽出されたクラスと実装を分離して、それらを独立に変更できるようにする。

GoF パターンのクラス図

structure-GoF:bridge

MixJuice 版 Bridge (改善)

解決される GoF パターンの問題点

拡張性の問題
ソースコードを編集することなく Abstraction と Implementor に新しいオペレーションを加えることができない。(この問題は GoF 本で指摘されていない。)

使用するプログラミング技法

Abstraction と Implementor に対してアブストラクトメソッド追加を用いる。

結果

構造と擬似コード

structure:bridge_sample1

module original {
  define abstract class Abstraction {
    Implementor imp;
    define void operation() { imp.operationImp(); }
  }

  define class RefinedAbstraction extends Abstraction {
    ...
  }

  define abstract class Implementor {
    define abstract void operationImp();
  }

  define class ConcreteImplementorA extends Implementor {
    void operationImp() { ... }
  }

  define class ConcreteImplementorB extends Implementor {
    void operationImp() { ... }
  }
}

module extension extends original {
  class Abstraction {
    define void operation2() { imp.operationImp2(); }
  }

  class Implementor {
    define abstract void operationImp2();
  }

  class ConcreteImplementorA {
    void operationImp2() { ... }
  }

  class ConcreteImplementorB {
    void operationImp2() { ... }
  }
}

MixJuice 版 Bridge (別解)

解決される GoF パターンの問題点

型安全性の問題
ConcreteImplementor の特定のメソッドを呼び出すために、オリジナルの GoF パターンは Implementor から ConcreteImplementor へのダウンキャストを必要とする。(この問題は GoF 本では指摘されていない。)
複雑さの問題
オリジナルの GoF パターンのクラス構造は複雑である。(この問題は GoF 本では指摘されていない。)

使用するプログラミング技法

Abstraction に対して実装モジュール選択を用いる。

結果

(+) ダウンキャストは不要になる。この対策はプログラムが1種類の Abstraction のみしか必要としない場合のみに適用できる。

構造と擬似コード

structure:bridge_sample2

module original {
  define class Abstraction {
    define void operation() {
      ... implementationOperation1(); ...
    }

    define abstract void implementationOperation1();
    define abstract void implementationOperation2();
  }

  define class RefinedAbstractionA extends Abstraction {...}
  define class RefinedAbstractionB extends Abstraction {...}
}

module implementation1 extends original {
  class Abstraction {
    void implementationOperation1() {...}
    void implementationOperation2() {...}
  }
}

module implementation2 extends original {
  class Abstraction {
    void implementationOperation1() {...}
    void implementationOperation2() {...}
  }
}

Composite

重要な改善は発見できなかった。

Decorator

パターンの目的

オブジェクトに責任を動的に追加する。 Decorator パターンは、サブクラス化よりも柔軟なオペレーション拡張方法を提供する。

GoF パターンのクラス図

structure-GoF:decorator

MixJuice 版 Decorator (改善)

解決される GoF パターンの問題点

拡張性の問題
ソースコードを編集せずに Component と Decorator に新しいメソッドを追加することができない。(この問題は GoF 本の p.191, 7-9行目で指摘されている。)

使用するプログラミング技法

Component や Decorator へのメソッドまたはアブストラクトメソッド追加を用いる。

結果

構造と擬似コード

structure:decorator_sample1

module original {
  define abstract class Component {
    define abstract void operation();
  }
  define class ConcreteComponent extends Component {
    void operation(){...}
  }
  define abstract class Decorator extends Component {
    Component component;
    void operation(){ component.operation(); }
  }
  define class ConcreteDecorator extends Decorator {
    void operation(){...}
  }
}
module extension extends original {
  class Component {
    define abstract void newOperation();
  }
  class ConcreteComponent {
    void newOperation(){...}
  }
  class Decorator {
    void newOperation(){ component.newOperation(); }
  }
}

MixJuice 版 Decorator (別解)

解決される GoF パターンの問題点

拡張性の問題
ソースコードを編集せずに Component と Decorator に新しいメソッドを追加することができない。(この問題は GoF 本の p.191, 7-9行目で指摘されている。)
複雑さの問題
Decorator とそれが修飾しているコンポーネントが同一ではない。(この問題は GoF 本の p.190, 30-33行目で指摘されている。)
多くの小さなオブジェクトが必要になる。(この問題は GoF 本の p.190, 34行目 - p.191, 1-4行目で指摘されている。)

使用するプログラミング技法

Component に対してクラス拡張を用いる。

結果

構造と擬似コード

structure:decorator_sample2

module original {
  define class Component {
    define void operation();
  }
}
module extension1 extends original {
  class Component {
    void operation(){...}
  }
}
module extension2 extends original {
  class Component {
    void operation(){...}
  }
}

Facade

パターンの目的

サブシステム内に存在する複数のインターフェースに1つの統一インターフェースを与える。 Facade パターンはサブシステムの利用を容易にするための高レベルインターフェースを定義する。

GoF パターンのクラス図

structure-GoF:facade

MixJuice 版 Facade (改善)

解決される GoF パターンの問題点

情報隠蔽の問題
サブシステム内にあるクラスをクライアントに対して非公開にすることは「古い」オブジェクト指向言語ではサポートされていない。(この問題は GoF 本の p.201, 4-8行目で指摘されている。)

使用するプログラミング技法

名前空間分離を用いる

結果

構造と擬似コード

structure:facade_sample1

module subsystem {
  define class C1 { ...  }
  define class C2 { ...  }
  ...
}

module facade {
  define class Facade {
    define abstract void easy_operation();
  }
}

module facade.implementation extends facade, subsystem {
  class Facade {
    void easy_operation() { ... }
  }
}

Flyweight

重要な改善は発見できなかった。

Proxy

重要な改善は発見できなかった。

Chain of Responsibility

パターンの目的

1つ以上のオブジェクトに要求を処理する機会を与えることにより、要求を送信するオブジェクトと受信するオブジェクトの結合を避ける。受信する複数のオブジェクトをチェーン状につなぎ、あるオブジェクトがその要求を処理するまで、そのチェーンに沿って要求を渡していく。

GoF パターンのクラス図

structure-GoF:chain

MixJuice 版 Chain of Responsibility (改善)

解決される GoF パターンの問題点

導入可能性の問題
ソースコードを編集せずに既存のクラスに Chain of Responsibility パターンを導入することはできない。(この問題は GoF 本では指摘されていない。)
拡張性の問題
ソースコードを編集せずに Handler に新しいオペレーションを追加することはできない。(この問題は GoF 本の p.241, 22-25行目で指摘されている。)

使用するプログラミング技法

Handler クラスと ConcreteHandler クラスにメソッド追加を用いる。

結果

構造と擬似コード

structure:chain_sample1

module original {
  define abstract class Handler {
    Handler successor;
    define void handleRequestA(){ successor.handleRequestA(); }
  }

  define class ConcreteHandler1 extends Handler {
    void handleRequestA() {...}
  }

  define class ConcreteHandler2 extends Handler {
    void handleRequestA() {...}
  }
}

module extension extends original {
  class Handler {
    define void handleRequestB(){ successor.handleRequestB(); }
  }

  class ConcreteHandler1 {
    void handleRequestB() {...}
  }

  class ConcreteHandler2 {
    void handleRequestB() {...}
  }
}

Command

重要な改善は発見できなかった。

Interpreter

パターンの目的

言語に対して、文法表現と、それを使用して文を解釈するインタープリタを一緒に定義する。

GoF パターンのクラス図

structure-GoF:interpreter

MixJuice 版 Interpreter (改善)

解決される GoF パターンの問題点

導入可能性の問題
ソースコードを編集せずに既存の木構造に Interpreter パターンを導入することができない。(この問題は GoF 本では指摘されていない。)
拡張性の問題
ソースコードを編集せずに表現を解釈する新しい方法を追加することができない。(この問題は GoF 本の p.265, 11-15行目で指摘されている。)

使用するプログラミング技法

AbstractExpression に対してアブストラクトメソッド追加を用いる。

結果

構造と擬似コード

structure:interpreter_sample1

module original {
  define class Client {
    AbstractExpression exp;
    Context context;
    define void m(){
      exp.interpret(context);
    }
  }
  define class Context {...}
  define abstract class AbstractExpression {
    define abstract void interpret(Context context);
  }
  define class ExpressionA extends AbstractExpression {
    void interpret(Context context){...}
  }
  define class ExpressionB extends AbstractExpression {
    void interpret(Context context){...}
  }
}
module extension extends original {
  class AbstractExpression {
    define abstract void newOperation();
  }
  class ExpressionA {
    void newOperation(){...}
  }
  class ExpressionB {
    void newOperation(){...}
  }
}

Iterator

パターンの目的

集約オブジェクトが基にある内部表現を公開せずに、その要素に順にアクセスする方法を提供する。

GoF パターンのクラス図

structure-GoF:iterator

MixJuice 版 Iterator (改善)

解決される GoF パターンの問題点

情報隠蔽の問題
Aggregate の内部状態は Iterator からはアクセスできるべきだが、クライアントに対しては隠蔽されているべきである。(この問題は GoF 本の p.280, 24-29行目で指摘されている。)この問題は Java のパッケージ/ネストしたクラスの機構によって解決できることに注意。

使用するプログラミング技法

ConcreteAggregate と ConcreteIterator に対して名前空間分離を用いる。

結果

構造と擬似コード

structure:iterator_sample1

module aggregate {
  define class Aggregate {
    define abstract Iterator createIterator();
  }

  define class Iterator {
    define abstract void first();
    define abstract void next();
    define abstract boolean isDone();
    define abstract Object currentItem();
  }

  define class ConcreteAggregate extends Aggregate {
  }
  define class ConcreteIterator extends Iterator {
  }
}

module aggregate.implementation extends aggregate {
  class ConcreteAggregate {
    Iterator createIterator() {...}
  }
  class ConcreteIterator {
    // ConcreteAggregate に直接アクセスできる。
    void first() {...}
    void next() {...}
    boolean isDone() {...}
    Object currentItem() {...}
  }
}

Mediator

重要な改善は発見できなかった。

Memento

パターンの目的

カプセル化を破壊せずに、オブジェクトの内部状態を捉えて外面化しておき、オブジェクトを後にこの状態に戻すことができるようにする。

GoF パターンのクラス図

structure-GoF:memento

MixJuice 版 Memento (改善)

解決される GoF パターンの問題点

情報隠蔽の問題
Memento の内部状態は Originator からはアクセスできるべきだが、クライアントに対しては隠蔽されているべきである。(この問題は GoF 本の p.307, 8-10行目で指摘されている。)この問題は Java のパッケージ/ネストしたクラスの機構によって解決できることに注意。

使用するプログラミング技法

Memento に対して名前空間分離を用いる。

結果

構造と擬似コード

structure:memento_sample1

module m {
  define class Originator {
    define abstract void setMemento(Memento m);
    define abstract Memento createMemento();
  }

  define class Memento {}
}

module m.implementation extends m {
  class Originator {
    int state;
    void setMemento(Memento m) {...}
    Memento createMemento() {...}
  }

  class Memento {
    int state;
  }
}

Observer

パターンの目的

あるオブジェクトの状態が変更されたときに、それに依存するすべてのオブジェクトに自動的にそのことが知らされ、また、それらが更新されるように、オブジェクト間に一対多の依存関係を定義する。

GoF パターンのクラス図

structure-GoF:observer

MixJuice 版 Observer (改善)

解決される GoF パターンの問題点

導入可能性の問題
ソースコードを編集せずに既存のプログラムに Observer パターンを導入することはできない。(この問題は GoF 本では指摘されていない。)

使用するプログラミング技法

Subject に対してクラス拡張を用いる。

結果

構造と擬似コード

structure:observer_sample1

module original {
  define class Subject {
    define void sideEffectOperation() {...}
  }
}

module extension extends original {
  define abstract class Observer {
    define abstract void update();
  }

  class Subject {
    define void attach(Observer o) {...}
    define void detach(Observer o) {...}
    define void changed() {...}

    void sideEffectOperation() {
      original();
      changed();
    }
  }
}

State

パターンの目的

オブジェクトの内部状態が変化したときに、オブジェクトが振る舞いを変えるようにする。クラス内では、振る舞いの変化を記述せず、状態を表すオブジェクトを導入することでこれを実現する。

GoF パターンのクラス図

structure-GoF:state

MixJuice 版 State (改善)

解決される GoF パターンの問題点

拡張性の問題
ソースコードを編集せずに State に新しいオペレーションを追加することはできない。(この問題は GoF 本では指摘されていない。)

使用するプログラミング技法

State に対するアブストラクトメソッド追加を用いる。

結果

構造と擬似コード

structure:state_sample1

module original {
  define class Context {
    State state;
    define void request(){
      state.handle();
    }
  }
  define abstract class State {
    define abstract void handle();
  }
  define class ConcreteStateA extends State {
    void handle(){...}
  }
  define class ConcreteStateB extends State {
    void handle(){...}
  }
}
module extension extends original {
  class State {
    define abstract void newOperation();
  }
  class ConcreteStateA {
    void newOperation(){...}
  }
  class ConcreteStateB {
    void newOperation(){...}
  }
}

Strategy

パターンの目的

アルゴリズムの集合を定義し、各アルゴリズムをカプセル化して、それらを交換可能にする。 Strategy パターンを利用することで、アルゴリズムを、それを利用するクライアントからは独立に変更することができるようになる。

GoF パターンのクラス図

structure-GoF:strategy

MixJuice 版 Strategy (別解)

解決される GoF パターンの問題点

複雑さの問題
オリジナルの GoF パターンのクラス構造は複雑である。(この問題は GoF 本では指摘されていない。)

使用するプログラミング技法

Context に対して実装モジュール選択を用いる。

結果

構造と擬似コード

structure:strategy_sample1

module m {
  define class C {
    define void contextInterface() {
      ... algorithmInterface() ...
    }
    define abstract void algorithmInterface();
  }
}
module m.strategyA extends m {
  class C { void algorithmInterface() {...} }
}
module m.strategyB extends m {
  class C { void algorithmInterface() {...} }
}

Template Method

重要な改善は発見できなかった。

Visitor

パターンの目的

あるオブジェクト構造上の要素で実行されるオペレーションを表現する。 Visitor パターンにより、オペレーションを加えるオブジェクトのクラスに変更を加えずに、新しいオペレーションを定義することができるようになる。

GoF パターンのクラス図

structure-GoF:visitor

MixJuice 版 Visitor (改善1)

解決される GoF パターンの問題点

情報隠蔽の問題
オブジェクトの構造を表すフィールドを、公開属性にしなければならないことがある。(この問題は GoF 本の p.359, 5-8行目で指摘されている。)

使用するプログラミング技法

ConcreteElement クラスに対して設計モジュールと実装モジュールの分割を用いる。(この改善は以下で述べられる別解に対しても適用できる。)

結果

構造と擬似コード

structure:visitor_sample1

module element {
  define abstract class Element {
    define abstract void accept(Visitor visitor);
  }
  define class ConcreteElementA extends Element {
    void accept(Visitor v){ v.visitConcreteElementA(this); }
  }
  define class ConcreteElementB extends Element {
    void accept(Visitor v){ v.visitConcreteElementB(this); }
  }
  define abstract class Visitor {
    define abstract void visitConcreteElementA(ConcreteElementA a);
    define abstract void visitConcreteElementB(ConcreteElementB a);
  }
}
module element.implementation extends element {
  class ConcreteElementA {
    int state;
  }
  class ConcreteElementB {
    int state;
  }
}
module visitor extends element {
  define class ConcreteVisitor extends Visitor {}
}
module visitor.implementation
  extends visitor, element.implementation {
  class ConcreteVisitor {
    void visitConcreteElementA(ConcreteElementA a){
      ... int x = a.state; ...
    }
    void visitConcreteElementB(ConcreteElementB b){
      ... int x = b.state; ...
    }
  }
}

MixJuice 版 Visitor (改善2)

解決される GoF パターンの問題点

導入可能性の問題
オブジェクト構造のソースコードを編集せずに既存のオブジェクト構造に Visitor パターンを導入することができない。(この問題は GoF 本では指摘されていない。)
拡張性の問題
Visitor クラスや ConcreteVisitor クラスのソースコードを編集せずに新しい ConcreteElement クラスを導入することができない。(この問題は GoF 本の p.358, 7-12行目で指摘されている。)

使用するプログラミング技法

Element クラスと ConcreteElement クラスに対するアブストラクトメソッド追加を用いることによって導入可能性の問題を解決できる。 Visitor や ConcreteVisitor に対してアブストラクトメソッド追加を用いると拡張性の問題を解決できる。

結果

構造と擬似コード

structure:visitor_sample2

module element {
  define abstract class Element {...}
  define class ConcreteElementA extends Element {...}
  define class ConcreteElementB extends Element {...}
}

module visitor extends element {
  class Element { define abstract void accept(Visitor v); }
  class ConcreteElementA { void accept(Visitor v) { v.visit(this); } }
  class ConcreteElementB { void accept(Visitor v) { v.visit(this); } }

  define class Visitor {
    define abstract void visit(ConcreteElementA elt);
    define abstract void visit(ConcreteElementB elt);
  }

  define class ConcreteVisitor1 extends Visitor {
    void visit(ConcreteElementA elt) {...}
    void visit(ConcreteElementB elt) {...}
  }

  define class ConcreteVisitor1a extends ConcreteVisitor1 {
    void visit(ConcreteElementA elt) {...}
    void visit(ConcreteElementB elt) {...}
  }
}

module new_concrete_element extends visitor {
  define class ConcreteElementC extends Element {
    ...
    void accept(Visitor v) { v.visit(this); }
  }

  class Visitor {
    define abstract void visit(ConcreteElementC elt);
  }

  class ConcreteVisitor1 {
    void visit(ConcreteElementC elt) {...}
  }

  class ConcreteVisitor1a {
    void visit(ConcreteElementC elt) {...}
  }
}

MixJuice 版 Visitor (別解)

解決される GoF パターンの問題点

複雑さの問題
Visitor パターンのクラス構造は複雑で間違いを起こしやすい。(この問題は GoF 本では指摘されていない。)

使用するプログラミング技法

既存のオブジェクト構造に対してアブストラクトメソッド追加を用いる。

結果

構造と擬似コード

structure:visitor_sample3

module element {
  define abstract class Element {...}
  define class ConcreteElementA extends Element {...}
  define class ConcreteElementB extends Element {...}
}

module traverser extends element {
  class Element { define abstract void traverse(); }
  class ConcreteElementA { void traverse() {...} }
  class ConcreteElementB { void traverse() {...} }
}

MixJuice プログラミングの技法

デザインパターンの改善策において使用される、 MixJuice のプログラミング技法には次のようなものがある。

メソッド拡張

既存のメソッドの処理をオーバライドによって拡張することができる。

structure-08-07:c01

module original {
  define class C {
    define void m(){...}
  }
}
module extension extends original {
  class C {
    void m(){ original(); ...}
  }
}

スーパーインターフェース追加

既存のクラスに新しいスーパーインターフェースを追加することができる。言い換えると、既存のクラスを新しいインターフェースのサブタイプにすることができる。

structure-08-07:c02

module original {
  define class C {...}
  define interface I {...}
}
module extension extends original {
  class C implements I {...}
}

フィールド追加

既存のクラスに新しいフィールドを追加することができる。

structure-08-07:c03

module original {
  define class C {...}
}
module extension extends original {
  class C {
    int newField;
  }
}

メソッド追加

既存のクラスに新しいメソッドを追加することができる。

structure-08-07:c04

module original {
  define class C {...}
}
module extension extends original {
  class C {
    define void newMethod(){...}
  }
}

クラス拡張

スーパーインターフェース追加、メソッド追加、フィールド追加の組み合わせ。

アブストラクトメソッド追加

既存のアブストラクトクラスに対して新しいアブストラクトメソッドを追加できる。全ての具象サブクラスに対してそのメソッドに対応する実装を追加しなければ、実行可能なプログラムにならない。

structure-08-07:c05

module original {
  define abstract class S {...}
  define class A extends S {...}
  define class B extends S {...}
}
module extension extends original {
  class S {
    define abstract void newMethod();
  }
  class A {
    void newMethod(){...}
  }
  class B {
    void newMethod(){...}
  }
}

名前空間分離

クラスの境界と独立な情報隠蔽の境界を作ることができる。名前を定義する側は、提供する名前の集合を任意にグループ化できる。定義された名前を利用する側は、グループ化された名前の集合を任意に選んで利用できる。

structure-08-07:c06

module namesA {
  define class A1 {...}
  define class A2 {...}
  ...
}
module namesB {
  define class B1 {...}
  define class B2 {...}
  ...
}
module clientOfA extends namesA { ... }
module clientOfB extends namesB { ... }
module clientOfBothAandB  extends namesA, namesB { ... }

仕様モジュールと実装モジュールの分離

クラスを二つのモジュールに分割する。片方を仕様モジュールと呼び、仮想コンストラクタ仮想メソッドによる外部インターフェースのみを定義する。一方を実装モジュールと呼び、クラスの内部実装を定義する。

module c {
  define class C {
    // 公開メソッド
    define abstract void m1();
    define abstract void m2();
  }
}
module c.implementation extends c {
  class C {
    // 公開メソッド
    void m1(){...}
    void m2(){...}
    // protected fields and methods
    int f1;
    define void m3(){...}
  }
}

実装モジュール選択

リンク時にユーザが複数の異なる実装モジュールから一つの実装モジュールを選択することができる。(ユーザはモジュールのコンフリクトを避けるためにクラスの実装モジュールを慎重にただ一つだけ選ばなければならない。現在の MixJuice リンカーの実装では一つのクラスに対して一つより多い実装モジュールが選択されたとしても、エラーにならない。)

structure-08-07:c08

module m {
  define class C {  // 注意: C はアブストラクトクラスではない。
    define abstract void m();
  }
}
module m.implementationA extends m {
  class C {
    void m(){...}
  }
}
module m.implementationB extends m {
  class C {
    void m(){...}
  }
}

Akira TANAKA <akr@m17n.org>, Yuuji ICHISUGI <y-ichisugi@aist.go.jp>

Top MixJuice Top