機能の階層と実装の階層を分ける
機能(メソッド)を追加したいとき、サブクラス(子クラス、派生クラス、拡張クラス)をつくる
さらに機能を追加する場合、さらに階層が深くなる
Something SomethingGood SomethingBetter
抽象クラスでインタフェース(API)を規定し、サブクラスで実装する。
部品として交換しやすいクラスになる。
AbstractClass ConcreteClass1 ConcreteClass2
機能の階層と実装の階層が混在していると見通しが悪いので分ける。
Abstraction(抽象化):Display
機能クラス階層の最上位。
Implementorを保持し、そのメソッドを使って機能を記述。
RefinedAbstraction(改善した抽象化):CountDisplay
Abstractionに機能を追加したもの。
Implementor(実装者):DisplayImpl
実装クラス階層の最上位。
Abstractionのインタフェースを実装するためのメソッドを規定。
ConcreteImplementor(具体的な実装者):StringDisplayImpl
Implementorを実装。
2つの階層を$implが橋渡ししている。
分けておけば拡張するのが楽になる。
継承だとソースコードを書き換えないと変わらない。
Displayのように、委譲すると、引数で渡すものを変えれば変わる。
Display.php
class Display { private $impl; function __construct(DisplayImpl $impl) { $this->impl = $impl; } function open() { $this->impl->rawOpen(); } function print() { $this->impl->rawPrint(); } function close() { $this->impl->rawClose(); } final function display() { $this->open(); $this->print(); $this->close(); } }
CountDisplay.php
class CountDisplay extends Display { function __construct(DisplayImpl $impl) { parent::__construct($impl); } function multiDisplay($times) { $this->open(); for ($i=0;$i<$times;$i++) { $this->print(); } $this->close(); } }
DisplayImpl.php
abstract class DisplayImpl { abstract function rawOpen(); abstract function rawPrint(); abstract function rawClose(); }
StringDisplayImpl.php
class StringDisplayImpl extends DisplayImpl { private $string; private $width; function __construct($string) { $this->string = $string; $this->width = strlen($string); } function rawOpen() { $this->printLine(); } function rawPrint() { echo "|" . $this->string . "|\n"; } function rawClose() { $this->printLine(); } private function printLine() { echo "+"; for ($i=0;$i<$this->width;$i++) { echo "-"; } echo "+\n"; } }
Main.php
require "../autoload.php"; $d1 = new Display(new StringDisplayImpl("Hello")); $d2 = new CountDisplay(new StringDisplayImpl("Good bye")); $d3 = new CountDisplay(new StringDisplayImpl("Good night")); $d1->display(); $d2->display(); $d3->display(); $d3->multiDisplay(3);