概要
例によって、java言語で学ぶデザインパターン入門を使って学んで行きます。
Adapterパターンとは
交流100ボルトで、直流12ボルトのパソコンを動かす時は、ACアダプターを使います。
アダプターというのは英語で、adapterと表現され、adapt(適用する)という意味です。
つまり、既にあるものを使って、新しいものを使いたい時に、適合させて使えるようにするのが、Adapterです。
実装
本の例では、既にあるものとして、Banner
クラスがあり、新しくPrint
というインターフェイスを実装したPrintBanner
を作ろうとしています。
Banner |
---|
-text |
+showWithParen |
+showWithAster |
- showWithParen : 文字列を()でくくって表示
- showWithAster : 文字列を**でくくって表示
やりたいこと
PrintInterfaceでは、printWeak
とprintStrong
が定義されており、下記のような事を実現したいと考えてます。
- printWeak : 文字列を()でくくって表示
- printStrong : 文字列を**でくくって表示
つまり、やってることをBanner
クラスでやってるのと同じですが、実装するインターフェイスが異なるって事です。
そこで、既にある、Banner
クラスを使ってPrintBanner
をアダプターにしてみまSHOW。
本では、Bannerを継承する方法と、Bannerを委譲する方法の2つを紹介されてたので、両方やってみます。
Bannerを継承するパターン
<?php
class Banner
{
private $text = '';
public function __construct($text)
{
$this->text = $text;
}
public function showWithParen()
{
return sprintf('(%s)', $this->text);
}
public function showWithAster()
{
return sprintf('*%s*', $this->text);
}
}
interface PrintInterface
{
public function printWeak();
public function printStrong();
}
class PrintBanner extends Banner implements PrintInterface
{
public function printWeak()
{
return $this->showWithParen();
}
public function printStrong()
{
return $this->showWithAster();
}
}
?>
テストコードも書きました。
?php
require_once __DIR__ . "/../vendor/autoload.php";
require_once __DIR__ . '/main.php';
class PrintBannerTest extends PHPUnit_Framework_TestCase
{
/**
* @test
*/
public function printWeakは弱く表示する()
{
$printBanner = new PrintBanner('yahoo');
$this->assertSame('(yahoo)', $printBanner->printWeak());
}
/**
* @test
*/
public function printStrongは強く表示する()
{
$printBanner = new PrintBanner('yahoo');
$this->assertSame('*yahoo*', $printBanner->printStrong());
}
}
うん。
委譲するパターン
<?php
class Banner
{
private $text = '';
public function __construct($text)
{
$this->text = $text;
}
public function showWithParen()
{
return sprintf('(%s)', $this->text);
}
public function showWithAster()
{
return sprintf('*%s*', $this->text);
}
}
interface PrintInterface
{
public function printWeak();
public function printStrong();
}
class PrintBannerTransfer implements PrintInterface
{
private $banner = null;
public function __construct(Banner $banner)
{
$this->banner = $banner;
}
public function printWeak()
{
return $this->banner->showWithParen();
}
public function printStrong()
{
return $this->banner->showWithAster();
}
}
これもテストかいてみます。
<?php
require_once __DIR__ . "/../vendor/autoload.php";
require_once __DIR__ . '/main.php';
class PrintBannerIjouTest extends PHPUnit_Framework_TestCase
{
/**
* @test
*/
public function printWeakは弱く表示する()
{
$printBanner = new PrintBannerTransfer(new PrintBanner('yahoo'));
$this->assertSame('(yahoo)', $printBanner->printWeak());
}
/**
* @test
*/
public function printStrongは強く表示する()
{
$printBanner = new PrintBannerTransfer(new PrintBanner('yahoo'));
$this->assertSame('*yahoo*', $printBanner->printStrong());
}
}
継承と委譲、どちらが良いのか
デザインパターンでは、基本的に継承戦略よりも、委譲戦略を推奨している。
- 理由1
- 大体の言語で、単一継承しかサポートしてないため、既に他のクラスを継承してたら終わる。
- 理由2
- 継承の場合は、継承先のクラスで親クラスのメソッドを上書きしてしまう
- 委譲の場合は、エラーを返すのでこのような事故を防ぐ事が可能
ただし、委譲を使う場合には注意も必要
self Problem
といって、継承元のクラスで、自分自身をcallbackしていたり、return
してるとか.
その場合は、継承元のクラスでは実装してないものを継承先で呼び出してしまうなどの予期せぬ事故に繋がったりする恐れもあるので注意が必要。
どんな時に使うのか
- いつもゼロからコードを作る訳ではない
- 既にあるコードを使える場合がある
- しかも既にあるコードが充分テストされてた場合、何かバグがあった場合はAdapterにバグがあることがすぐに分かる
- バージョンアップの際に、既にあるバージョン1のクラスを使いつつ、アダプターを使ってバージョン2のアウトプットを出すような事も出来そう
- ただし、アウトプットがあまりにもかけ離れている場合はアダプターを使うべきでない事もある