Decoratorパターンは、オブジェクトの機能を動的に追加するためのデザインパターンです。
継承を用いることなく、既存のクラスに新しい機能を柔軟に追加できる点が特徴です。
この記事では、Decoratorパターンの概要、使い方、そしてJava、C++、C#、VB.NETの実装サンプルを通じて、このパターンの実践的な使い方を解説します。
Decoratorパターンとは
Decoratorパターンは、オブジェクトの動作を拡張するために使用される構造型デザインパターンです。
このパターンを使うことで、元のオブジェクトを変更せずに新しい機能を追加できます。
Decoratorは、元のオブジェクトをラップする形で新しい振る舞いを追加するため、インターフェースを変更することなく、クラスの振る舞いを強化することが可能です。
Decoratorパターンのメリット
Decoratorパターンの最大のメリットは、オブジェクトの振る舞いを柔軟に変更できる点にあります。
継承を使わずに機能を追加できるため、クラス階層が複雑になるのを防ぎ、動的な機能拡張が可能です。
また、異なるDecorator同士を組み合わせることで、多様な機能を持つオブジェクトを作成できます。
Decoratorパターンのデメリット
一方で、Decoratorパターンにはデメリットもあります。
複数のDecoratorが組み合わさることで、コードが複雑化する可能性があり、トレースが難しくなることもあります。
また、デコレータの多用はパフォーマンスに悪影響を与える場合があります。
Decoratorパターンの使い方
Decoratorパターンを使うには、基本的な構造を理解することが重要です。
Decoratorは、元のオブジェクトと同じインターフェースを実装し、元のオブジェクトを保持します。
その上で、新しい振る舞いを追加し、元のオブジェクトに委譲することで、機能を拡張します。
実装の基本構造
Decoratorパターンは、以下のような要素で構成されます:
- コンポーネント (Component): 拡張可能なオブジェクトのインターフェース。
- 具体的なコンポーネント (Concrete Component): 元の機能を持つオブジェクト。
- デコレータ (Decorator): 元のオブジェクトをラップし、新しい機能を追加するオブジェクト。
Decoratorパターン実装サンプル
次に、Java、C++、C#、VB.NETでの実装例を紹介します。
各言語でのDecoratorパターンの使い方を理解しやすくするために、基本的な装飾機能の例を挙げます。
Javaの実装例
interface Component {
void operation();
}
class ConcreteComponent implements Component {
public void operation() {
System.out.println("基本機能");
}
}
abstract class Decorator implements Component {
protected Component component;
public Decorator(Component component) {
this.component = component;
}
public void operation() {
component.operation();
}
}
class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
public void operation() {
super.operation();
System.out.println("機能A追加");
}
}
class ConcreteDecoratorB extends Decorator {
public ConcreteDecoratorB(Component component) {
super(component);
}
public void operation() {
super.operation();
System.out.println("機能B追加");
}
}
public class DecoratorPattern {
public static void main(String[] args) {
Component component = new ConcreteComponent();
Component decoratorA = new ConcreteDecoratorA(component);
Component decoratorB = new ConcreteDecoratorB(decoratorA);
decoratorB.operation();
}
}
C++の実装例
#include
class Component {
public:
virtual void operation() const = 0;
};
class ConcreteComponent : public Component {
public:
void operation() const override {
std::cout << "基本機能" << std::endl;
}
};
class Decorator : public Component {
protected:
const Component* component;
public:
Decorator(const Component* c) : component(c) {}
void operation() const override {
component->operation();
}
};
class ConcreteDecoratorA : public Decorator {
public:
ConcreteDecoratorA(const Component* c) : Decorator(c) {}
void operation() const override {
Decorator::operation();
std::cout << "機能A追加" << std::endl;
}
};
class ConcreteDecoratorB : public Decorator {
public:
ConcreteDecoratorB(const Component* c) : Decorator(c) {}
void operation() const override {
Decorator::operation();
std::cout << "機能B追加" << std::endl;
}
};
int main() {
Component* component = new ConcreteComponent();
Component* decoratorA = new ConcreteDecoratorA(component);
Component* decoratorB = new ConcreteDecoratorB(decoratorA);
decoratorB->operation();
delete component;
delete decoratorA;
delete decoratorB;
return 0;
}
C#の実装例
using System;
public interface Component {
void Operation();
}
public class ConcreteComponent : Component {
public void Operation() {
Console.WriteLine("基本機能");
}
}
public abstract class Decorator : Component {
protected Component component;
public Decorator(Component component) {
this.component = component;
}
public virtual void Operation() {
component.Operation();
}
}
public class ConcreteDecoratorA : Decorator {
public ConcreteDecoratorA(Component component) : base(component) { }
public override void Operation() {
base.Operation();
Console.WriteLine("機能A追加");
}
}
public class ConcreteDecoratorB : Decorator {
public ConcreteDecoratorB(Component component) : base(component) { }
public override void Operation() {
base.Operation();
Console.WriteLine("機能B追加");
}
}
class Program {
static void Main() {
Component component = new ConcreteComponent();
Component decoratorA = new ConcreteDecoratorA(component);
Component decoratorB = new ConcreteDecoratorB(decoratorA);
decoratorB.Operation();
}
}
VB.NETの実装例
Public Interface Component
Sub Operation()
End Interface
Public Class ConcreteComponent
Implements Component
Public Sub Operation() Implements Component.Operation
Console.WriteLine("基本機能")
End Sub
End Class
Public MustInherit Class Decorator
Implements Component
Protected component As Component
Public Sub New(ByVal component As Component)
Me.component = component
End Sub
Public Overridable Sub Operation() Implements Component.Operation
component.Operation()
End Sub
End Class
Public Class ConcreteDecoratorA
Inherits Decorator
Public Sub New(ByVal component As Component)
MyBase.New(component)
End Sub
Public Overrides Sub Operation()
MyBase.Operation()
Console.WriteLine("機能A追加")
End Sub
End Class
Public Class ConcreteDecoratorB
Inherits Decorator
Public Sub New(ByVal component As Component)
MyBase.New(component)
End Sub
Public Overrides Sub Operation()
MyBase.Operation()
Console.WriteLine("機能B追加")
End Sub
End Class
Module Program
Sub Main()
Dim component As Component = New ConcreteComponent()
Dim decoratorA As Component = New ConcreteDecoratorA(component)
Dim decoratorB As Component = New ConcreteDecoratorB(decoratorA)
decoratorB.Operation()
End Sub
End Module
Decoratorパターンの応用例
Decoratorパターンは、GUIシステムやストリーム処理など、さまざまな分野で応用されています。
例えば、JavaのI/Oクラスでは、InputStreamやOutputStreamがDecoratorパターンに基づいて設計されており、BufferedInputStreamやDataInputStreamなどで機能を拡張することができます。
以下はその簡単な例です。
import java.io.*;
public class DecoratorPatternExample {
public static void main(String[] args) {
try {
InputStream inputStream = new FileInputStream("example.txt");
InputStream bufferedStream = new BufferedInputStream(inputStream);
int data = bufferedStream.read();
while (data != -1) {
System.out.print((char) data);
data = bufferedStream.read();
}
bufferedStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
まとめ
Decoratorパターンは、オブジェクトの機能を柔軟に追加・拡張するための強力なデザインパターンです。
継承を使わずに機能を動的に拡張できるため、オブジェクト指向のプログラミングにおいて非常に有用です。
このパターンを正しく理解し、適切に適用することで、コードの柔軟性と再利用性を向上させることができます。
今回紹介したJava、C++、C#、VB.NETでの実装例を参考に、ぜひ自分のプロジェクトでもDecoratorパターンを活用してみてください。