これは何
今まで断片的に知っていたデザインパターンを改めて勉強し直したのでそのまとめです。
23個のデザインパターン
1. 生成に関するパターン
-
Abstract Factory
抽象的な部品から抽象的な製品を作成する、抽象的な工場のパターン。
ある製品の製造過程を抽象化するイメージ。
あらかじめ、工場、部品、完成品に対応する抽象クラスを作成しておき、
それらすべての抽象クラスを継承した工場、部品を使って具体的な完成品を作成する。 -
Builder
インスタンスの初期化をカプセル化するパターン。
Builderインスタンスは初期化したいインスタンスをフィールドに持ち、
外部からBuilderインスタンスのメソッド経由でインスタンスの初期化を行う。
比較的よく目にするパターン。 -
Factory Method
インスタンスの生成が外部の状態に依存していたり、
生成が外部の状態を変化させる時に便利なパターン。
Factoryクラス内で状態を管理しておき、
Factory Method経由でインスタンスを生成するようにする。 -
Prototype
インスタンスをコピーして新しいインスタンスを作成するパターン。
一つのクラスから多様なインスタンスを繰り返し生成する時や、
インスタンスの生成をプログラム的に行うのが困難な時に利用できる。 -
Singleton
プログラム全体を通じて、インスタンスが一つしか生成されないように制限するパターン。
よく目にする。
2. 構造に関するパターン
-
Adapter
データを供給するクラスと、消費するクラスの橋渡しをするパターン。
Adapterパターンはさらに、継承のパターンと委譲のパターンの二つに分けられる。
継承のパターンではAdapterクラスがデータ供給クラスと消費側に向けたのインターフェースの二つを継承する。
委譲のパターンでは、Adapterクラスは消費側に向けたインターフェースを継承し、データ供給インスタンスをメンバ変数として保持。データの取得は委譲を利用して行うというパターン。
AndroidでいうとRecyclerViewのAdapterは譲渡のAdapterパターンといえる。 -
Bridge
クラスの継承ツリーを、機能の分岐先と実装の分岐先に分けるパターン。
あるクラスから抽象化したい処理を別の新しいクラスに抽出、委譲することで、
機能を追加したいときは元のクラスを継承、抽象化された処理を実装したい時は、
別の新しいクラスを継承することで、継承ツリー内で機能の階層と実装の階層を
分けることができる。 -
Composite
容器と中身を同一視するパターン。
主にツリー構造のデータに対して、中間のノードと葉のノードを区別せずに扱いたい時に利用する。
AndroidでいうとViewとViewGroupがこのパターン。
ViewGroupはViewを継承しているが、Viewのリストを保持している。 -
Decorator
中身と装飾を同一視するパターン。
あるクラスに対して、再帰的に機能を追加でき、かつ機能を追加しても同じインターフェース越しに利用できるようなパターン。
java.io.*ライブラリがこのパターンを利用しているらしい。
FileReaderやInputStreamReader、BufferedReaderなどを入れ子状にして機能を追加していくことができる?。(あまりちゃんと確認していない。) -
Facade
複数のクラス間の連携をまとめて、シンプルなインターフェースを提供するパターン。
特に意識しなくても、複数のクラス間の連携が複雑になっていけば自然とまとめるようにらると思う。 -
Flyweight
インスタンスをプールして使いまわしてメモリを節約するパターン。 -
Proxy
Proxyクラス越しにインスタンスを操作することで、制限をかけたり実際の複雑な処理をブラックボックス化したりするパターン。
Androidではbinderという分散オブジェクトの仕組みを利用して、別プロセスのインスタンスのメソッドを呼び出したりするが、それがProxyパターンになっているらしい。
3. 振る舞いに関するパターン
-
Chain of Responsibility
問題をたらい回しにして、処理できる人に処理させるパターン。
あまり使わなさそう。 -
Command
操作をオブジェクトにすることで、UndoやRedoなどの処理を行えるようにするパターン。
各Commandオブジェクトはexecuteメソッドを持ち、これらをMacroCommandオブジェクトが取りまとめる。 -
Interpreter
構文解析木を作成するパターン。
各Nodeオブジェクトはparseメソッドを持ち、contextクラスからトークンを一つ一つ取得しながら構文解析木を作成する。 -
Iterator
リストに対してループ処理を行えるようにするパターン。
hasNextとnextメソッドを実装したIteratorクラスから、要素を一つ一つ取り出していく。 -
Mediator
複数のクラス間の連携を取りまとめるパターン。
Facadeが内部の複数のクラスを一方的に利用するのに対し、
Mediatorでは、Mediatorクラスと内部のクラスが双方向に連携する。
Androidだと、TabLayoutとViewPager2を連携するTabLayoutMediatorがこのパターンに相当する。 -
Memento
インスタンスの状態を保存して、再生成するパターン。
Androidだと、BundleとかParcelableとかがこのパターンに相当する。 -
Observer
リスナーを登録して、イベントの通知を監視するパターン。
Androidだと、LiveDataがこのパターンに相当する。 -
State
ロジックが複数の状態によって切り替わるときに、状態に相当するクラスを作成し、
そのクラスの中にロジックを閉じ込めるパターン。
stateインスタンスを差し替えるだけで、ロジックの切り替えができるので、
if文が少なくなり、新しい状態が発生しても対応するクラスを作成するだけで対応できる。 -
Strategy
ロジックを担当するクラスに特定の処理を委譲するパターン。
委譲に関する一番ベーシックなパターンのような気がする。
StateもObserverもBridgeもこのパターンの一種なような気がする。 -
Template Method
特定の処理を抽象化して、継承する子クラスに実装を任せるパターン。
継承に関する一般的なパターンのような気がする。
abstract classは須らくこのパターンに当てはまると思う。 -
Visitor
Visitorクラスがデーター構造(大抵の場合はツリー構造)の各ノードを渡り歩くパターン。
VisitorクラスはそれぞれのNodeクラスに対する処理を記述したメソッドを持つ。
各ノードはacceptメソッドを実装し、引数としてVisitorクラスを受け取る。
Nodeクラスはacceptメソッド内部で、自分に対応するVisitorクラスのメソッドを自分を引数として呼び出す。
まとめ
デザインパターンと言いつつも、StrategyやTmeplate Methodなど普段からあまり意識せずに使っているパターンもありました。
BridgeやStateパターンは、結構いろいろな場面で利用でき、可読性やメンテナンス性も高まるので普段から意識して使うようにしていきたいです。