デザインパターン 入門
デザインパターン
デザインパターン 入門
Basic
Iterator : 1つ1つ数え上げる
Adapter : 一皮かぶせて再利用
サブクラス 3. Tempalte Method : 具体的な処理をサブクラスにまかせる 4. Factory Method : インスタンス作成をサブクラスにまかせる
インスタンス生成 5. Singleton : たった1つのインスタンス 6. Prototype : コピーしてインスタンスを作る 7. Builder : 複雑なインスタンスを組み立てる 8. Abstract Factory : 関連する部品を組み合わせて製品を作る
分離 9. Bridge : 機能の階層と実装の階層を分ける 10. Strategy : アルゴリズムをごっそり切り替える
同一視 11. Composite : 容器と中身の同一視 12. Decorator : 飾り枠と中身の同一視
構造間の移動 13. Visitor : 構造を渡り歩きながら仕事をする 14. Chain of Responsibility : 責任のたらい回し
Simplifying 15. Facade : シンプルな窓口 16. Mediator : 相手は相談役1人だけ
状態管理 17. Observer : 状態の変化を通知する 18. Memento : 状態を保存する 19. State : 状態をクラスとして表現する
無駄をなくす 20. Flyweight : 同じものを共有して無駄をなくす 21. Proxy : 必要になってから作る
クラスで表現 22. Command : 命令をクラスにする 23. Interpreter : 文法規則をクラスで表現する
GoFによるデザインパターンの分類
生成に関するパターン
Abstract Factory
Factory Method
Singleton
Builder
Prototype
構造に関するパターン
Adapter
Composite
Facade
Proxy
Bridge
Decorator
Flyweight
振る舞いに関するパターン
Chain of Responsibility
Interpreter
Mediator
Observer
Strategy
Visitor
Command
Iterator
Memento
State
Template Method
設計に向けて
共通性/可変性分析
分析マトリクス
Details
以下、記載のソースコードは増補改訂版 Java言語で学ぶ デザインパターン入門のURLでThe zlib/libpng Licenseとして配布されているものから、コメントアウトを取り除いたものとして利用させていただいています。
1. Iterator
何かがたくさん集まっているときに、それを順番に指し示していき、全体をスキャンしていく処理を行うためのもの
実装と切り離して繰り返しを行える。使用するのはIteratorのhasNextとnextメソッドのみ。
実装
Iteratorの宣言を含む集合体
Iteratorの実装
使用方法
2. Adapter
すでに提供されているものがそのまま使えず、wrappterして必要な形に変換して利用
以下の2種類
継承
委譲
継承
元々定義されている実装
使用したいインターフェイス
ここで以下のような継承したクラスを実装
委譲
printは抽象クラスとして定義しておく。それ以外は基本同じ。PrintBannerクラスからBannerクラスのオブジェクトを生成し、同クラス内の関数を呼び出す。
3. Template Method
スーパークラスで処理の枠組みを定め、サブクラスでその具体的な内容を定義
複数のクラス間でロジックの抽象化
サブクラスをスーパークラスと同一視可能
スーパークラス型の変数に、サブクラスのインスタンスのどれを代入しても正しく動作するようにする
The Liskov Substition Principle(LSP)
抽象化クラス
具象クラス1
具象クラス2
使用方法
4. Factory Method
インスタンスを生成する工場をTemplate Methodパターンで構成
インスタンスの作り方をスーパークラスの側で定める
抽象化した工場
抽象化した製造物
具象化した工場
具象化した製造物
使用方法
抽象化した工場におけるメソッドの実装方法
抽象化メソッド
デフォルトの実装
例外スロー
5. Singleton
システムの中に1個しか存在しないものをプログラムで表現したいとき
以下の場合、getInstance()メソッドを呼び出した際に暮らすが初期化されて、そのときにstaticフィールドの初期化が行われる。
デメリット
ユニットテストがしづらい
複数並列して行われる場合にはシングルトンは使えないかも
6. Prototype
new Something()のようにクラスからインスタンスを作るのではなく、インスタンスを複製して新しいインスタンスを作る例えば、以下のような状況
種類が多すぎてクラスにまとめられない場合
クラスからのインスタンス生成が難しい場合
フレームワークと生成するインスタンスを分けたい場合
複製される対象のクラスはCloneableインターフェイスを実装する必要があります。 このCloneableインターフェイスは、マーカーインターフェイスと呼ばれるもので、メソッドが一つも定義されておらず、cloneによってコピーすることができるという印のみに使用されます。Cloneableインターフェイスを実装しない場合、CloneNotSupportedExceptionの例外が発生します。
Cloneableインターフェイスを継承したインターフェイスを用意。実際には、コピー対象のクラスやそのスーパークラスで直接実装しても問題ありません。
Managerクラスの実装
使用方法
7. Builder
Builderクラスは、インスタンスを作成するためのインターフェイスを定義し、そこからそれを実装した各種具象クラスを用意し、Director役のクラスがそれらの具象クラスのメソッドを組み合わせて、目的のものを作り上げる。
Builderクラス
具象クラスの例
Directorクラスで具象クラスのメソッドを組み合わせて目的のものを作り上げる。
使用方法
8. Abstract Factory
抽象的な工場で、抽象的な部品を組み合わせて抽象的な製品を作る
部分の具体的な実装には着目せず、インターフェースに着目し、そのインターフェイスだけを使って、部品を組み立て、製品にまとめる
工場の呼び出し元では、具体的な部品、製品、工場等は利用せず、
java Main listfactory.ListFactoryコマンドのように呼び出すクラスを引数で与えており、Class.forName(classname).newInstance()のように生成
抽象的な工場
抽象化部品の例
具体的な工場
具体的な部品の例
呼び出し元
クラスの生成方法
new :
Somethine obj = new Something();clone :
obj = (Something)clone();newInstance :
(Something) Class.forName(classname).newInstance();,someobj.getClass().newInstance()
メリット/デメリット
メリット : 具体的な工場を新たに追加するのは簡単
デメリット: 部品を新たに追加するのは困難
9. Bridge
機能のクラス階層と実装のクラス階層をわけることで、それぞれのクラス階層を独立に拡張できる
機能の抽象クラス
機能の具象クラス
実装の抽象クラス
実装の具象クラス
使用方法
10. Strategy
アルゴリズムを切り替え、同じ問題を別の方で解くのを容易にするパターン
委譲というゆるやかな結びつきでアルゴリズムの切り替えが容易
Strategy役の抽象クラス
Strategy役の具象クラス
Strategy役を利用するクラス
11. Composite
複数と単数を同一視して、再帰的な構造を作る
Component役となるEntryという抽象クラスで、FileクラスとDirectoryクラスを同一視する。
単数となるFileクラス
複数となるDirectoryクラス
呼び出し元
12. Decorator
中心となるオブジェクトがあり、それに飾り付けとなる機能を一皮一皮かぶせていって、より目的にあったオブジェクトに仕上げていく
飾り枠と中身を同一しており、透過的なインターフェイスを持つ
中身を変えずに機能追加ができる。
呼び出し元
中心となるオブジェクトの元になる抽象化されたクラス
中心となるオブジェクトの元になる具象化されたクラス
飾り付けの元になる抽象化クラス
飾り付けの元になる具象化クラス
13. Visitor
データ構造と処理を分離
データ構造の中を巡り歩く主体である訪問者を表すクラスを用意し、そのクラスに処理を任せる
ConcreateElement役とConcreteVisitor役の組によって実際の処理が決定するというダブルディスパッチ。それぞれのクラスで互いが互いを呼び出している。
抽象化されたVisitor役
具象化されたVisitor役
データ構造側のもので、キーとなるインターフェイスを定義
抽象クラスで実装を宣言
ただし実際には具象クラスで定義
呼び出し元
OCP : Open-Closed Principle
クラスなどが以下の原則に従うべき
拡張については開かれているが、
修正については閉じられている
既存のクラスを修正せずに拡張できるようにせよ
14. Chain of Responsibility
複数のオブジェクトをチェーンのようにつないでおき、そのオブジェクトの鎖を順次渡り歩いて、目的のオブジェクトを決定する方法
要求する側と処理する側の結びつきを弱めることができ、それぞれを部品として独立させることができる
状況によって要求を処理するオブジェクトが変化するようなプログラムにも対応することができる
使用方法
抽象化クラス。support()中のnext.support(trouble);で次に人に処理を渡す。
15. Facade
関係し合っているたくさんのクラスを適切に制御するために、それらのクラスを個別に制御しなくても、窓口に対して要求を出すだけですむように新たにシンプルなインターフェースを用意する
システムの内側にある各クラスの役割や依存関係を考えて、正しい順番でクラスを利用
インターフェースを少なくする
Facade役の実装
個々の実装1
個々の実装2
利用方法
16. Mediator
各メンバーはみんな相談役だけに報告し、メンバーへの支持は相談役だけから来るようにする
多数のオブジェクト間の調整を行わなければならないときこそ、Mediatorパターンの出番
ConcreteColleague役は再利用しやすいが、ConcreteMediator役は再利用しにくい
呼び出し元
抽象化されたMediatorクラス
具象化したMediatorクラス
抽象化したColleagueクラス
具象化したColleaqueクラス
17. Observer
観察対象の状態が変化すると、観察者に対して通知。状態変化に応じた処理を記述するときに有効
Observer役の処理がSubject役の処理に影響を与えるとき、無限連鎖に注意して設計。Observer役に、現在Subject役から通知されている最中かどうかを表すフラグ変数を一つ持たせるのがよい
抽象化したObserver役のクラス
具象化したObserver役のクラス
抽象化したSubject役のクラス
具象化したSubject役のクラス
呼び出し元
18. Memento
ある時点でのインスタンスの状態を記録して保存しておき、あとでインスタンスをその時点の状態に戻す
Caretaker役はOriginator役にお願いして、現在の状態を表現するMemento役を作ってもらう
Caretaker役はMemento役の内部情報は知らないし、気にしない。将来の復元に備えて、大事にMemento役を保存しておく。必要なときに引き出しの奥からMemento役を取り出して、Originator役にわたすと、きちんと復元される
Caretaker役とOriginator役
Caretaker役 : どのタイミングでスナップショットを撮るかを決め、いつUndoするかを決め、Memnto役を保持
Originator役: Memento役を作る仕事と、与えられたMemento役を使って自分の状態を元に戻す仕事
Caretaker役で現在のOriginator役の状態を保存したいときに、そのことをOriginator役に伝える
Originator役で、Memento役を作ったり、リストアしたりする。
Memento役で、Originator役の内部情報をまとめるクラス
Memento役
wide interface
オブジェクトの状態をもとに戻すために必要な情報がすべて得られるメソッドの集合
これを使えるのはOriginator役のみ
narrow interface
外部のCaretaker役に見せるもの
内部状態が外部に公開されるのを防ぐ
19. State
状態をクラスで表現。個々の具体的な状態を別々のクラスとして表現して問題を分割
新しい状態を追加するのが簡単
状態遷移を状態に依存した振る舞いとして見なし、個々の具体的な状態のクラスのメソッド中で他のクラスを呼び出すメリット/デメリット
他の状態に遷移するのはいつかという情報が、1つのクラス内にまとまっている
1つのConcreateState役が他のConcreate役を知らなければならない
抽象化したState役のクラス
具象化したState役のクラス
抽象化したContext役のクラス。現在の状態を表すConcreteState役を持つ。
具象化したContext役のクラス
20. Flyweight
インスタンスをできるだけ共有させて、無駄にnewしない
管理されているインスタンスはガベージ対象になれない点について注意
Flyweight役。普通に扱うとプログラムが重くなるので共有した方がよいもの。
FlyweightFactoryの役。Flyweight役を作る工場。
Clientの役。FlyweightFactory役を使ってFlyweight役を作り出し、それを利用する役。
21. Proxy
実際に必要な段階になってはじめて、対象のクラスを生成する。
Proxy役とRealSubject役を同一視するためのインターフェースを定義
RealSubject役。重い処理の想定。
Proxy役はClient役からの要求をできるだけ処理。もし、自分だけで処理できず、必要になってからはじめてProxy役はRealSubject役に仕事を任せる。
Client役。Proxyパターンを利用
22. Command
命令をオブジェクトとして表現することで、履歴を取ったり再実行を行ったりすることができるようになる
抽象化されたCommand役
具象化されたCommand役
命令の受け取り手となるReceiver役
上記インターフェイス
ConcreteCommand役を生成し、その際にReceiver役を割り当てるClient役
命令の実行を開始するInvoker役。Command役で定義されているインターフェースを呼び出す役
Mainクラス(ConcreteCommand役を生成し、その際にReceiver役を割り当てるClient役も兼ねる)
DrawCanvasクラス(命令の受け取り手となるReceiver役も兼ねる)
23. Interpreter
プログラムが解決しようとしている問題を簡単なミニ言語で表現
AbstrtactExpressionの役。構文木のノードに共通のインタフェースを定義する役。
TerminalExpressionの役。BNFのターミナル・エクスプレッションに対応する役。
NonterminalExpressionの役。BNFのノンターミナル・エクスプレッションに対応する役。以下が例。他も例多数。
Contextの役。インタプリタが構文解析を行うための情報を提供する役。
Client役。構文木を組み立てるために、TerminalExpression役やNonterminalExpression役を呼び出す役。
マルチスレッド
マルチスレッド
Overview
Single Threaded Execution - この橋を渡れるのは、たった一人
Immutable - 壊したくとも、壊せない
Guarded Suspension - 用意できるまで、待っててね
Balking - 必要なかったら、やめちゃおう
Producer-Consumer - わたしが作り、あなたが使う
Read-Write Lock - みんなで読むのはいいけれど、読んでる間は書いちゃだめ
Thread-Per-Message - この仕事、やっといてね
Worker Thread - 仕事が来るまで待ち、仕事が来たら働く
Future - 引換券を、お先にどうぞ
Two-Phase Termination - 後片付けしてから、おやすみなさい
Thread-Specific Storage - スレッドごとのコインロッカー
Active Object - 非同期メッセージを受け取る、能動的なオブジェクト
詳細
Single Threaded Execution - この橋を渡れるのは、たった一人
synchronizedで同時に1スレッドからしか実行できないようにする
アトミックな操作
基本型・参照型の代入・参照はアトミックな操作
ただし、long,double型の代入・参照はアトミックな操作ではない
long,doubleをスレッド間で共有する場合、synchroizedの中に入れるかvolatileとして宣言
ある領域を一つのスレッドだけに限定していたところをN個までに広げたものを計数セマフォ
Immutable - 壊したくとも、壊せない
set系メソッドのないクラス
以下のような工夫で子のクラスから変更されることを防止
finalとして宣言
フィールドはprivate
Guarded Suspension - 用意できるまで、待っててね
自分の状態が適切な状態のときだけ、目的の処理をスレッドに実行させる
オブジェクトが適切な状態であることをガード条件で表現
呼び方
guarded suspension
guarded wait
条件のテストにwhileを使い、待つためにwaitメソッドを使用。条件が変化したら、notify/notifyAllメソッドを使用
busy wait
スレッドがwaitで待つのではなく、yieldしながら条件をテストするような実装方法
spin lock
条件が成り立つまでwhileループで回って待つ様子を表現
polling
あるイベントが起こるのを繰り返し調べに行き、起こったらそれを調査する方法
Balking - 必要なかったら、やめちゃおう
オブジェクトが適切な状態であることをガード条件で表現
ガード条件が満たされているときに限り、実行を継続。ガード条件が満たされていなければ、処理を実行せずにすぐにメソッドから帰る。
タイムアウト
synchronizedブロック
タイムアウトもinterruptもできない
waitを実行してウェイトウェットの中にある状態
guarded timed
BalkingとGuarded Suspensionの中間でガード条件を満たすまで一定時間待つ
スレッドがオブジェクトのウェイトセットに入って停止した状態から抜け出す
方法
notify : インスタンスに対して実行
notifyAll : インスタンスに対して実行
interrupt : スレッドに対して実行
wait(タイムアウト値)
notify/notifyAllとタイムアウトの場合を識別する方法はない。interruptの場合はInterruptedExceptionの例外が投げられる
詳細
waitの場合でもsleepの場合でもjoinの場合でも同様にinterruptで中断できる
throws InterruptedExceptionがついている
時間はかかるけどしれないがキャンセルできる
notify/notifyAll
java.lang.Objectクラスのメソッドで、そのインスタンスのウェイトセットの中にあるスレッドを起こす
スレッドを直接指定するものではなくインスタンスのロックを撮る必要がある
notifyについてウェイトセットの中にあるスレッドのどれを選択するかは仕様で定められておらず、Javaの処理系に依存
interrupt
インタラプト状態にするメソッド
InterruptedExceptionが指定されているメソッドは時間がかかるかもしれないが処理をキャンセルできる
java.lang.Threadクラスのメソッドでスレッドを直接指定して呼び出すもの
interrupted
インタラプト状態のテストとクリアを行うメソッド
stop
Threadクラスのstopメソッドはdeperecated
安全性を壊す可能性があるため使ってはいけない
スレッドがクリティカルセクションを実行している途中であってもいきなり終了させることができる
java.util.concurrent
例外でタイムアウト
Futureインターフェイスのgetメソッド
Exchangerインターフェイスのexchangeメソッド
CyclicBaririerインターフェイスのawaitメソッド
CountDownLatchインターフェイスのwaitメソッド
戻り値でタイムアウト
BlockingQueue
Semaphore
locks.Locks
Producer-Consumer - わたしが作り、あなたが使う
Data役を作るProcuder役のスレッドから、Data役の中継地点、橋渡し、通信路の働きをするChannel役に渡したいData役を保持させて、Data役を使うConsumer役のスレッドにデータを渡す
Channel役
種類
キュー
スタック
優先度付きキュー
java.util.concurrent
BlockingQueue
ArrayBlockingQueue
ConcurrentLinkedQueue
Exchanger
Read-Write Lock - みんなで読むのはいいけれど、読んでる間は書いちゃだめ
readとwriteという2つの処理を提供するSharedResource役がいる。readはSharedResource役の状態を変更しない処理でwriteは変える処理
read中に他のreadに対するロックを他の組み合わせと同様にロックするとリソースがもったいなく排他制御が不要
そのため、read用のロックとwrite用のロックを別々に提供するReadWriteLock役を導入
Thread-Per-Message - この仕事、やっといてね
Client役はHost役のrequestメソッドを呼び出して要求を出す。その要求を実際に処理するためのHelper役のhandleメソッドがある。このとき要求を処理するために、新しいスレッドをHost役の中で起動して、新しいスレッドがhandleの呼び出しを行うことで、非同期のメッセージ送信を実現
応答性を上げ、遅延時間を下げる
処理の順序が問題にならず、戻り値が不要な場合に使用する
Worker Thread - 仕事が来るまで待ち、仕事が来たら働く
Thread-Per-Messageのようにリクエストのたびに新しいスレッドを起動するのは無駄なので、仕事を実行するスレッドを予め起動しておき、Producer-Consumerを使って、仕事の内容を表すインスタンスをワーカースレッドにわたす
Future - 引換券を、お先にどうぞ
Thread-Per-Messageでは処理を任せた時点では結果を得られなかったが、処理の結果を表すRealData役と同じインターフェイスAPIを持つFuture役を作る
処理の開始時点ではFuture役を戻り値として、別のスレッドの処理が完了したら、その結果をFuture役にセット。Client役はFuture役を使って処理の結果を得る
Two-Phase Termination - 後片付けしてから、おやすみなさい !
概要
動作しているスレッドを終了させ、終了する前には、特定の終了処理を行う場合、オブジェクトの安全性を失わせないために終了要求を表すメソッドを用意
そのメソッドの中で終了要求がきたというフラグを立てる。スレッドは終了処理をはじめても良い安全なポイントでそのフラグをテスト。終了処理への移行はあくまで終了するスレッド自身の判断で行う。
Javaではwait,sleep,joinの各メソッドで待っているスレッドを中断させるため、interruptメソッドを使用。Thread.interruptedメソッドを呼び出すとインタラプト状態がクリアされるので、終了要求のテストには注意
実行時に例外がおきたときでも確実に終了処理を行わせるにはfinallyを使用する
スレッドの終了方法 !!!
非推奨
java.lang.Threadのstopメソッドは、スレッドを強制終了するが安全性が保証されずdeprecated
java.lang.ThreadDeathの例外を投げる
推奨
shutdownRequestメソッドでshutdownRequestedフラグ判定だけを入れつつ、interruptメソッドも使用
理由
shutdownRequestメソッドでshutdownRequestedフラグ判定だけではなくinterruptメソッドを使用しているのは、スレッドがsleepしているかもしれないから
逆にinterrupstメソッドのみでshutdownRequestedフラグ判定を入れないと、一箇所でもInterruptedExceptionの例外をcatchしたところで何も処理しないと、インタラプト状態ではなくなり要求がなかったことになる。フラグは終了要求が出されたことを記録しておくためのもの
Thread-Specific Storage - スレッドごとのコインロッカー
対象となるオブジェクトをTSObject役とし、TSObject役と同じインタフェース(API)を持つTSObjectProxy役を作る。Client役→TSObject役の対応表を管理するためにTSObjectCollection役を用意。TSObjectProxy役はTSObjectCollection役を使って、現在のスレッドに対応するTSObjectを取得。そして、そのTSOBject役に処理を委譲
joinメソッドとisAliveメソッド
指定したスレッドの終了を待つにはjava.lang.Threadのjoinメソッド
指定したスレッドが現在終了しているかどうかはjava.lang.ThreadのisAliveメソッド
指定したスレッドの状態を取得するにはjava.lang.ThreadのgetStateメソッド
java.util.concurrent.ExecutorService
shutdown : 動作しているスレッドをきちんと終了
isShutdownメソッド : shutdownメソッドが呼ばれたか調べる
isTerminatedメソッド : スレッドが実際に止まっているかどうか調べるメソッド
Active Object - 非同期メッセージを受け取る、能動的なオブジェクト
処理を依頼する人(Client役)と、処理を実行する人(Servant役)がいる状況について
Servant役が処理を実行するのに時間がかかったり、処理を実行できるタイミングが遅くなったりしてもClient役に影響を与えたくない場合
Client役からServant役への一方向の呼び出しだけではなく、実行結果をServant役からClient役へ返すことができるように双方向の呼び出しにしたい場合
処理の依頼順序と処理の実行順序を独立にしたい場合
処理
Client役からの依頼はProxy役へのメソッド呼び出しとして実現。Proxy役はその依頼をConcreteMethodRequest役という1つのオブジェクトに変換し、Scheduler役を経由して、ActivationQueue役に保存
ActivationQueue役から次に実行すべきConcreteMethodRequest役を選び出して実行するのは、Scheduler役の仕事。Scheduler役はClient役とは別のスレッドで動作し、ConcreteMethodRequest役経由で、Servant役に処理を委譲
volatile
同期化 : あるスレッドがvolatileフィールドに行った書き込みは他のスレッドからすぐに見える
longとdoubleのアトミックな扱い
複数のスレッドで共有されるフィールドは、synchronizedまたはvolatileで守る
Last updated