第3章


Adapter(一皮かぶせて再利用)パターン

「Adapter」とは?

すでに提供されているものがそのまま使えないときに、
必要な形に変換してから利用することがある。
この「すでに提供されているもの」と「必要なもの」の間の
「ズレ」を埋めるようなデザインパターンが、Adapterパターン

※「Wrapper(ラッパー)パターン」と呼ばれることもある。

Adapterパターンにはは「継承」と「委譲」を使った2種類があるので、
順に見ていく。

Adaptorの具体例 (サンプルプログラム)

Bannerクラス (あらかじめ提供されているクラス)

<?php

class Banner {

    private $string;

    public function __construct($string) {
        $this->string = $string;
    }

    public function showWithParen() {
        echo "(" . $this->string . ")";
    }

    public function showWithAster() {
        echo "*" . $this->string . "*";
    }
}

?>

継承を使うパターン


PrintClass?インターフェイス

必要とされているインターフェイス
(実際に使用するメソッド)

<?php

interface PrintClass {
    function printWeak();
    function printStrong();
}

?>

PrintBanner?クラス

アダプターの役目をし、用意されていた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();
    }

}

?>

Mainクラス

実行クラス

<?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?クラスの実装を変更できる
 ということ。

委譲を使うパターン

委譲とは?

委譲の直接の意味は「誰かに任せる」ということ。
この場合の「委譲」とは
「あるメソッドの実際の処理を、他のインスタンスのメソッドに任せること」


PrintClass2クラス

必要とされているクラス(実際に使用するメソッド)

<?php

abstract class PrintClass2 { 
    public abstract function printWeak();
    public abstract function printStrong(); 
}

?>

PrintBanner2クラス

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();
    }
}

?>

Mainクラス

実行クラス。
呼び出すクラスが変わっただけで、あとは変わっていない。

<?php

require_once("PrintBanner2.class.php");

class Main {

    public function main() {
        $print = new PrintBnner("Hello");
        $print->printWeak();
        echo "<br />";
        $print->printStrong();
    }

}

new Main();

?>

Adaptorパターンにでてくるもの

Target(対象)

現在必要となっているメソッドを定める役。
サンプルプログラムでは、
PrintClass?インターフェイス(継承の場合)やPrintClass?クラス(委譲の場合)にあたる。


Client(依頼者)

Target役が定めたメソッドを使って実際に仕事をする役。

Adaptee(適合される側)

すでにメソッドを持っている役。
サンプルプログラムではBannerクラスにあたる。

Adapter

Adaptee役のメソッドを使って、Targetの役を満たそうとするもの。
サンプルプログラムではPrintBanner?クラス、PrintBanner2クラスにあたる。

Adapterパターンの利点

プログラムの再利用が容易になる

すでに存在しているクラスを利用する際に有効。

もしその既存のクラスが十分にテストされ、バグの少なく、
実際にこれまで使われてきた実績があったとする。

このクラスを再利用する際にAdapterパターンは既存のクラスに一皮かぶせて
必要とするクラスをつくり、利用することが出来る。

この方がテストも少なく、より効率的な開発が可能になる。

実際に再利用しようとしたクラスを修正した場合には、
修正後もう一度テストをしなければいけない。


ソースが無くてもよい

上記で示したように、Adapterパターンは既存のクラスにはまったく手を付けずに、
目的のインターフェイスにあわせようとするもの。

なので、既存のクラスのソースコードが無くても、その仕様さえ把握できていれば、
新しいクラスを作ることが可能。

バージョンアップと互換性

ソフトウェアにはバージョンアップがつきもの。

このバージョンアップのときに問題となるのが「古い版との互換性」。
古い版と新しい版を共存させ、しかも楽にメンテナンスを行うためにAdapterパターンを使う場合がある。

例えば、今後は新しい版だけをメンテナンスしたいとする。
その場合は新しい版をAdaptee役とし、古い版をTarget役とする。

そしてその新しい版を使って古い版のメソッドを実装するAdaptater役のクラスを作る。


参考資料

  • Java言語で学ぶデザインパターン入門