Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

概要

例によって、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のアウトプットを出すような事も出来そう
  • ただし、アウトプットがあまりにもかけ離れている場合はアダプターを使うべきでない事もある
Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away