読者です 読者をやめる 読者になる 読者になる

はむはむエンジニアぶろぐ

このブログのコンセプトは"ハッキングの為なら愛する家族を傷つけることをいとわない" 自分にとってエンジニアリングは "手段ではなく生きる目的" である

Decoratorパターンで既存のクラスに機能追加をする

f:id:secret_hamuhamu:20150816222337j:plain
Decoratorパターンは、GoFのデザインパターンの一つです。

既存のオブジェクトに新しい機能や振る舞いを動的に追加することを可能にする。

Decoratorパターン - wikipedia

既存のクラスに対して、機能追加を行おうというものです。
ここで、重要なのが既存のクラスに手を加えずに追加を行うということです。

そもそも、要件が変更されて振る舞いを変更しなければいけないのであれば、既存のクラスを修正します。
ですが、機能追加は本質的に満たさなければならない要件 + αであることが多いです。

なので、機能追加をする際、既存のクラスに手を加えてしまうと、既存のクラスの凝集度が下がってしまいますし 既存のクラスに対するテストがコケてしまう恐れがあります。

高凝集を保ったまま比較的安全に機能追加を行うパターンと覚えるといいと思います。

環境構成

  • PHP 5.6

Decoratorパターンを実装する

Decoratorパターンの特徴

  • インターフェースは変更しない
  • 振る舞いを変更せずに機能を追加する

Decoratorパターンを実装する方法は、2パターンかなと思います。

  • 継承
  • 委譲

ケースバイケースで、使い分ければいいと思います。
※ 世の中的には、委譲を使うパターンのみDecoratorパターンと定義するかもしれませんが、目的を達成する手段と考えれば、同列と考えて良いと私は思います。

継承を使った実装

Carクラスに、継承を使ってロギング機能を追加させます。
ロギングは、クラスのコアな機能ではないので、既存のクラスに手を加えて修正してしまうと凝集度が下がってしまいます。

Decoratorパターンを用いて、LoggableCarクラスを作ります。
実際には、ログを取らずに echo してるだけなので、ログをとっていると思っていただければ。

<?php

class Car
{
    public function broom()
    {
        echo "broom\r\n";
    }
}

class LoggableCar extends Car
{
    public function broom()
    {
        echo "logging start...\r\n";
        parent::broom();
        echo "logging end...\r\n";
    }
}

$car = new LoggableCar();
$car->broom();

実行結果

logging start...
broom
logging end...


委譲を使った実装

次はCarクラスに、継承を使ってロギング機能を追加させます。
万一、委譲するメソッド( broom ) をタイポしてしまう可能性があるのでInterfaceを使いましょう。
これは、継承を使うパターンでも言えることですけど。

<?php

interface CarBroom
{
    public function broom();
}

class Car implements CarBroom
{
    public function broom()
    {
        echo "broom\r\n";
    }
}

class LoggableCar implements CarBroom
{
    private $carBroom;
    public function __construct(CarBroom $carBroom)
    {
        $this->carBroom = $carBroom;
    }

    public function broom()
    {
        echo "logging start...\r\n";
        $this->carBroom->broom();
        echo "logging end...\r\n";
    }
}

$car = new LoggableCar(new Car());
$car->broom();

実行結果

logging start...
broom
logging end...


まとめ

Decoratorパターンは、既存のクラスに手を加えることなく機能追加できるパターンです。
既存のクラスに手を加えなくてもよいのであれば、既存のテストを壊す心配はありません。

私が、Decoratorをよく使うシーンとしては、特定期間のみ追加機能を提供したいなど クラスの本質的な機能変更ではない場合に有効かと思います。

おすすめの本

オブジェクト指向における再利用のためのデザインパターン

オブジェクト指向における再利用のためのデザインパターン

  • 作者: エリックガンマ,ラルフジョンソン,リチャードヘルム,ジョンブリシディース,Erich Gamma,Ralph Johnson,Richard Helm,John Vlissides,本位田真一,吉田和樹
  • 出版社/メーカー: ソフトバンククリエイティブ
  • 発売日: 1999/10
  • メディア: 単行本
  • 購入: 21人 クリック: 711回
  • この商品を含むブログ (210件) を見る

Head Firstデザインパターン ―頭とからだで覚えるデザインパターンの基本

Head Firstデザインパターン ―頭とからだで覚えるデザインパターンの基本

  • 作者: Eric Freeman,Elisabeth Freeman,Kathy Sierra,Bert Bates,佐藤直生,木下哲也,有限会社福龍興業
  • 出版社/メーカー: オライリージャパン
  • 発売日: 2005/12/02
  • メディア: 大型本
  • 購入: 14人 クリック: 362回
  • この商品を含むブログ (98件) を見る

オブジェクト指向でなぜつくるのか 第2版

オブジェクト指向でなぜつくるのか 第2版

テスト駆動開発入門

テスト駆動開発入門

  • 作者: ケントベック,Kent Beck,長瀬嘉秀,テクノロジックアート
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 2003/09
  • メディア: 単行本
  • 購入: 45人 クリック: 1,058回
  • この商品を含むブログ (161件) を見る