すでに提供されているものがそのまま使えないときに、
必要な形に変換してから利用することがある。
この「すでに提供されているもの」と「必要なもの」の間の
「ズレ」を埋めるようなデザインパターンが、Adapterパターン
※「Wrapper(ラッパー)パターン」と呼ばれることもある。
Adapterパターンにはは「継承」と「委譲」を使った2種類があるので、
順に見ていく。
<?php class Banner { private $string; public function __construct($string) { $this->string = $string; } public function showWithParen() { echo "(" . $this->string . ")"; } public function showWithAster() { echo "*" . $this->string . "*"; } } ?>
必要とされているインターフェイス
(実際に使用するメソッド)
<?php interface PrintClass { function printWeak(); function printStrong(); } ?>
アダプターの役目をし、用意されていたBannerクラスを拡張して、
showWithParenm?メソッドとshowWithAster?メソッドを継承している。
また、PrintClass?のPrintWeak?メソッドとprintStringメソッドを実装している。
<?php require_once("Banner.class.php"); require_once("PrintClass.class.php"); class PrintBnner extends Banner implements PrintClass { public function printWeak() { parent::showWithParen(); } public function printStrong() { parent::showWithAster(); } } ?>
実行クラス
<?php require_once("PrintBanner.class.php"); class Main { public function main() { $print = new PrintBnner("Hello"); $print->printWeak(); echo "<br />"; $print->printStrong(); } } new Main(); ?>
※このMainはあくまでPrintClass?というインターフェイスを使って
プログラミングされている。
MainクラスはPrintBanner?クラスがどのように実装されているか知らない。
ということは、Mainクラスをまったく変更せずにPrintBnner?クラスの実装を変更できる
ということ。
委譲の直接の意味は「誰かに任せる」ということ。
この場合の「委譲」とは
「あるメソッドの実際の処理を、他のインスタンスのメソッドに任せること」
必要とされているクラス(実際に使用するメソッド)
<?php abstract class PrintClass2 { public abstract function printWeak(); public abstract function printStrong(); } ?>
PrintBanner2クラスのprintWeakメソッド、printStrongメソッドが呼ばれたとき、
自分で処理するのではなく、別のインスタンス(Bannerクラスのインスタンス)の
showWithParen?メソッドに任せている。
<?php require_once("Banner.class.php"); require_once("PrintClass2.class.php"); class PrintBnner extends PrintClass2 { private $banner; public function __construct($string) { $this->banner = new Banner($string); } public function printWeak() { $this->banner->showWithParen(); } public function printStrong() { $this->banner->showWithAster(); } } ?>
実行クラス。
呼び出すクラスが変わっただけで、あとは変わっていない。
<?php require_once("PrintBanner2.class.php"); class Main { public function main() { $print = new PrintBnner("Hello"); $print->printWeak(); echo "<br />"; $print->printStrong(); } } new Main(); ?>
現在必要となっているメソッドを定める役。
サンプルプログラムでは、
PrintClass?インターフェイス(継承の場合)やPrintClass?クラス(委譲の場合)にあたる。
Target役が定めたメソッドを使って実際に仕事をする役。
すでにメソッドを持っている役。
サンプルプログラムではBannerクラスにあたる。
Adaptee役のメソッドを使って、Targetの役を満たそうとするもの。
サンプルプログラムではPrintBanner?クラス、PrintBanner2クラスにあたる。
すでに存在しているクラスを利用する際に有効。
もしその既存のクラスが十分にテストされ、バグの少なく、
実際にこれまで使われてきた実績があったとする。
このクラスを再利用する際にAdapterパターンは既存のクラスに一皮かぶせて
必要とするクラスをつくり、利用することが出来る。
この方がテストも少なく、より効率的な開発が可能になる。
実際に再利用しようとしたクラスを修正した場合には、
修正後もう一度テストをしなければいけない。
上記で示したように、Adapterパターンは既存のクラスにはまったく手を付けずに、
目的のインターフェイスにあわせようとするもの。
なので、既存のクラスのソースコードが無くても、その仕様さえ把握できていれば、
新しいクラスを作ることが可能。
ソフトウェアにはバージョンアップがつきもの。
このバージョンアップのときに問題となるのが「古い版との互換性」。
古い版と新しい版を共存させ、しかも楽にメンテナンスを行うためにAdapterパターンを使う場合がある。
例えば、今後は新しい版だけをメンテナンスしたいとする。
その場合は新しい版をAdaptee役とし、古い版をTarget役とする。
そしてその新しい版を使って古い版のメソッドを実装するAdaptater役のクラスを作る。
参考資料