LoginSignup
2
1

More than 3 years have passed since last update.

Adapterパターンを使うメリットとPHP実装例

Posted at

※この投稿はJava言語で学ぶデザインパターン入門を読んだ感想と復習を兼ねたまとめです

Adapterパターンとは

Adapterパターンはデザインパターンのひとつで、既存のクラスを修正することなく、インタフェースを変更するための手法です。

Java言語で学ぶデザインパターン入門では、電化製品のACアダプタを例に説明されていました。家庭用のコンセントは100ボルトで固定されていますが、ノートパソコンが必要としているのは12ボルトの電源です。この「既に提供されているもの」と「いま必要としているもの」の差を吸収するのがAdapterの役割です。

Adapterパターンに登場する役割は、それぞれ次のように呼ばれるようです。

役割 意味
Target いま必要としているもの(インタフェース) ノートパソコンを動かすための12ボルトの電源
Adapter TargetとAdapteeの差を埋めるもの ノートパソコンと家庭用コンセントをつなぐACアダプタ
Adaptee 既に提供されているもの 家庭用コンセントの100ボルトの電源

この関係をクラス図で表現すると次のようになります。

スクリーンショット 2020-08-06 9.44.45.png

また、AdapterパターンはWrapperパターンと呼ばれることもあるようです。

継承を利用したAdapterパターン実装例

前提として、次のようなBannerクラスが既に実装されているとします。

class Banner
{
    private $string;

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

    public function showWithParen(): void
    {
        echo '(' . $this->string . ')' . "\n";
    }

    public function showWithAster(): void
    {
        echo '*' . $this->string . '*' . "\n";
    }
}

Bannerは、文字列を出力する方法を2パターン(showWithParen()showWithAster())持っています。

しかし、いま必要とされているのは次のようなインタフェースの実装です。

interface PrintInterface
{
    public function printWeak(): void;
    public function printStrong(): void;
}

このPrintInterfaceを経由して、Bannerが持っているメソッドを呼び出す方法を考えます。

Adapterパターンには2つの実現方法がありますが、さきに紹介するのは継承を利用した方法です。

次のようなAdapterを作成します。

class PrintBanner extends Banner implements PrintInterface
{
    public function printWeak(): void
    {
        $this->showWithParen();
    }

    public function printStrong(): void
    {
        $this->showWithAster();
    }
}

上記のPrintBannerは、既に実装されているBannerを継承して、いま必要とされているPrintInterfaceを実装しています。このようなAdapterを間にはさむことによって、既存のクラスを変更することなく、呼び出し元はインタフェース経由で目的の関数を呼び出すことが可能になります。

委譲を利用したAdapterパターン実装例

Adapterパターンを実現するもうひとつの方法は、委譲を利用する方法です。

委譲という言葉は知りませんでしたが、利用したいクラスのインスタンスを生成し、そのインスタンスを経由して目的の処理を実行することを委譲というようです。クラス図で表現すると次のようになります。

スクリーンショット 2020-08-06 9.44.24.png

委譲を利用する方法は、Targetがインタフェースではなくクラスである場合に便利です。

// Targetがクラスである場合
abstract class PrintAbstract
{
    abstract public function printWeak(): void;
    abstract public function printStrong(): void;
}

さきほどはAdpterがAdapteeを継承することでAdapterパターンを実現できましたが、複数のクラスを同時に継承することはできないため、Targetがクラスである場合にはそれが使えません。代わりに、委譲を利用して次のように実現します。

class PrintBanner extends PrintAbstract
{
    private $banner; // Bannerをプロパティとして持つ

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

    public function printWeak(): void
    {
        $this->banner->showWithParen(); // 委譲を利用してBannerの関数を呼び出す
    }

    public function printStrong(): void
    {
        $this->banner->showWithAster();
    }
}

Adapterパターンのメリット

Adapterパターンは、既存のクラスを修正することなく、インタフェースを変更するための手法です。

既存のクラスを使い回すことのメリットには、次のようなものがあるようです。

  • ゼロから実装するよりも早く完成する
  • 既存のクラスがテスト済みである場合は、確実に動作するものを使いまわせる
    • バグが発生した場合は、Adapter側に原因があることが明らかなので修正も容易
    • 既存のクラスは修正しないため、テストを書き直す必要がない
  • 委譲を利用すればTargetがクラスである場合も他のクラスの実装を利用できる

参考までに、Adapterパターン実装例(PHP)を載せておきます。

<?php

/**
 * 継承を利用して実現するパターン
 */

interface PrintInterface
{
    public function printWeak(): void;
    public function printStrong(): void;
}

class Banner
{
    private $string;

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

    public function showWithParen(): void
    {
        echo '(' . $this->string . ')' . "\n";
    }

    public function showWithAster(): void
    {
        echo '*' . $this->string . '*' . "\n";
    }
}

class PrintBanner extends Banner implements PrintInterface
{
    public function printWeak(): void
    {
        $this->showWithParen();
    }

    public function printStrong(): void
    {
        $this->showWithAster();
    }
}

function main(PrintInterface $print)
{
    $print->printWeak();
    $print->printStrong();
}

$print = new PrintBanner('Hello');
main($print);
<?php

/**
 * 委譲を利用して実現するパターン
 */

abstract class PrintAbstract
{
    abstract public function printWeak(): void;
    abstract public function printStrong(): void;
}

class Banner
{
    private $string;

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

    public function showWithParen(): void
    {
        echo '(' . $this->string . ')' . "\n";
    }

    public function showWithAster(): void
    {
        echo '*' . $this->string . '*' . "\n";
    }
}

class PrintBanner extends PrintAbstract
{
    private $banner;

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

    public function printWeak(): void
    {
        $this->banner->showWithParen();
    }

    public function printStrong(): void
    {
        $this->banner->showWithAster();
    }
}

function main()
{
    $print = new PrintBanner('Hello');
    $print->printWeak();
    $print->printStrong();
}

main();
2
1
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
2
1