感謝のプログラミング 10000時間

たどり着いた結果(さき)は、感謝でした。

PHPでAdapterパターン

<スポンサーリンク>

Adapterパターンって何?

一言でいうと、APIの異なるクラスどうしを「適合させる」パターンのこと。
Adapterパターンの目的は、新たなインターフェースを作成することだ。
GoF本の定義では以下のようになっている。

あるクラスのインターフェースを、クライアントが求める他のインターフェースへ変換する。
Adapterパターンは、インターフェースに互換性のないクラス同士を組み合わせることができるようにする。

すでに存在していて、かつ十分にテストされたクラスがあるとする。
そんな十分にテストされたクラスでも、どうしても利用する側が欲しいAPIとは異なったAPIが提供されているとする。
里ユオする側を変更するには労力がかかり、かといって既存の十分にテストされたクラスも変更したくない。

そんなときに使うのが、Adapterパターンだ。
2つの異なるAPIを吸収するアダプタを用意することで、変更範囲をおさえた上で、クラス同士を結合させることができる。

Adapterパターンのメリット

  • 既存のコードを修正すること無く再利用できる

一皮かぶせて再利用ともいうが、既存のクラスを包み込むWrapperとして機能するため、元からあるクラスを変更しないで再利用できる。

  • 使う側は「アダプタの向こう側」を意識する必要がない

クライントからはアダプタを意識すればよく、アダプタの向こう側の実装を意識せずに機能だけ使うことができるようになる。

  • 公開するAPIを制限できる

Adapterをかぶせることで、既存のクラスのインターフェースによる制約から解放される。
外部に公開するAPIを制限することもできる。

AdapterパターンとFacadeパターンの違い

チェック事項 Façade Adapter
既存のクラスはあるか? yes yes
インターフェースを設計する必要はある? no yes
ポリモーフィズムに則ったオブジェクトの振る舞いが必要になるか? no たぶんno
より簡潔なインターフェースが必要か? yes no

サンプル

これから見ていくサンプルのクラス図はこんな感じ。
・Adapterパターンのクラス図
f:id:sho322:20131218223307j:plain
NUBoard
CANSAY NUboard

Banner.php
これは元からあるテスト済みの「既存のクラス」の想定

<?php

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

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

    public function showWithSharp() {
        echo "#" . $this->string . "#", PHP_EOL;
    }
}

で、上の「既存のクラス」に被せる皮(=Adapter)がこちら。
PrintBanner.php

<?php
require_once './Banner.php';
require_once './Printer.php';
class PrintBanner extends Banner implements Printer {
    public function __construct($string) {
        parent::__construct($string);
    }
    public function printKome() {
       parent::showWithStar();
    }

    public function printSha() {
       parent::showWithSharp();
    }
}

上の皮となるAdapterは既存のクラスである「Banner」を継承(extends)している。
で、中のメソッドで、Bannerのメソッドを呼び出している。

このPrintBannerのインターフェース(=API)となるのが、以下のPrinter.phpだ。
Printer.php

<?php

interface Printer {
    public function printKome();
    public function printSha();
}
?>

使う側からすると、このAPIとなるPrinter.phpのことだけ知っていればいい。
では、使ってみよう。
client.php

<?php

require_once './Banner.php';
require_once './Printer.php';
require_once './PrintBanner.php';

$printer = new PrintBanner("hello Adapter!");
$printer->printKome();
$printer->printSha();

実行結果はこうなる

$ /usr/bin/php client.php
*hello Adapter!*
#hello Adapter!#

Adapterパターンのクラス図

f:id:sho322:20131218223341j:plain
f:id:sho322:20131218223352j:plain

参考文献

PHPによるデザインパターン入門

PHPによるデザインパターン入門

増補改訂版Java言語で学ぶデザインパターン入門

増補改訂版Java言語で学ぶデザインパターン入門

いろんな本を参考にしながら記事を書いているんだけど、上記2冊がとてもよくまとまっていて、勉強になった。