Javaのデザインパターンを整理(振る舞い編)
Javaのデザインパターンについて復習します。プログラムをデザインするにあたって、参考にできる引き出しは多く持っておきたいです。
デザインパターンとは
過去のソフトウェア設計者が発見し編み出した設計ノウハウを蓄積し、名前をつけ、再利用しやすいように特定の規約に従ってカタログ化したもの
有名なのはGoFの23のデザインパターンです。それぞれのデザインパターンは3つの分類に分かれています。
- オブジェクトの「生成」に関するパターン
- プログラムの「構造」に関するパターン
- オブジェクトの「振る舞い」に関するパターン
振る舞いに関するパターン
パターン名 | 概要 |
---|---|
Chain of Responsibility | イベントの送受信を行う複数のオブジェクトを鎖状につなぎ、それらの間をイベントが渡されてゆくようにする。 |
Command | 複数の異なる操作について、それぞれに対応するオブジェクトを用意し、オブジェクトを切り替えることで、操作の切替えを実現する。 |
Interpreter | 構文解析のために、文法規則を反映するクラス構造を作る。 |
Iterator | 複数の要素を内包するオブジェクトのすべての要素に対して、順番にアクセスする方法を提供する。反復子。 |
Mediator | オブジェクト間の相互作用を仲介するオブジェクトを定義し、オブジェクト間の結合度を低くする。 |
Memento | データ構造に対する一連の操作のそれぞれを記録しておき、以前の状態の復帰または操作の再現が行えるようにする。 |
Observer (出版-購読型モデル) | インスタンスの変化を他のインスタンスから監視できるようにする。Listenerとも呼ばれる。 |
State | オブジェクトの状態を変化させることで、処理内容を変えられるようにする。 |
Strategy | データ構造に対して適用する一連のアルゴリズムをカプセル化し、アルゴリズムの切替えを容易にする。 |
Template Method | あるアルゴリズムの途中経過で必要な処理を抽象メソッドに委ね、その実装を変えることで処理が変えられるようにする。 |
Visitor | データ構造を保持するクラスと、それに対して処理を行うクラスを分離する。 |
Commandパターン
- 概要
- 「命令」をインスタンスとして扱うことにより、処理の組み合わせや切り替えなどを容易にするパターン。
- 何が嬉しいか
- 処理が多岐に渡る場合や組み合わせて処理を行う場合に、コードの見通しが良くなります。
クラス図
実装例
SQLを実行するモードがいくつかあり、いろいろなモードで任意のSQLを実行できるようなコマンドを実行するパターン。Clientからは宣言的に実行したいモードで実行できるのが嬉しいです。if文で分岐し始めると大変です。
Command
public abstract class Command { protected String sql; public void setSql(String sql) { this.sql = sql; } public abstract void execute(); }
ConcreteCommand
public class GetParamCommand extends Command { @Override public void execute() { // DBへのConnectionなど // SQLの実行結果をパラメータとして取得するなど } } public class UpdateDataCommand extends Command { @Override public void execute() { // DBへのConnectionなど // Insertなどの実行 } }
Client
public class Main { public static void main(String[] args) { String getParamSql = "select * from ..."; String updateDataSql = "insert into ..."; Command updateDataCommand = new UpdateDataCommand(); Command getParamCommand = new GetParamCommand(); updateDataCommand.setSql(updateDataSql); updateDataCommand.execute(); getParamCommand.setSql(getParamSql); getParamCommand.execute(); } }
Strategyパターン
- 概要
- 処理アルゴリズムを容易に切り替えられるようにするパターン
- Commandパターンと似ている
- 何が嬉しいか
- 処理が多岐に渡る場合や組み合わせて処理を行う場合に、コードの見通しが良くなります。
クラス図
実装例
Strategy
public interface Strategy { public void process(String sql); }
ConcreteStrategy
public class GetParamStrategy implements Strategy { @Override public void process(String sql) { // SQLの実行結果をパラメータとして取得するなど } }
ConcreteStrategy
public class UpdateDataStrategy implements Strategy { @Override public void process(String sql) { // Insertなどの実行など } }
ConcreteStrategy
public class DefaultStrategy implements Strategy { @Override public void process(String sql) { // デフォルトの処理 } }
Context
public class Context { private Strategy strategy; public Context() { this.strategy = new DefaultStrategy(); } public void setStrategy(Strategy strategy) { this.strategy = strategy; } public void execute(String sql) { this.strategy.process(sql); } }
Client
public class Main { public static void main(String[] args) { String getParamSql = "select * from ..."; String updateDataSql = "insert into ..."; Strategy getParamStrategy = new GetParamStrategy(); Strategy updateDataStrategy = new UpdateDataStrategy(); Context context = new Context(); context.setStrategy(getParamStrategy); context.execute(getParamSql); context.setStrategy(updateDataStrategy); context.execute(updateDataSql); } }
ライブラリでの利用
Observerパターン
- 概要
- プログラム内のオブジェクトのイベントを他のオブジェクトへ通知する処理で使われるデザインパターン
- 状態が変化する側が通知する仕組みを持ちます
- 何が嬉しいか
- 状態を通知する際に、通知側と受信側のクラスを疎結合にすることができます
- ObserverインターフェースとSubject抽象クラスがゆるく結合しているために、具象クラスの実装を知る必要がありません
クラス図
実装例
Observer
public interface Observer { void update(Subject subject); }
Subject
public abstract class Subject { private List<Observer> observers = new ArrayList<>(); public void addObserver(Observer observer) { observers.add(observer); } public void notifyObservers() { for (Observer observer : observers) { observer.update(this); } } public abstract void execute(); }
ConcereteObserver
public class Client implements Observer { @Override public void update(Subject subject) { System.out.println("通知を受信しました"); } } public class ClientEn implements Observer { @Override public void update(Subject subject) { System.out.println("Accepted notification."); } }
ConcreteSubject
public class DataChanger extends Subject { private int status; @Override public void execute() { status++; System.out.printf("ステータスが%sに変わりました。\n", String.valueOf(status)); notifyObservers(); } }
テスト
public class Main { public static void main(String[] args) { Observer observer = new Client(); Observer observer2 = new ClientEn(); Subject dataChanger = new DataChanger(); dataChanger.addObserver(observer); dataChanger.addObserver(observer2); for (int count = 0; count < 10; count++) { dataChanger.execute(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
結果
ステータスが1に変わりました。 通知を受信しました Accepted notification. ステータスが2に変わりました。 通知を受信しました Accepted notification. ステータスが3に変わりました。 通知を受信しました Accepted notification. ...
ライブラリでの利用
参考
Java本格入門 ~モダンスタイルによる基礎からオブジェクト指向・実用ライブラリまで
- 作者: 谷本心,阪本雄一郎,岡田拓也,秋葉誠,村田賢一郎,アクロクエストテクノロジー株式会社
- 出版社/メーカー: 技術評論社
- 発売日: 2017/04/18
- メディア: 大型本
- この商品を含むブログを見る