LoginSignup
1
1

More than 5 years have passed since last update.

【PHPデザインパターン】13_Decorator~かぶせて機能UP

Posted at

引用記事

この記事を書くきっかけになったブログです。

記事内の解説やソースコードは、こちらのブログと著者の公開リポジトリを参考にしています。

Do You PHP はてな〜[doyouphp][phpdp]PHPによるデザインパターン入門 - Decorator~かぶせて機能UP

概要

  • Decoratorとは直訳すると「装飾者」。
  • オブジェクトに対して機能を柔軟に(動的に)追加したり取り外したりする。
  • サブクラス化よりも柔軟な拡張方法を提供する。
  • 継承による拡張は責任(機能)を「静的」に追加する。「動的」な追加は、実行中に責任を追加したり外したりできる。

構成要素

Componentクラス

  • 拡張される機能を定義した抽象クラス。

ConcreteComponentクラス

  • Componentクラスで定義した機能を基本実装する、飾り付けされる具象クラス。

Decoratorクラス

  • Componentクラスを実装し、さらにプロパティ(メンバ変数)としてComponentクラスのオブジェクトを保持する抽象クラス。
  • メソッドの具体的な実装はComponentクラス(ConcreteComponentクラス)へ委譲する。

ConcreteDecoratorクラス

  • Componentクラスに機能を追加するために、Decoratorクラスを継承するクラス。
  • 自身のメソッドで親クラスのメソッドを利用しながら機能の拡張(飾り付け)を行う。

実演

処理の流れ

  • PlainTextクラスで文字列をプロパティとして保有する。
  • TextDecoratorクラスTextDataクラスのオブジェクトを保有する。
  • DoubleTextクラスで文字列を全角に変換する。
  • UpperTextクラスで文字列を大文字に変換する。
  • TextDataクラスのオブジェクトに対し、再帰処理で各ConcreteDecoratorクラスのメソッドを実行する。

ファイル構造

MyDecorator
  ├── DoubleText.php
  ├── PlainText.php
  ├── TextData.php
  ├── TextDecorator.php
  ├── UpperText.php
  └── my_client.php

ソースコード

Componentクラス

TextData.php

TextData.php
<?php
namespace DoYouPhp\PhpDesignPattern\Decorator\MyDecorator;

/**
 * Componentクラスに相当する
 * インターフェイスのため、サブクラスで実装する
 */
interface TextData
{
    public function getText();
    public function setText($str);
}

ConcreteComponentクラス

PlainText.php

PlainText.php
<?php
namespace DoYouPhp\PhpDesignPattern\Decorator\MyDecorator;

use DoYouPhp\PhpDesignPattern\Decorator\MyDecorator\TextData;

/**
 * ConcreteComponentクラスに相当する
 * 編集前のテキストを表すクラス
 */
class PlainText implements TextData
{
    private $textString = null;

    // 文字列を取得する
    // 常にplainの状態で返す
    public function getText()
    {
        return $this->textString;
    }

    // 文字列をセットする
    public function setText($str)
    {
        $this->textString = $str;
    }
}

Decoratorクラス

TextDecorator.php

TextDecorator.php
<?php
namespace DoYouPhp\PhpDesignPattern\Decorator\MyDecorator;

use DoYouPhp\PhpDesignPattern\Decorator\MyDecorator\TextData;

/**
 * Decoratorクラス
 * TextDataクラスのオブジェクトをプロパティ(メンバ変数)として保有する
 */
abstract class TextDecorator implements TextData
{
    // TextDataインスタンスを保有する
    private $text;

    public function __construct(TextData $target)
    {
        $this->text = $target;
    }

    // 一番深い階層に存在するPlainTextクラスで保有する文字列が返される
    // 再帰処理を行っている
    // getTextメソッドの戻り値に対して、さらにgetTextメソッドを実行している
    public function getText()
    {
        return $this->text->getText();
    }

     // TextDataインスタンスに文字列をセットする
    public function setText($str)
    {
        $this->text->setText($str);
    }
}

ConcreteDecoratorクラス

DoubleText.php

DoubleText.php
<?php
namespace DoYouPhp\PhpDesignPattern\Decorator\MyDecorator;

use DoYouPhp\PhpDesignPattern\Decorator\MyDecorator\TextDecorator;
use DoYouPhp\PhpDesignPattern\Decorator\MyDecorator\TextData;

/**
 * ConcreteDecoratorクラスに相当する
 * TextDecoratorクラスを継承する
 */
class DoubleText extends TextDecorator
{
    // インスタンスをセットする
    public function __construct(TextData $target)
    {
        // 親クラスのメソッド
        parent::__construct($target);
    }

    // 文字列を全角文字に変換して返す
    public function getText()
    {
        // 親クラスのメソッド
        $str = parent::getText();
        // 文字列を変換する
        $str = mb_convert_kana($str, "R");

        return $str;
    }
}

UpperText.php

UpperText.php
<?php
namespace DoYouPhp\PhpDesignPattern\Decorator\MyDecorator;

use DoYouPhp\PhpDesignPattern\Decorator\MyDecorator\TextDecorator;
use DoYouPhp\PhpDesignPattern\Decorator\MyDecorator\TextData;

/**
 * ConcreteDecoratorクラスに相当する
 * TextDecoratorクラスを継承する
 */
class UpperText extends TextDecorator
{
    // インスタンスをセットする
    public function __construct(TextData $target)
    {
        // 親クラスのメソッド
        parent::__construct($target);
    }

    // 文字列を大文字に変換して返す
    public function getText()
    {
        // 親クラスのメソッド
        $str = parent::getText();
        // 文字列を変換する
        $str = mb_strtoupper($str);

        return $str;
    }
}

Clientクラス

my_client.php

my_client.php
<?php
namespace DoYouPhp\PhpDesignPattern\Decorator\MyDecorator;

require dirname(dirname(__DIR__)).'/vendor/autoload.php';

use DoYouPhp\PhpDesignPattern\Decorator\MyDecorator\DoubleText;
use DoYouPhp\PhpDesignPattern\Decorator\MyDecorator\UpperText;
use DoYouPhp\PhpDesignPattern\Decorator\MyDecorator\PlainText;

function decorate($text, array $decorate = [])
{
    $text_object = new PlainText();
    // plainの文字列がセットされる
    $text_object->setText($text);

    // $text_objectは階層のようになる
    foreach ($decorate as $val) {
        switch ($val) {
        case 'double' :
            $text_object = new DoubleText($text_object);
            break;
        case 'upper':
            $text_object = new UpperText($text_object);
            break;
        }
    }

    // PlainTextクラスが装飾されている状態
    // echo '<pre>';
    // var_dump($text_object);
    // echo '</pre>';

    // 最後に追加したクラスが表示される
    // var_dump(get_class($text_object));

    // 各クラスのオブジェクトのメソッドを実行できる
    echo $text_object->getText().'<br>'."\n";
}

$text = 'Hello, World!';

echo '---plain---'.'<br>'."\n";
decorate($text);

echo '---double---'.'<br>'."\n";
decorate($text, ['double']);

echo '---upper---'.'<br>'."\n";
decorate($text, ['upper']);

// upper,double,plainの順で呼び出される
// 再帰処理が実行されているため、実質はplain, double, upperの順番になる
echo '---double + upper---'.'<br>'."\n";
decorate($text, ['double', 'upper']);
1
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
1
1