[保存版]人間が読んで理解できるデザインパターン解説#3: 振舞い系(翻訳)

こんにちは、hachi8833です。デザインパターン解説シリーズの最終回です(全3回)。 #1 作成系デザインパターン #2 構造系デザインパターン #3 振舞い系デザインパターン(本記事) 概要 原著者の許諾を得て、MITライセンスに基づき翻訳・公開いたします。 英語記事: Design Patterns for Humans™ – An ultra-simplified explanation 更新日: 2017/09/25 著者: Kamran Ahmed サイト: Hugobots — 開発者向けのニュースレターを発行しています。 「Design Patterns for Humans」は商標(TM)です。 人間が読んで理解できるデザインパターン解説#3: 振舞い系(翻訳) 🎉 究極にシンプルなデザインパターン解説! 🎉 誰もがつい心躍ってしまうタイトルにしてみました。タイトルに負けないよう、本記事ではこれ以上不可能なまでにシンプルな方法にこだわってデザインパターンを解説しています。 本ガイドを気に入っていただいた方はコーヒー一杯おごってくださいまし。他の記事も読んでみたい方は、ぜひHugobotsのご購読をお願いいたします。 🚀 はじめに デザインパターンとは、常に繰り返される問題を解決するためのものであり「特定の問題に取り組む方法のガイドライン」です。デザインパターンはクラスではありませんし、アプリに追加するだけで奇跡を起こしてくれるようなパッケージやライブラリでもありません。デザインパターンはそうしたものではなく、特定の状況で特定の問題に取り組む方法を示すガイドラインであるとご理解ください。 デザインパターンとは、常に繰り返される問題を解決するためのものであり「特定の問題に取り組む方法のガイドライン」です。 Wikipediaには次のように書かれています。 ソフトウェアエンジニアリングにおけるデザインパターンとは、ソフトウェア設計上の特定コンテキストにおいてよく発生する問題に対する「一般的かつ再利用可能な問題解決法」である。デザインパターンは、ソースコードやマシンコードに直接置換えられるような最終設計ではない。デザインパターンは、さまざまな状況に適用可能な問題解決法の記述、またはテンプレートである。 ⚠️ ご注意 デザインパターンは、あらゆる問題を解決する「銀の弾丸」ではありません。 デザインパターンを強制してはなりません。無理にデザインパターンを適用すれば良くない結果が生じることでしょう。デザインパターンは、問題が起こってからそれを解決するものであって、問題をあら捜しして解決するものではありません。デザインパターンに過剰な期待を持たないことです。 適切な場所で適切に用いられたデザインパターンは、救いの神になるでしょう。そうでないデザインパターンは荒れ狂い、コードをめちゃめちゃにしてしまうことでしょう。 追伸: 以下のコードサンプルではPHP-7を使っていますが、コンセプトはどの言語でも同じなので、どうかページを閉じないでください。ついでながら、他の言語のサポートについては現在作業中です。 デザインパターンの種別 作成系 構造系 振舞い系 — 本記事 #3 振舞い系デザインパターン わかりやすくまとめるとこうです。 オブジェクト間での責務の割り当てに関連します。単に構造を指定するのではなく、メッセージ受け渡しや通信パターンの大枠を定める点が構造系 と異なります。言い換えると、「ある振舞いをソフトウェアコンポーネントでどのように実行するか」という問いに答える手助けをします。 Wikipediaではこうです。 ソフトウェアエンジニアリングにおける振舞い系デザインパターンとは、オブジェクト間で共通の通信パターンを指定することでパターンを実現するものである。これを実現すると、パターンによって通信実行時の柔軟性が高まる。 Chain of Responsibilityパターン Commandパターン Iteratorパターン Mediatorパターン Mementoパターン Observerパターン Visitorパターン Strategyパターン Stateパターン Template Methodパターン 🔗 Chain of Responsibilityパターン(link) 現実世界になぞらえるとこうです。 たとえば、3種類の異なる支払い方法(A、B、C)が設定された銀行口座を持っているとします。それぞれの(最大)支払額は異なっており、Aは100米ドル、Bは300米ドル、Cは1000米ドルです。支払い設定では「A、次にB、次にC」のように選択されます。そして210米ドルで何かを購入したとします。Chain of Responsibilityパターンを使って、最初にAで支払い可能かどうかをチェックします。支払い可能であればそのまま購入を行い、チェイン(連鎖)は終了します。支払い可能でない場合、リクエストは次のBに進んで支払額をチェックします。支払い可能な場合はチェインは終了し、支払い可能でない場合はリクエストを次に進め、適切なハンドラが見つかるまでこれを繰り返します。ここでいうA、B、Cはチェインのリンクであり、この動作全体がChain of Responsibilityパターンになります。 わかりやすくまとめるとこうです。 Chain of Responsibilityパターンは、オブジェクトのチェイン形成を支援します。リクエストはチェインの一方から他方へ、あるオブジェクトから別のオブジェクトへと進み、適切なハンドラが見つかるまでこれを繰り返します。 Wikipediaではこうです。 オブジェクト指向設計におけるChain of Responsibilityパターンは、コマンドオブジェクトの提供元と、一連の処理オブジェクトで構成される。各処理オブジェクトには、オブジェクトが扱えるコマンドオブジェクトの種類を定義するロジックが含まれ、それ以外のコマンドオブジェクトは、チェイン上の次の処理オブジェクトに渡される。 プログラム例 先の銀行口座の例を使います。最初に基本となる口座を記述し、口座と他の口座をチェインするロジックを記述します。 abstract class Account { protected $successor; protected $balance; public function setNext(Account $account) { $this->successor = $account; } public function pay(float $amountToPay) { if ($this->canPay($amountToPay)) { echo sprintf(‘%sで%sドル支払われました。’ . PHP_EOL, get_called_class(), $amountToPay); } elseif ($this->successor) { echo sprintf(‘%sで支払いできません。次の支払い方法に進みます。’ . PHP_EOL, get_called_class()); $this->successor->pay($amountToPay); } else { throw new Exception(‘残高が十分なアカウントがありません’); } } public function canPay($amount): bool { return $this->balance >= $amount; } } class Bank extends Account { protected $balance; public function __construct(float $balance) { $this->balance = $balance; } } class Paypal extends Account { protected … Continue reading [保存版]人間が読んで理解できるデザインパターン解説#3: 振舞い系(翻訳)