デザインパターン第9回「Decoratorパターン」
お久しぶりの、独りよがりなデザパタ講座です。
今回はGoFデザインパターンより、「Decoratorパターン」をぬるーく解説します。
Decoratorパターンは、実質的に継承と同じ機能を実現します。
ただ、継承に比べて結合度が低い分、このパターンでは柔軟な機能拡張が期待できます。
このパターンは、処理の委譲を利用してオブジェクト間の結合度を低くすることを目的とするので、
オブジェクト指向技術者の間では、あまり意識することなく自然と使われているケースの方が多いかもしれません。
もしあなたが、機能の拡張について「継承」だけに頼っているのであれば、
このパターンを理解することで、より柔軟性のある機能拡張について学ぶことができるだろう。
■Decoratorパターンの概要■
Decoratorとは、「装飾」を意味する言葉です。
このパターンは、委譲を活用して振る舞いを拡張(装飾)していくことができるパターンです。
拡張部分をクラスとして定義し、再帰的な構造を作ることで、核となる振る舞いを拡張することができます。
Decoratorパターンは、動的にオブジェクトに機能を追加することに注目したパターンです。
他のオブジェクトに依存することなく、オブジェクトに付加的な機能を動的に追加できるので、
継承による機能の拡張よりも、柔軟な拡張方法を提供します。
Decoratorパターンの特徴
クラスの持つ機能を拡張する方法としては、一般的に「継承」と「委譲」の2つの方法があります。
オブジェクト指向技術を学び始めたての頃は、この継承による機能の拡張を多用しがちです。
しかし、この継承を利用した機能の拡張では、拡張の順序によって、
クラス同士に不要な従属関係が生じてしまうという問題に悩まされることがあります。
しかし、Decoratorパターンを利用した拡張方法では、
拡張の組み合わせや順序が動的に自由に決められるので、その問題を解決してくれます。
また、継承による機能の拡張はコンパイル時に機能を拡張するのに対し、
Decoratorパターンによる機能の拡張はプログラムの実行時に機能を拡張できるので、
動的に機能を変更することが出来る点で、2つの拡張方法は大きく異なります。
Decoratorパターンの実装について
Decoratorパターンの方針は、既存のオブジェクトに新しい Decorator オブジェクトをラップすることである。その方法として、Decorator のコンストラクタの引数でラップ対象の Component オブジェクトを読み込み、
コンストラクタの内部でそのオブジェクトをメンバに設定することが一般的である。
そして共通のDecoratorインターフェイスで定義されたメソッド内で、
ラップ対象の共通インターフェイスで定義された同じメソッドを呼び出します。
また、ラップしているオブジェクト自身もDecoratorインターフェイスを実装しているため、
自身もDecoratorパターンにおけるラップ対象となることができます。
Decoratorパターンの利用場面
Decoratorパターンを利用する場面は、機能の拡張という利用だけにとどまらず、動的にフィルタリングを行う様なプログラムを作成する場合にも有効だろう。
各々のフィルタを、Decorator抽象クラスの具象クラスとして作成することで、
必要なフィルターを動的に重ねて利用することができるので、
Decoratorパターンの考え方を用いることを検討したほうがよい。
Decoratorパターンの拡張性の問題点
Decoratorのインターフェイスは、それが装飾するComponentのインターフェイスと一致していなければならない。したがって、ConcreteDecoratorクラスは1つの共通なインターフェイスを持つ必要があり、
あらかじめ決められたインターフェイス以外のオペレーションを
ComponentとDecorator にあとから追加することは容易ではないという問題がある。
インターフェイスの最大化が起こらない分、インターフェイスによる制約があると言えるだろう。
Decoratorパターンの問題点
Decoratorパターンの主たる問題点として、クラス数の増大がある。このパターンを利用して設計したシステムは、同じような多くの小さなオブジェクトから構成される。
それらのオブジェクトは相互に関連のしかたが異なるだけで、所属するクラスや変数の値は同じである。
このようなクラスが沢山あるとき、それを理解している人にとってはカスタマイズが容易だが、
それを知らない人が、学んだりデバッグしたりするのは困難になることがあるので注意が必要だ。
継承を使うべきか、Decoratorパターンを使うべきか
もし、 Componentの振る舞いを動的に拡張したり、複数の振る舞いを持つComponent をアプリケーション内で使う必要がないのであれば、
Decoratorパターンを用いずに、Componentそのものをクラス継承によって拡張する方がよい。
これは、GoFパターンの別解とされている。何でもかんでもパターンにはめれば良いというわけではない。
結構面倒くさかったけど作ったオマケ
Decoratorパターン - VB.NET - 雑なサンプルソース