10
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

PHPで学ぶデザインパターン ~ 第2章 - Adapter ~

Last updated at Posted at 2016-02-03

概要

例によって、java言語で学ぶデザインパターン入門を使って学んで行きます。

Adapterパターンとは

交流100ボルトで、直流12ボルトのパソコンを動かす時は、ACアダプターを使います。

アダプターというのは英語で、adapterと表現され、adapt(適用する)という意味です。

つまり、既にあるものを使って、新しいものを使いたい時に、適合させて使えるようにするのが、Adapterです。

実装

本の例では、既にあるものとして、Bannerクラスがあり、新しくPrintというインターフェイスを実装したPrintBannerを作ろうとしています。

Banner
-text
+showWithParen
+showWithAster
  • showWithParen : 文字列を()でくくって表示
  • showWithAster : 文字列を**でくくって表示

やりたいこと

PrintInterfaceでは、printWeakprintStrongが定義されており、下記のような事を実現したいと考えてます。

  • 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のアウトプットを出すような事も出来そう
  • ただし、アウトプットがあまりにもかけ離れている場合はアダプターを使うべきでない事もある
10
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?