デザインパターン学習メモ:「Decorator」


このパターンの目的

GoF本より引用する。

オブジェクトに責任を動的に追加する。Decoratorパターンは、サブクラス化よりも柔軟な機能拡張方法を提供する。(P.187)

このパターンの動作

『オブジェクト指向のこころ』の説明がわかりやすかった。

Decoratorパターンは、付加する機能に対して責任を持つDecoratorというオブジェクトから始まり、元々からある機能を表すオブジェクトで終了する、一連のオブジェクト連鎖を生成することによって機能します。(P.261)

つまり、図示するとこのようになる。(同じくP.261の図を参考に作成)

[:Decorator1]-->[:Decorator2]-->[Decorator3]-->[:ConcreteComponent]

「オブジェクトの連鎖」がポイントになる。ここが、オブジェクトを多重に修飾している箇所だ。

詳しくは実装例を見てほしい。

実装例

『デザインパターン入門』を参考に作成。

Display.java
// 共通のインタフェース
// Component役
public interface Display {
    String getText();

    default void show() {
        System.out.println(getText());
    }
}
StringDisplay.java
// ConcreteComponent役
public class StringDisplay implements Display {
    private String string;

    public StringDisplay(String string) {
        this.string = string;
    }

    @Override
    public String getText() {
        return this.string;
    }
}
DisplayDecorator.java
// Decorateクラス用のインタフェース
// Decorator役
public abstract class DisplayDecorator implements Display {
    protected Display display;

    protected DisplayDecorator(Display display) {
        this.display = display;
    }
}

SurroundWithParentheses.java
// ConcreteDecorator役
public class SurroundWithParentheses extends DisplayDecorator {
    public SurroundWithParentheses(Display display) {
        super(display);
    }

    @Override
    public String getText() {
        String text = "(" + display.getText() + ")";
        return text;
    }
}
SurroundWithBrackets.java
//ConcreteDecorator役
public class SurroundWithBrackets extends DisplayDecorator {
    public SurroundWithBrackets(Display display) {
        super(display);
    }

    @Override
    public String getText() {
        String text = "[" + display.getText() + "]";
        return text;
    }
}

実行

Main.java
// 実行用クラス
public class Main {
    public static void main(String[] args) {
        Display display1 = new StringDisplay("Hello, World!");
        display1.show();

        Display display2 = new SurroundWithBrackets(display1);
        display2.show();

        Display display3 = new SurroundWithParentheses(display2);
        display3.show();
    }
}
結果
Hello, World!
[Hello, World!]
([Hello, World!])

実装例の解説

  • show()メソッド呼び出すと、表示する内容はgetText()メソッドによって取得される
  • Decoratorを使った場合(上記の実行例でいうところのdisplay2と3)は、まずいちばん外側のクラスのgetText()を実行して修飾を加え、次にひとつ内部のクラスのgetText()を実行し、最後にConcreteComponent(StringDisplayクラス)のgetText()を実行する
    • この動作では「委譲」を用いている

利点

  • オブジェクトに対してクラスの拡張よりも柔軟に責務を追加できる
    • 機能追加が容易(既存のコードに影響を与えずに機能の追加が可能)
    • 実行時に追加可能

参考文献

  • エリック ガンマ、ラルフ ジョンソン、リチャード ヘルム、ジョン プリシディース(1999)『オブジェクト指向における再利用のためのデザインパターン 改訂版』本位田 真一、吉田 和樹 監訳、SBクリエイティブ
  • アラン・シャロウェイ、ジェームズ・R・トロット(2014)『デザインパターンととともに学ぶ オブジェクト指向のこころ』村上雅章訳、丸善出版
  • 結城浩(2004)『Java言語で学ぶ デザインパターン入門』SBクリエイティブ