『Head Firstデザインパターン』 Decoratorパターン

Decoratorパターンとは

Decoratorパターンはオブジェクトに付加的な責務を動的に付与する。デコレータはサブクラス化の代替となる、柔軟な機能拡張手段を提供する。

スターバズコーヒー

継承を使って飲み物を増やすのはクラス爆発や硬直した設計になる。

継承は強力だが、必ずしも柔軟な設計や保守性のある設計につながるわけではない。
継承をしないで再利用を実現するにはコンポジションと委譲を使う。
サブクラス化で振る舞いを継承すると、すべてのサブクラスが同じ振る舞いを継承しなければいけない。
しかし、コンポジションを使ってオブジェクトの振る舞いを拡張すると、振る舞いの拡張を動的に行うことができる。既存コードを変更するのではなく新しいコードを記述することで新しい機能を追加できる。

開放・閉鎖原則

クラスは拡張に対しては開かれていて、変更に対しては閉じた状態であるべきである。

既存のコードを変更せずに、クラスを簡単に拡張して新しい振る舞いを組み込めるようにすること。
既存コードを変更することなく、拡張に対して開いた設計にするには時間と努力が必要。
開放・閉鎖原則に従うと新しいレベルの抽象化が導入され、それによってコードが複雑になる。そのため、設計の中で最も変化する可能性が高い部分に注目し、この原則を適用する。 すべての部分に開放・閉鎖原則を適用することは無駄であり、結果的に複雑で理解しにくいコードになる可能性がある。

デコレータを使って飲み物の注文を構築する

デコレータはラップするオブジェクトに振る舞いを追加する。Beverageクラスのコンポーネントをデコレータでラップすることで機能を追加する。

<?php

$espresso = new Espresso();
// エスプレッソ $ 1.99
print_r("{$espresso->getDescription()} $ {$espresso->cost()}\n");

// エスプレッソをインスタンス化し、それをトッピングでラップする
$house_blend = new Mocha(new HouseBlend());
// ハウスブレンドコーヒー, モカ $ 1.09
print_r("{$house_blend->getDescription()} $ {$house_blend->cost()}\n");

$house_blend_whip = new Whip(new Mocha(new HouseBlend()));
// ハウスブレンドコーヒー, モカ, ホイップ $ 1.59
print_r("{$house_blend_whip->getDescription()} $ {$house_blend_whip->cost()}\n");

Decoratorパターンのデメリット

JavaのIOはデコレータパターン

"利点は、ニーズに合わせて1つまたは複数のさまざまなデコレータを使用してストリームをデコレートできる自由があることです。... ストリームを閉じようとしているときは、一番外側のデコレータを閉じるだけで十分です。クローズコールを一番下まで委譲します。"
https://codeday.me/jp/qa/20181225/84820.html

設計に柔軟性を付加するが、多数の小さなクラスができてしまい利用者にとってわかりにくくなってしまう。
Javaのファイル入出力関係のクラス/インタフェースについて整理する

参考