GoF
生成に関するパターン
Factory Method
- 使用目的
複数のサブクラス実装を持つ、多種類の抽象クラスを作る際に、
あるサブクラスと一緒に使用するべき別のサブクラスを指定する。
- 有効性
コンポジションやラッパークラスを作ったほうが良い
Abstract Factory
- 使用目的
状況に応じた何らかのサブクラスを生成する、
ファクトリークラス自体を生成するファクトリークラス
- 有効性
生成クラスグループの選択処理を別クラスに委譲・隠蔽するという点は良いが、
生成クラスグループに関してはコンポジションやラッパークラスを作ったほうが良い
Builder
- 使用目的
複数の非設定可能な(オプションの)初期パラメータを持つクラスを作る場合に、
それぞれの引数の組み合わせのコンストラクタを作るのではなく、
その作成対象クラスにオプションを与え、後からクラス自体を生成するというクラスを用意する。
- 実装例
class HumanClass
{
/* 複数のコンストラクタが不要
*
* HumanClass(){}
* HumanClass(String name){}
* HumanClass(int age){}
* HumanClass(String name, int age){}
*/
HumanClass(String name, int age){}
}
class Builder
{
private String name;
private int age = -1;
void setName(String val){ name=val; }
void setAge(int val){ age=val; }
HumanClass getHuman()
{
if(name==null)
{
if(age==-1)
{
return new HumanClass("No Name", 999);
}
else
{
return new HumanClass("No Name", age);
}
}
else
{
if(age==-1)
{
return new HumanClass(name, 999);
}
else
{
return new HumanClass(name, age);
}
}
}
}
- 有効性
以下の代替案のほうが良い。
引数無しコンストラクタとオプション引数はsetterメソッドとしてクラス生成後に設定できるようにすれば良い。
class HumanClass
{
HumanClass setName(String name){ this.name = name; return this; }
HumanClass setAge(int age){ this.age = age; return this; }
}
Prototype
- 使用目的
似たような複数のクラスが必要な場合、その種類分のクラスを定義するのではなく、
プロトタイプとなるクラスを1つ定義し、内部変数で、
どの種類であるかを表すことで、クラス定義数を削減する。
インスタンス生成の際は、予め定義した各種類のインスタンスを
クローンすることで生成すれば、毎回状態を設定する必要はない。
- 有効性
プロトタイプクラス内で、ある場合では使用しないメンバー変数があったりと、
無駄な部分が多くなる。
ただし、予めインスタンスを一定数生成し、プールして使用するという場合に、
有効であるかもしれない。
Singleton
- 使用目的
あるクラスを複数生成することを許さず、共有させる
- 有効性
そもそもインスタンスを生成せずに、staticな機能として検討できないか考慮する。
できない場合は、有効。
構造に関するパターン
Adapter
- 使用目的
ある2種類のクラスがあり、一方(Aクラス)をもう一方(Bクラス)として使用したい場合、
両方を統合しなおすのではなく、Bクラスを継承した内部にAクラスを持つクラスを定義する。
そのクラスの内部ではBクラスから継承したメソッドが呼ばれた場合は
それを中継してAクラスのものを呼ぶようにする。
- 有効性
可能な限りクラスを再編したほうが保守面でよい
Bridge
- 使用目的
相互に呼び合う相関の強い2クラスをそれぞれ継承して新たなクラスを作成するとき、
親クラスか子クラスか、それぞれ2クラスでどれを使用するかが複雑になるが、
一方のクラスで対応するクラスを生成するようにすれば、
一方のクラスが決まった時点で、もう一方もどちらのクラスを使用すればよいかが決まる。
- 有効性
2クラスを統合してコンポジションしてしまえばよい
Composite
- 使用目的
階層構造的関係を持つオブジェクトの集合に対して、
上位のオブジェクトで何らかの処理を呼び出すとき、
その呼び出しを下位階層のオブジェクトに伝播させる。
Decorator
- 使用目的
クラスを拡張する際に継承するのではなく、拡張対象クラスのインスタンスを
内部に保持するクラスを作成し、そのクラスに拡張機能を実装する。
元々のクラスの機能を呼ぶ際は、拡張クラスから中継して返す。
つまりラッパークラスを作り、メソッド追加や返り値の修正などを行う。
- 実装例
class Origin
{
private String c="";
String concat(String a, String b)
{
return a+b+c;
}
}
class OriginExtended
{
private Origin origin = new Origin();
private String d="";
String concat(String a, String b)
{
return origin.concat(a,b)+d;
}
String concat2nd(String a, String b)
{
return a+b+d;
}
}
- 有効性
拡張が必要になったとき、拡張対象のクラスを改造せずに、
残す必要があるか再考する必要がある。
Facade
- 使用目的
既存のクラスを使用して新しい汎用メソッドを作成する場合、
それを新たなクラスのメソッドとして提供することで、
そのクラスを汎用ライブラリ化する。
- 有効性
場合によっては既存のクラスの継承による拡張で代替したほうが良いか考える。
汎用性の低い場合は、別クラス・別メソッドとして抜き出さないほうが良い場合もある。
Flyweight
- 実装例
class HeavyClass
{
HeavyClass(){ /*重い処理*/ }
HeavyClass setGroup(String group){ this.group=group; return this; }
HeavyClass setValue(int val){ this.val=val; return this; }
}
class HeavyClassManager
{
Map<String, HeavyClass> pool = new Map<String, HeavyClass>();
HeavyClass getHeavyClass(String group, int val)
{
HeavyClass obj = pool.get(group);
if(obj==null)
{
obj = new HeavyClass();
obj.setGroup(group);
pool.put(group, obj);
}
return obj.setValue(val);
}
}
- 有効性
使用すると可読性が落ちるので使用できるケースは少ない
Proxy
- 使用目的
あるメソッド呼び出しの前後にそのメソッドを改造せずに
何らかの処理を加えたい場合、別のメソッドを用意し、
内部で元のメソッドを呼び出し、その前後に処理を加える。
- 実装例
class Origin
{
String concat(String a, String b)
{
return a+b;
}
}
class Proxy
{
private Origin origin = new Origin();
String concat(String a, String b)
{
return "\""+origin.concat(a,b)+"\"";
}
}
振る舞いに関するパターン
Chain of Responsibility
- 使用目的
あるオブジェクトグループの中から動的に適切な対象を選択して、
そのオブジェクトに処理を行わせる。
- 実装例
abstract class Processor
{
protected child;
void setChild(Processor processor){ child = processor; }
abstract String process(String str);
}
class ProcessorA extends Processor
{
String process(String str);
{
if(str==null)
return "NULL";
else if(child!=null)
return child.process(str);
else
return null;
}
}
class ProcessorB extends Processor
{
String process(String str);
{
if(str.length>10)
return str.substring(0,5);;
else if(child!=null)
return child.process(str);
else
return null;
}
}
class ProcessorC extends Processor
{
String process(String str);
{
if(str.startWith("a"))
return "abcd";
else if(child!=null)
return child.process(str);
else
return null;
}
}
- 有効性
もともと関連を持たないオブジェクト間ではなく、
GUIコンポーネントなどの階層構造を持つオブジェクト間であれば有効。
Command
- 使用目的
ある条件である処理を行うというものが複数パターンある場合、
長大なifやswitchなどの条件文を列挙する代わりに、
処理自体を内包した、インタフェース/抽象クラスの実装オブジェクトを渡し、
そのオブジェクト内のメソッドを起動することで処理を行う。
- 有効性
いずれのCommand実装クラスを生成するかのタイミングで、
結局同じ問題が発生する可能性がある。
ただし、処理の実施と条件処理を完全に分離させたいときには有効。
Interpreter
- 使用目的
自由度の高い文字列を使用することで、
その文字列を構文解析し、処理内容を多岐に変化させる。
- 有効性
インタプリタの実装に非常に手間がかかる。(JavaCCを使用可能)
HTMLオブジェクトの指定にCSSの記法を持ち込んだ
JavaScriptライブラリのjQueryは非常に効果が高い使用例である。
Iterator
- 使用目的
順次処理のためのオブジェクトリストを保持するクラスを用意することで、
配列インデックスを使用したループ処理が不要になる。
- 有効性
java.util.Iteratorが既に存在するので、
それを使用すればよい。
Mediator
- 使用目的
あるオブジェクトのグループ全体で、相互作用が発生するとき、
それぞれで参照を持ち合い、通知を行うのではなく、
相互作用を統括するクラスを作成して、それを通して行うことで、
オブジェクト間の関係を簡略化する。
- 有効性
Mediatorにオブジェクトを扱わせるために何らかの共通性持たせる必要性がある。
そのため、インタフェースの実装なり抽象クラスの継承なりが必要になるので、
各オブジェクトは根本的に同じ種類のものである場合に有効である。
例えば、UIコンポーネントがそうであり、コンポーネントを載せるパネルがMediatorとなる。
Memento
- 使用目的
オブジェクトの状態を保存しておき、戻せるようにする。
- 有効性
アプリケーションの機能としてアンドゥがある場合のそれの実装としてや、
処理失敗時のロールバック機能の実装としてなどで有効
Observer
- 使用目的
特定オブジェクトの状態変化などのイベント監視を行う
- 有効性
UIのListenerのようにイベント監視に有効
State
- 使用目的
複雑な複数の条件が存在しうるとき、その分岐をifなどで列挙するのではなく、
状態を表すオブジェクト内にカプセル化することで、
記述をシンプルにする。
処理をカプセル化するCommandパターンに状態変化も加えた感じ。
- 有効性
Commandパターンと同様で条件分岐処理を別の場所に移しただけになる可能性がある。
Strategy
- 使用目的
複数の中から動的に選択可能なアルゴリズム実装をオブジェクトとして定義しておき、
実行時に選択・切り替えして使用する。
Template Method
- 使用目的
ある長大な処理の一部のみ複数の実装パターンがある場合、
それを抽象メソッドとして定義し、それをオーバーライドすることで、
複数の処理クラスを定義する。
- 有効性
有効だが、共通部分に修正が入っても全体の整合性が保てるかどうか注意。
Visitor
- 使用目的
多種類のクラスに何らかの共通な処理を加えるとき、
個々のクラスにその記述を定義するのではなく、
その処理をクラスとして実装し、そのインスタンスを
多種類のクラスへ与えることで、実行させる。
- 有効性
他クラス間で共通の処理が行われることは少ないと思われる。