デザインパターンの章

6.5. Decorator

機能を追加(拡張)していくときに使えるパターンです。

元々あるクラスに皮を重ねて肉付けすることで、 目的に即した機能を備えたクラスを作成することができます。 デコレーションして飾り付けるので Decorator パターンと呼ばれています。

継承してサブクラスを作るで機能を拡張することはできますが、 このパターンは継承よりも自由度の高い機能追加が可能なので要検討といえます。

■クラス図

■サンプルソース

クラス図と内容が少し異なりますが根本的には同じですのでご了承を。

これは java.util.Set インターフェースです。

public interface Set<E> extends Collection<E> {
    boolean add(E e);
    boolean addAll(Collection<? extends E> c);
    void clear();
    boolean isEmpty();
        // :
}

ラッパーです。

public class Hoge<E> implements Set<E> {
    private final Set<E> s;
    private int addCount;

    public Hoge(Set<E> s) {
        this.s = s;      /* くるむ。 */
    }

    public boolean add(E e) {
        addCount++;      /* 追加処理 */
        return s.add(e);
    }

    public boolean addAll(Collection<? extends E> c) {
        addCount += c.size(); /* 追加処理 */
        return s.addAll(c);
    }

    public int getCount() {
        return addCount;
    }

    /* 転送メソッド。 */
    public void clear() { s.clear();}
    public boolean isEmpty() { return s.isEmpty();}
        // :
}

追加したい処理を記述します。
それ以外はそのままくるんだクラスのメソッドを呼び出します(転送といいます)。

■実装のミソ

1.
同じインターフェースか同じスーパークラスを用意すること。

2.
追加処理分はラッパークラスに記述すること。

3.
既存処理はラップしたクラスのメソッドを呼び出すこと。

■利用例

void Test() {
    /* 計測機能を得た Set 系クラス達。 */
    Set<String> s1 = new Hoge<>(new TreeSet<String>(list));
    Set<String> s2 = new Hoge<>(new HashSet<String>(capacity, loadFactor));
    Set<String> s3 = new Hoge<>(new LinkedHashSet<String>()):
        // :
}

Hoge クラスは Set インターフェースを実装しており、またその唯一のコンストラクタも Set インターフェースを有したクラスを受け取るようになっています。

Hoge クラスは計測機能を追加して1つの Set を他の Set へ変換します。
どのような Set が来ても平気なので、Set のバリエーションがどんなに増えようとも 全く問題ありません。柔軟に機能拡張できることがわかると思います。

もし継承して計測機能を追加しようとすると、各々の Set に対して継承して 「計測機能付き~Set」を何種類も作る羽目になってしまうでしょう。
そうなると大変ですね。

■注意点

Decorator パターンにおける「機能を追加する」ということは、 クラスにメソッドを追加する(インターフェースを拡張する)ということではなく、 あるメソッドの内容に機能を追加するという意味です。

機能を追加したくてメソッドを追加する目的にはこのパターンは使えません。

動的に機能の拡張ができることがミソなパターンですから、
  1.デコレーション(拡張)されたモノを、元のモノと同じに扱いたい
  2.実行時に柔軟にデコレーションしたい
の両方が必要なとき(特に2.を欲するとき)にこのパターンを使えばよいでしょう。
1.だけならば継承してメソッドオーバーライドでも構わないです。

< 前のページへ

Pagetop