デザインパターンまとめ
デザインパターンとは
- オブジェクト指向言語において、オブジェクト同士の再利用性や変更容易性をあげ、生産性を向上させていくもの
Itaratorパターン
概要
- Listオブジェクトの要素に順番にアクセスする処理の一般化したもの
参考:https://www.techscore.com/tech/DesignPattern/Iterator/Iterator2
なぜIteratorパターンを実装するのか
- ConcreateAggregateクラスが扱う構造体の内部構造を気にしなくても良いから
- 構造体を扱うクラスはConcreteIteratorクラス
Adapterパターン
概要
- 既存のクラスのインターフェースを変換して、別のインターフェースに適合させる
Adapterクラス
Adapterクラスは、Target(ターゲット)というインターフェースを実装し、その内部にAdaptee(適合される側のクラス)のインスタンスを保持します。Adapterクラスは、Targetインターフェースを実装することで、クライアント側からはTargetとして振る舞うことができます。
public class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
// Adapteeのメソッドを呼び出して、Targetのメソッドとして振る舞う
adaptee.specificRequest();
}
}
Adapteeクラス
Adapteeクラスは、既存のクラスであり、Targetとして必要な機能を持っているわけではありません。このクラスをそのままでは使えない場合、Adapterパターンを使用してTargetとして利用できるようにします。
public class Adaptee {
public void specificRequest() {
System.out.println("Adapteeの特定の処理が実行されました。");
}
}
Adapterクラスの利用
Adapterクラスを使うことで、AdapteeクラスをTargetとして利用することができます。
public class Client {
public static void main(String[] args) {
// Adapteeクラスのインスタンスを生成
Adaptee adaptee = new Adaptee();
// Adapterクラスを介してAdapteeをTargetとして利用
Target target = new Adapter(adaptee);
target.request();
}
}
継承を利用したパターン
移譲を利用したパターン
参考:https://www.techscore.com/tech/DesignPattern/Adapter/Adapter2
利用用途としては
- Adapteeクラスの振る舞いを変更しないままClientの要望に対応できること
- Adapteeクラスを使用している他クラスの実装を変更しなくても良いこと
かなと個人的には思う
TemplateMethodパターン
概要
アルゴリズムの骨格を定義し、一部のステップをサブクラスに委任するためのパターン
参考:https://www.techscore.com/tech/DesignPattern/TemplateMethod
abstract class AbstractClass {
// テンプレートメソッド
public void templateMethod() {
step1();
step2();
step3();
}
// 抽象メソッド
abstract void step1();
abstract void step2();
// 具象メソッド
void step3() {
System.out.println("AbstractClass: Performing Step 3");
}
}
class ConcreteClass extends AbstractClass {
@Override
void step1() {
System.out.println("ConcreteClass: Performing Step 1");
}
@Override
void step2() {
System.out.println("ConcreteClass: Performing Step 2");
}
}
public class Main {
public static void main(String[] args) {
AbstractClass abstractClass = new ConcreteClass();
abstractClass.templateMethod();
}
}
利用用途としては
- 処理ステップの大枠がある程度決まっているもの(ドキュメント生成など)
- 継承を利用して柔軟性を持たせたい場合
FactoryMethod パターン
概要
- インスタンスの生成をサブクラスに行わせることで柔軟性と拡張性に優れた設計にすることができる
下記の役割がある
-
Creator(生成者):
Creatorは、生成されるオブジェクトのインスタンスを生成するための抽象クラスまたはインターフェースです。
生成するオブジェクトの型に依存しない共通のメソッドが含まれます。これがFactory Methodとして知られるメソッドです。 -
ConcreteCreator(具象生成者):
ConcreteCreatorは、具体的な生成処理を実装します。つまり、Creatorで定義されたFactory Methodをオーバーライドして、具体的なオブジェクトのインスタンス化を行います。 -
Product(生成物):
Productは、生成されるオブジェクトを表す抽象クラスまたはインターフェースです。
Creatorによって生成されるオブジェクトの型を定義します。 -
ConcreteProduct(具象生成物):
ConcreteProductは、具体的な生成物(具体的なクラスのインスタンス)を表します。
ConcreteCreatorによって生成され、ConcreteProductのインスタンスが生成されます。
参考:https://www.techscore.com/tech/DesignPattern/FactoryMethod
Prototype パターン
概要
指定したインスタンスの複製を行うことができる
下記の役割がある
- Prototypeは、クローンを作成するためのインターフェースまたは抽象クラスです。
clone() メソッドを定義し、オブジェクトの浅いまたは深いコピーを提供します。 - ConcretePrototype(具象プロトタイプ):
ConcretePrototypeは、実際のオブジェクトを表します。
clone() メソッドを実装し、自身のコピーを作成します。 - Client(クライアント):
Clientは、新しいオブジェクトを生成するためにPrototypeを利用します。
新しいオブジェクトの作成方法は、既存のオブジェクトをCloneすることで行います。
パターンの利点
Prototypeパターンの主な利点は次のとおりです:
- 新しいオブジェクトの作成: Prototypeパターンは、新しいオブジェクトの生成を容易にします。既存のオブジェクトをCloneするだけで、新しいオブジェクトが得られます。
- インスタンス化のコスト削減: 新しいオブジェクトを生成するためにインスタンス化のコストがかかる場合、Prototypeパターンはそのコストを削減します。
- 柔軟性の向上: 新しいオブジェクトの作成方法がオブジェクト自体に委ねられるため、具象クラスの変更がなく、柔軟性が向上します。
使い所
- インスタンス生成に時間がかかる場合で、インスタンスを複製したい場合
Builderパターン
概要
Builderパターンは、オブジェクトの生成過程を分解し、複雑なオブジェクトの構築をより制御可能にするためのデザインパターンです。このパターンを使用すると、異なる構成のオブジェクトを簡単に生成することができ、オブジェクトの生成過程をステップごとに進めることができます。Builderパターンは、特にオブジェクトの構築が多段階にわたる場合や、オブジェクトが多数の構成オプションを持つ場合に有用です。
パターンの利点
- 可読性の向上:
複雑なオブジェクトの生成コードが明確になり、可読性が高まります。 - 変更に対する柔軟性:
新しいバリエーションのオブジェクトを簡単に作成できるため、要件の変更に柔軟に対応できます。 - 一貫性の確保:
オブジェクト生成のプロセスが一元管理されるため、一貫性を持ったオブジェクトを生成することができます。 - 構築過程の分離:
オブジェクトの構築とその内部表現が分離されるため、構築過程をカスタマイズすることが容易になります。 - 再利用性:
同じ構築プロセスを利用して異なるオブジェクトを生成できるため、コードの再利用性が向上します。
使い所
- 複雑なオブジェクトの生成:
多数のプロパティを持つオブジェクトや、生成に多くのステップを必要とするオブジェクトの生成に適しています。 - 同じ生成プロセスで異なるオブジェクトを生成する場合:
例えば、同じ手順で異なる種類の製品を作成する際に有効です。 - 不可変オブジェクトの生成:
一度作成された後に変更されないオブジェクトの生成に適しています。 - 複数のコンストラクタが必要な場合:
パラメータの組み合わせが多岐にわたり、複数のコンストラクタを持つことが難しい場合に有効です。 - デフォルト値やオプション設定が多い場合:
オプション設定が多く、特定の設定が必須ではない場合に、必要な部分だけを設定してオブジェクトを生成できます。
AbstractFactory パターン
概要
Abstract Factory パターンは、関連する一連のオブジェクトを生成するためのインターフェースを提供するデザインパターンです。具体的なクラスを指定せずに、一貫性のあるオブジェクトのファミリーを作成することができます。このパターンは、Factory Method パターンの拡張版と見ることができます。
利点
-
一貫性の保証
互いに関連する製品が常に一貫した方法で生成されるため、システム全体での整合性が保たれます。 -
可読性と保守性の向上
生成ロジックを一箇所に集中させることで、コードの理解や保守が容易になります。 -
拡張性
新しい製品ファミリーを追加する場合、既存のクライアントコードに影響を与えることなく、具体ファクトリーと製品を追加するだけで対応できます。 -
疎結合の実現
クライアントコードは具体的なクラスに依存せず、抽象インターフェースに依存するため、クラス間の結合度が低くなります。
使い所
製品ファミリーを一貫して使用したい場合
例えば、GUIアプリケーションで異なるスタイルのウィジェット(ボタン、テキストボックスなど)を一貫して使用したい場合に有効です。
異なる実装を切り替える必要がある場合
データベース接続やファイルシステム操作など、異なるプラットフォームや環境に応じて実装を切り替える必要がある場合。
依存性の注入を行う場合
抽象ファクトリーを利用して依存性の注入を行い、クライアントコードのテストやモック化を容易にすることができます。
Bridgeパターン
概要
Bridgeパターンは、抽象化と実装を分離し、それぞれを独立して変更できるようにするデザインパターンです。これにより、システムの柔軟性と拡張性が向上します。
利点
独立した拡張性:
抽象化と実装を独立して拡張できる。
クラスの爆発的増加の抑制:
組み合わせによるクラスの増加を防ぐ。
変更に強い:
実装を変更しても、抽象化に影響を与えない。
モジュール性の向上:
コードの分離により、管理しやすくなる。
使い所
- 多様な機能を持つクラス階層:
- 様々な機能を持つクラスが、異なる実装を持つ場合。
- プラットフォームやデバイス依存の実装:
- プラットフォームに依存する機能を持つシステムで、異なるプラットフォーム向けに実装を切り替えたい場合。
- ランタイムでの実装の切り替え:
- 実行時に実装を切り替える必要がある場合。
- Bridgeパターンを使用することで、柔軟で拡張可能なコードベースを構築できます。
// Implementor interface
interface SortImpl {
sort(data: number[]): number[];
}
// Concrete Implementor 1 - Bubble Sort
class BubbleSort implements SortImpl {
sort(data: number[]): number[] {
const arr = [...data];
for (let i = 0; i < arr.length - 1; i++) {
for (let j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
}
}
}
return arr;
}
}
// Concrete Implementor 2 - Quick Sort
class QuickSort implements SortImpl {
sort(data: number[]): number[] {
if (data.length < 2) return data;
const pivot = data[0];
const left = data.slice(1).filter(item => item <= pivot);
const right = data.slice(1).filter(item => item > pivot);
return [...this.sort(left), pivot, ...this.sort(right)];
}
}
// Abstraction
class Sorter {
private impl: SortImpl;
constructor(impl: SortImpl) {
this.impl = impl;
}
sort(data: number[]): number[] {
return this.impl.sort(data);
}
}
// Client code
const data = [5, 3, 8, 4, 2];
const bubbleSorter = new Sorter(new BubbleSort());
console.log("Bubble Sort:", bubbleSorter.sort(data));
const quickSorter = new Sorter(new QuickSort());
console.log("Quick Sort:", quickSorter.sort(data));
Strategyパターン
概要
ストラテジーパターン(Strategy Pattern)は、アルゴリズムをクラスとして分離し、クライアントのコードから独立して変更や選択ができるようにするデザインパターンです。これは、特定の動作(戦略)をクラスとしてカプセル化し、動的に切り替えることを可能にします。
利点
- アルゴリズムの独立した変更:
アルゴリズムを独立したクラスとして分離することで、他の部分に影響を与えずに変更できる。 - コードの再利用:
同じアルゴリズムを複数のコンテキストで再利用可能。 - クラスの責任の分離:
異なるアルゴリズムを持つクラスの責任が分離され、コードが整理される。 - 動的なアルゴリズムの切り替え:
実行時に異なるアルゴリズムを選択できる。
使い所 - アルゴリズムの切り替え:
異なるアルゴリズムを実行時に切り替えたい場合。例えば、異なるソートアルゴリズムや異なる圧縮アルゴリズムなど。 - 複雑な条件分岐の整理:
複数のアルゴリズムや動作を条件分岐で管理している場合、コードの見通しを良くするために使用。 - 異なる振る舞いのカスタマイズ:
ユーザーの設定や環境に応じて異なる振る舞いを提供する場合。
Composite パターン
概要
Composite パターンは、個々のオブジェクトとオブジェクトの集約を同一視できるようにするデザインパターンです。これにより、クライアントは個々のオブジェクトと集約(ツリー構造)を同じ方法で扱うことができます。
構成要素
- Component:
オブジェクトの共通インターフェースを定義します。ここに共通の操作を宣言します。
Leaf:
実際のオブジェクトを表し、子オブジェクトを持たないコンポーネントです。Component インターフェースを実装します。 - Composite:
子オブジェクトを持つコンポーネントです。子オブジェクトを管理し、Component インターフェースを実装します。
利点 - 一貫した処理:個々のオブジェクトとオブジェクトの集約を同様に扱えるため、クライアントコードがシンプルになります。
- 柔軟性:オブジェクトの構造を動的に変更できます。新しい種類のコンポーネントを追加しても、既存のコードに影響を与えません。
- 再帰的な構造:ツリー構造を再帰的に処理できるため、複雑な構造を簡潔に管理できます。
使い所 - 階層構造の表現:ファイルシステムのディレクトリ構造、組織の階層、GUIコンポーネントの階層など。
- 再帰的な処理が必要な場合:ツリー構造を再帰的に処理する必要がある場合。
- 一貫した操作が求められる場合:個々の要素とその集合に対して同一の操作を適用したい場合。
Decorator パターン
概要
Decoratorパターンは、オブジェクトに動的に機能を追加するデザインパターンです。これにより、サブクラスを作成することなく、既存のクラスに新しい機能を追加できます。
構成要素
Component
- 基本機能を定義する共通インターフェースまたは抽象クラス。
ConcreteComponent - 基本機能を実装するクラス。
Decorator - Componentインターフェースを実装し、ConcreteComponentのインスタンスを保持する抽象クラス。
- 基本機能の前後に追加機能を実装するためのベースとなる。
ConcreteDecorator - Decoratorを拡張し、追加機能を実装するクラス。
利点
動的な機能追加
- 実行時にオブジェクトに機能を追加できるため、柔軟性が高い。
クラスの爆発的増加の抑制 - サブクラスを大量に作成することなく、さまざまな機能を組み合わせることができる。
シングル責任の原則の遵守 - 各Decoratorクラスは1つの新しい機能に集中できるため、コードの理解と保守が容易。
使い所
機能の拡張
- 既存のクラスに対して、クラスの修正やサブクラスの作成なしで機能を追加したい場合。
柔軟な機能追加 - 実行時にオブジェクトに異なる機能を組み合わせて追加する必要がある場合。
オブジェクトの構成 - オブジェクトの構成を変更して新しい振る舞いを持たせたい場合。
Visitor パターン
概要
Visitorパターンは、オブジェクトの構造と操作を分離するデザインパターンです。異なる型のオブジェクトに対して、操作(メソッド)を追加する際に使用され、オブジェクトのクラスを変更することなく新しい操作を追加できます。
構成要素
Visitor:
- オブジェクト構造内の各要素に対して操作を実行するインターフェースまたは抽象クラスを定義します。
ConcreteVisitor: - Visitorインターフェースを実装し、具体的な操作を定義します。各要素に対する処理をそれぞれ実装します。
Element: - accept メソッドを持つインターフェースまたは抽象クラスを定義します。このメソッドは、Visitor を引数に取り、訪問者を受け入れる役割を持ちます。
ConcreteElement: - Elementインターフェースを実装する具体的なクラスです。このクラスは自分自身を Visitor に引き渡し、適切な操作を実行させます。
Object Structure: - Element オブジェクトを保持し、Visitor を適用する役割を持ちます。
利点
新しい操作の追加が容易:
- クラスを変更することなく、新しい操作を追加できます。
要素ごとの操作を一元化: - すべての要素に対する操作が Visitor クラスに集約されるため、コードが整理されます。
複雑な構造の扱い: - 複雑なオブジェクト構造に対しても、各要素への処理を一元管理できる。
使い所
異なる処理を追加したいが、要素クラスを変更できない場合:
- 要素クラスの変更が難しい場合や、変更を避けたい場合に役立ちます。
複雑なオブジェクト構造への処理を集中管理したい場合: - 複数のオブジェクトタイプが混在する構造に対して、一括で処理を追加したい場合に有効です。
要素のクラスが頻繁に変更されない場合: - 要素クラスが安定していて、新しい操作が頻繁に追加される場合に適しています。
参考:
Chain of Responsibility パターン
概要
Chain of Responsibilityパターンは、リクエストを処理する一連のハンドラーをチェーン(鎖)のように繋げ、各ハンドラーがリクエストを処理するか、次のハンドラーに処理を委ねるデザインパターンです。これにより、複数のハンドラーが協力して1つのリクエストを処理できます。
構成要素
Handler:
- リクエストを処理するための共通インターフェースや抽象クラスを定義します。次のハンドラーへの参照を持ちます。
ConcreteHandler:
-
Handler
を実装する具体的なクラスで、特定のリクエストを処理します。処理できない場合は、次のハンドラーにリクエストを渡します。
Client:
- リクエストを最初のハンドラーに送信し、処理が始まるトリガーを提供します。
利点
責任の分散:
- リクエストの処理が1つのハンドラーに集中せず、複数のハンドラーに分散されます。
ハンドラーの追加が容易:
- 新しいハンドラーを追加する際、既存のコードに大きな変更を加えることなく、チェーンに追加できます。
柔軟な処理フロー:
- リクエストの処理順序や条件を柔軟に設定でき、フローを簡単に変更可能です。
使い所
リクエストを段階的に処理する場合:
- リクエストが複数の段階を経て処理される場合や、特定の条件に応じて処理が分岐する場合に有効です。
複数の責任を分散させたい場合:
- 各処理を特定のクラスに任せたい場合や、処理の分岐が複雑な場合に役立ちます。
動的にハンドラーを組み替えたい場合:
- ハンドラーの順序や種類を動的に変更する必要がある場合に適しています。
Facade パターン
概要
Facadeパターンは、複雑なサブシステムへのシンプルなインターフェースを提供するデザインパターンです。サブシステム内の複数のクラスやメソッドが連携して処理を行う際、Facadeパターンはこれらをまとめて、より簡単で使いやすいインターフェースを提供します。これにより、クライアントはサブシステムの複雑さを意識せずに操作できるようになります。
構成要素
Facade:
- 複数のクラスやサブシステムに対するシンプルなインターフェースを提供します。クライアントはFacadeを通じてサブシステムの機能を利用します。
Subsystem Classes:
- 実際のビジネスロジックや処理を行う複数のクラスです。これらは直接クライアントに公開されることはなく、Facadeを通じて操作されます。
Client:
- Facadeを利用して、サブシステムの機能を操作する役割を持ちます。
利点
シンプルなインターフェース:
- 複雑なシステムを使いやすくするシンプルなインターフェースを提供します。
クライアントとサブシステムの分離:
- クライアントはサブシステムの内部構造に依存しないため、サブシステムの変更がクライアントに影響を与えにくくなります。
サブシステムのカプセル化:
- サブシステムの詳細が隠蔽され、クライアントから見えなくなります。
使い所
複雑なサブシステムを簡単に使いたい場合:
- 複数のクラスやメソッドが連携しているシステムを、単純な操作で利用したい場合に有効です。
サブシステムの構造を隠したい場合:
- クライアントからサブシステムの複雑な内部構造を隠蔽し、単純化されたインターフェースを提供したい場合に使用されます。
サブシステムが頻繁に変更される場合:
- サブシステムの内部構造が変更されても、クライアントへの影響を最小限に抑えたい場合に有効です。
Mediator パターン
概要
Mediatorパターンは、オブジェクト間の複雑な相互作用を**仲介者(Mediator)**によって管理するデザインパターンです。各オブジェクトは直接やり取りするのではなく、Mediatorを介して通信します。これにより、オブジェクト間の依存関係を減らし、システムの保守性と拡張性が向上します。
構成要素
Mediator:
- オブジェクト間のコミュニケーションを調整するためのインターフェースや抽象クラスを定義します。
ConcreteMediator:
-
Mediator
インターフェースを実装し、具体的なコミュニケーション方法を定義します。各オブジェクトからのメッセージを受け取り、それを他のオブジェクトに適切に転送します。
Colleague:
- Mediatorとやり取りするオブジェクトの抽象クラスやインターフェースです。具体的なオブジェクトは、このクラスを継承して実装されます。
ConcreteColleague:
-
Colleague
を実装する具体的なオブジェクトで、Mediatorを介して他のオブジェクトと通信します。
利点
依存関係の削減:
- オブジェクト同士が直接依存しないため、依存関係が減少し、システムの変更が容易になります。
可読性と保守性の向上:
- オブジェクト間の複雑な相互作用がMediatorに集約されるため、コードの可読性と保守性が向上します。
再利用性の向上:
- 個々のオブジェクトは特定の相手に依存しないため、他のシステムでも再利用しやすくなります。
使い所
複雑なオブジェクト間の相互作用を整理したい場合:
- 多数のオブジェクトが相互にやり取りする際、Mediatorを使用してそのやり取りを整理・管理できます。
依存関係を最小限に抑えたい場合:
- 各オブジェクトが他のオブジェクトに直接依存しないようにすることで、システムの変更や拡張を容易にしたい場合に適しています。
一元管理を行いたい場合:
- システム全体の通信を一元的に管理したい場合に有効です。
参考
facadeパターンとの違い:https://blog.ecbeing.tech/entry/2020/10/29/155503
Observer パターン
概要
Observerパターンは、オブジェクト間の1対多の依存関係を定義するデザインパターンです。1つのオブジェクト(Subject)の状態が変わったときに、そのオブジェクトに依存している複数のオブジェクト(Observer)に自動的に通知される仕組みを提供します。これにより、状態の変更を監視し、反応する柔軟なシステムを構築できます。
構成要素
Subject:
- 状態を持つオブジェクトで、Observerたちに通知を送る役割を持ちます。Observerの登録・削除、通知の機能を持ちます。
Observer:
- Subjectの状態を監視し、変更があった際に更新されるオブジェクトです。
ConcreteSubject:
- 状態を持つ具体的なクラスで、Observerに対する通知のトリガーを実装します。
ConcreteObserver:
- Subjectから通知を受け取り、更新を行う具体的なオブジェクトです。
利点
疎結合:
- SubjectとObserverが疎結合になるため、各オブジェクトが独立して動作し、保守や拡張が容易になります。
動的な通知管理:
- Observerの登録・削除が動的に行えるため、柔軟なシステム設計が可能です。
自動通知:
- 状態の変化に応じて自動的に通知が行われるため、Observerが手動で状態を監視する必要がなくなります。
使い所
システムの状態が変化するたびに複数のオブジェクトに通知したい場合:
- 例えば、GUIアプリケーションでボタンが押されたときに複数の要素を更新するなど、状態の変化に反応して他のオブジェクトを更新する必要がある場合に有効です。
オブジェクトの状態変化に応じて自動的に処理を行いたい場合:
- データが更新されたときにリアルタイムでビューを更新する場合や、イベントドリブンなシステムなどで使用されます。
複数のオブジェクトが1つのオブジェクトに依存する場合:
- 複数のObserverが1つのSubjectに依存し、その状態変化を追跡する場合に最適です。
Memento パターン
概要
Mementoパターンは、オブジェクトの内部状態を保存し、後でその状態を復元できるようにするデザインパターンです。このパターンを使用すると、オブジェクトの現在の状態を外部に公開することなく、その状態を保存したり、巻き戻したりできます。例えば、アプリケーションの「元に戻す (Undo)」機能の実装に役立ちます。
構成要素
Originator:
- 内部状態を持つオブジェクトで、現在の状態を
Memento
として保存したり、保存された状態から復元したりします。
Memento:
-
Originator
の内部状態を保持するクラスです。このクラスは、外部には状態にアクセスさせず、復元する際にだけOriginator
に使用されます。
Caretaker:
-
Memento
の保存と管理を担当するオブジェクトで、内部状態を直接操作することはありません。通常は、状態のスナップショットを記憶し、巻き戻しややり直しを管理します。
利点
カプセル化の維持:
-
Memento
を使うことで、Originator
の内部状態を他のオブジェクトに公開することなく保存・復元できるため、カプセル化が維持されます。
Undo機能の実現:
- 状態のスナップショットを保存することで、アプリケーションに元に戻す機能(Undo)や、過去の状態を参照する機能を実現できます。
安全な状態管理:
- 内部状態が外部から変更されることなく保存・復元が可能になるため、セキュリティや安定性が向上します。
使い所
履歴管理や元に戻す機能が必要なシステム:
- テキストエディタやゲームなど、ユーザーが操作を「元に戻す」「やり直す」必要があるシステムに適しています。
オブジェクトの状態を一時的に保存し、後で戻したい場合:
- オブジェクトの変更を一時的に保存し、必要に応じてその状態を復元したい場合に使用されます。
オブジェクトの内部状態を外部に公開せずに管理したい場合:
- 外部から状態を見えなくしたまま、オブジェクトの状態管理を行う必要があるシステムに役立ちます。
State パターン
概要
Stateパターンは、オブジェクトの状態に応じて異なる振る舞いを実行できるデザインパターンです。オブジェクトの内部状態が変わることで、そのオブジェクトの動作を動的に変更できるようにします。各状態を個別のクラスで表現し、状態遷移を管理します。これにより、状態ごとに異なる処理を持たせることが可能となり、状態ごとの処理ロジックが分離されます。
構成要素
Context:
- オブジェクトの現在の状態を保持し、状態に応じて動作を変更する役割を持つクラスです。現在の状態オブジェクトを保持し、その状態に基づいて振る舞いを委譲します。
State:
- 状態ごとの振る舞いを定義するインターフェースまたは抽象クラスです。具体的な状態クラスはこのインターフェースを実装し、異なる動作を実装します。
ConcreteState:
- Stateインターフェースを実装する具体的な状態クラスです。各状態ごとに異なる振る舞いを実装します。
Context
への状態遷移の指示も行います。
利点
状態ごとの振る舞いを明確に分離:
- 各状態が独立したクラスとして表現されるため、状態ごとの処理が明確に分離され、コードが読みやすく、保守しやすくなります。
状態遷移のロジックが整理される:
- 状態遷移が状態クラスに集約され、状態ごとに遷移条件が設定できるため、複雑な状態管理を整理できます。
条件分岐の削減:
-
if-else
やswitch
文による状態判定を避けることができ、コードの複雑さを低減できます。
使い所
オブジェクトが複数の状態を持ち、それに応じて異なる振る舞いをする場合:
- 例えば、トランザクションの進行状態(開始、処理中、完了)や、UIコンポーネントの状態(有効、無効、選択中)など、状態に応じて振る舞いが変わるシステムに適しています。
状態ごとの処理が複雑である場合:
- 各状態が異なる処理を持ち、それぞれが独立している場合に、Stateパターンを適用することで、コードの整理が可能です。
状態遷移が頻繁に行われる場合:
- 状態が頻繁に変わり、動的に動作を変更する必要がある場合に有効です。
Flyweight パターン
概要
Flyweightパターンは、大量のオブジェクトを効率的に共有してメモリ使用量を削減するためのデザインパターンです。オブジェクトの共通部分を共有し、重複を避けることで、メモリの使用量を最小限に抑えることができます。特に、多くのオブジェクトがほぼ同じ状態やデータを持っている場合に適しています。
Flyweightパターンでは、オブジェクトを内的状態 (Intrinsic State) と外的状態 (Extrinsic State) に分け、内的状態を共有することでメモリ効率を高めます。
構成要素
Flyweight:
- 共有可能な状態(内的状態)を持つインターフェースや抽象クラスです。具体的なクラスは、このインターフェースを実装し、オブジェクトの再利用を可能にします。
ConcreteFlyweight:
- Flyweightインターフェースを実装する具体的なクラスで、内的状態を持ち、共有されるオブジェクトです。
UnsharedConcreteFlyweight:
- Flyweightではないオブジェクトで、共有されない状態を持ちますが、他のFlyweightオブジェクトと一緒に使われることがあります。
FlyweightFactory:
- Flyweightオブジェクトを生成し、共有する役割を持つファクトリクラスです。既存のFlyweightオブジェクトを再利用し、新規作成を最小限に抑えます。
Client:
- Flyweightオブジェクトを利用し、外的状態を操作します。外的状態は共有されないデータで、Flyweightオブジェクトに渡されます。
利点
メモリ使用量の削減:
- 共通の部分を共有することで、大量のオブジェクトを生成する際のメモリ使用量を大幅に削減できます。
パフォーマンスの向上:
- 共有オブジェクトの利用により、システム全体のパフォーマンスを向上させることができます。
オブジェクト生成の効率化:
- 同じ状態を持つオブジェクトの生成を避けることで、無駄なインスタンス化を防ぎ、リソースの効率的な利用が可能です。
使い所
大量の似たようなオブジェクトが必要な場合:
- 例えば、ゲームで多数の同じ種類のキャラクターや、グラフィックエディタで多くの同じシンボルを描画する場合に適しています。
オブジェクトの共通部分を多く持つ場合:
- オブジェクトが大部分同じデータを共有し、異なる部分が少ない場合に、Flyweightパターンを使うことでメモリの節約ができます。
システムのパフォーマンスやメモリ使用量が重要な場合:
- リソースが限られている環境や、メモリ効率を最大化する必要があるシステムに有効です。
例
以下は、Flyweightパターンを使用してフォントの描画に関する例です。同じフォント情報を共有し、メモリ効率を上げています。
Proxy パターン
概要
Proxyパターンは、オブジェクトへのアクセスを制御するために代理オブジェクトを設置するデザインパターンです。本物のオブジェクト(RealSubject)に直接アクセスする代わりに、代理オブジェクト(Proxy)を通してアクセスすることで、アクセス制御や遅延初期化、リモートアクセス、キャッシュなどの処理を追加できます。
このパターンは**「本物のオブジェクトを置き換える代理」**として機能し、アクセスの管理や最適化、セキュリティ強化を目的としています。
構成要素
Subject:
- RealSubjectとProxyが共通して実装するインターフェースまたは抽象クラスで、共通のメソッドを定義します。
RealSubject:
- 実際の処理を行うクラスで、必要なメソッドの具体的な実装を提供します。クライアントが本来アクセスしたいオブジェクトです。
Proxy:
- RealSubjectと同じインターフェースを実装し、RealSubjectへのアクセスを管理する代理オブジェクトです。アクセス制御、ログ、キャッシュ、遅延初期化などの処理を追加できます。
利点
アクセス制御の強化:
- Proxyを使って、クライアントのアクセスを制御できます。例えば、アクセス権のあるクライアントのみ本物のオブジェクトにアクセスできるようにします。
パフォーマンスの最適化:
- リソースの重いオブジェクトの遅延初期化やキャッシュによって、システムのパフォーマンスを向上させることができます。
リモートオブジェクトの取り扱い:
- クライアントはProxyを通してリモートオブジェクトにアクセスし、ネットワークの違いを意識せずに利用できます。
使い所
遅延初期化が必要な場合:
- 大量のリソースを使用するオブジェクトを必要になるまで初期化しない「バーチャルプロキシ」に適しています。
リモートアクセスが必要な場合:
- ネットワーク越しにあるリモートオブジェクトにアクセスする際の「リモートプロキシ」として利用できます。
アクセス制御を行いたい場合:
- クライアントごとに異なる権限でアクセスを制限したい場合の「保護プロキシ」に適しています。
キャッシュを管理したい場合:
- キャッシュを通してオブジェクトにアクセスし、効率的にリソースを管理できます。
Command パターン
概要
Commandパターンは、操作(コマンド)をオブジェクトとして分離して扱うデザインパターンです。このパターンでは、各コマンド(操作)をオブジェクトとして抽象化し、リクエスト(コマンドの実行)とその実行者を分離します。こうすることで、コマンドの履歴管理、取り消し(Undo)、再実行(Redo)などが容易に実装できます。
構成要素
Command:
- コマンドを表すインターフェースまたは抽象クラスで、
execute
メソッドを定義します。具体的なコマンドを実行するクラス(ConcreteCommand)はこのインターフェースを実装します。
ConcreteCommand:
- Commandインターフェースを実装するクラスで、具体的な操作を実行するクラスです。このクラスは「レシーバー」を持ち、操作を委譲する形でリクエストを実行します。
Receiver:
- 実際の操作を行うオブジェクトで、ConcreteCommandはこのクラスに操作を委譲します。具体的な実行の詳細はこのクラスに含まれます。
Invoker:
- Commandを保持し、必要に応じて
execute
メソッドを呼び出すクラスです。Invokerが複数のCommandを管理することで、操作の順序制御やバッチ実行が可能になります。
Client:
- InvokerにConcreteCommandを設定し、Invokerにリクエストを送信する役割を持ちます。
利点
リクエストと実行者の分離:
- コマンドの実行とその呼び出し元を分離することで、柔軟な設計が可能になります。
Undo/Redoの実装が容易:
- コマンドオブジェクトの履歴を保持することで、操作の取り消しや再実行が簡単に実現できます。
新しいコマンドの追加が容易:
- Commandインターフェースを実装するクラスを追加するだけで新しい操作を追加できます。
使い所
操作の履歴管理が必要な場合:
- コマンドの履歴を保持し、UndoやRedo機能を実装する場面で利用されます。
操作のバッチ処理が必要な場合:
- 複数の操作を順番に実行したり、まとめて実行するシステムに適しています。
操作をキューに追加したり、非同期で実行する必要がある場合:
-
実行タイミングが異なる場合や、リクエストをキューに保存して後で処理する場合に有効です。
Interpreter パターン
概要
Interpreterパターンは、言語の文法や構文を表現し、その文法に従った評価(解釈)を行うデザインパターンです。このパターンは、簡単な言語や構文を処理するために、文字列やシンボルを解釈・評価する際に使用されます。構文規則を表すクラス階層を構築し、その構文に従って入力を解釈することができます。
構成要素
AbstractExpression(抽象式クラス):
- 式の共通インターフェースまたは抽象クラスです。
interpret
メソッドを定義し、具象クラスでその実装が提供されます。
TerminalExpression(終端式):
- 具体的な評価を行うクラスで、文法におけるリテラルや個々の値の解釈を行います。これは、再帰的に呼び出されることで複数の要素に対応できます。
NonTerminalExpression(非終端式):
- 文法規則に基づき、複数の式を組み合わせるクラスです。再帰的に終端式や他の非終端式を組み合わせて、複雑な構文の解釈を行います。
Context(文脈):
- 式の評価に必要な情報を保持します。解釈に使用される変数や値を保持し、
interpret
メソッド内で使用されます。
Client(クライアント):
- 文法を定義し、式を構成して評価を行います。クライアントは文法ルールを適用して式を構築し、その解釈を行います。
利点
柔軟な文法解釈:
- 文法をオブジェクトとして表現し、複雑な構文を柔軟に解釈できます。
拡張性:
- 新しい文法規則や式を追加する場合、既存のコードにほとんど影響を与えずに拡張できます。
使い所
簡易的な構文解析が必要な場合:
- 特定の構文やルールを解釈・評価する場面で使われます。例えば、数式計算や特定フォーマットのテキスト解析などです。
特定の構文に従うドメイン特化言語(DSL)を扱う場合:
- 小規模なスクリプトエンジンや設定ファイルの解釈、簡易的な問い合わせ言語を処理する際に適しています。
参考: https://www.techscore.com/tech/DesignPattern/Interpreter