デザインパターン入門
CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 90/104。
デザインパターンは、ソフトウェア設計において発生する一般的な問題に対する、実証済みの再利用可能な解決策です。これらは直接コピーするコードではなく、特定の課題をエレガントに解決するために、クラスやオブジェクトをどのように構成するかを記述したテンプレートや設計図です。
この概念は、1994年の著書で23の基本パターンをカタログ化した「Gang of Four」(GoF)によって普及しました。これらのパターンは通常、3つのカテゴリに分類されます。
| カテゴリ | 目的 | 例 |
|---|---|---|
| 生成 (Creational) | オブジェクト生成のメカニズム | Singleton, Factory, Builder |
| 構造 (Structural) | クラスとオブジェクトの構成 | Adapter, Decorator, Composite |
| 振る舞い (Behavioral) | オブジェクトの相互作用と責任 | Observer, Strategy, Command |
なぜデザインパターンを学ぶのでしょうか?それらは開発者間の共通言語を提供します。「ここでは Factory を使おう」と言うだけで、複雑なアイデアを即座に伝えることができます。また、解決策を再発明することを避け、コードをより柔軟で保守しやすくするのにも役立ちます。
C++では、デザインパターンはこれまでに学んだOOP(オブジェクト指向プログラミング)の機能、つまり継承、ポリモーフィズム、抽象クラス、およびテンプレートを活用します。これからのレッスンでは、いくつかの重要なパターンを実装し、それぞれがどのような場合に最適なツールとなるかを理解していきます。
チャレンジ
簡単3つのデザインパターンのカテゴリについての理解を示す、シンプルなパターン分類器(classifier)を作成しましょう。パターンの名前を受け取り、それがどのカテゴリ(Creational、Structural、または Behavioral)に属するかを特定し、そのカテゴリの目的の簡潔な説明とともに表示するシステムを構築します。
コードは以下の3つのファイルに分けて構成します:
PatternCategory.h: デザインパターンのカテゴリを表すPatternCategoryという抽象基底クラスを定義します。これには以下のものを含める必要があります:- カテゴリ名を文字列として返す純粋仮想関数
getName() - そのカテゴリのパターンの役割の説明を返す純粋仮想関数
getPurpose() - 仮想デストラクタ
次に、
PatternCategoryを継承する3つの派生クラスを作成します:CreationalCategory— 名前"Creational"と目的"Object creation mechanisms"を返しますStructuralCategory— 名前"Structural"と目的"Class and object composition"を返しますBehavioralCategory— 名前"Behavioral"と目的"Object interaction and responsibility"を返します
- カテゴリ名を文字列として返す純粋仮想関数
PatternClassifier.h: 与えられたパターンがどのカテゴリに属するかを識別できるPatternClassifierクラスを作成します。分類器には、パターン名(文字列)を受け取り、適切な
PatternCategoryへのポインタを返すclassifyメソッドが必要です。メモリ管理にはスマートポインタ(std::unique_ptr)を使用してください。分類器は以下のパターンを認識する必要があります:
- Creational:
Singleton,Factory,Builder - Structural:
Adapter,Decorator,Composite - Behavioral:
Observer,Strategy,Command
パターン名が認識されない場合は、
nullptrを返してください。- Creational:
main.cpp: 1つの入力(デザインパターンの名前)を読み取ります。PatternClassifierを作成し、それを使用して入力されたパターンを分類します。パターンが認識された場合は、以下のように出力します:[PatternName] is a [CategoryName] pattern Purpose: [CategoryPurpose]パターンが認識されない場合は、以下のように出力します:
Unknown pattern: [PatternName]
例えば、入力が Singleton の場合:
Singleton is a Creational pattern
Purpose: Object creation mechanisms入力が Observer の場合:
Observer is a Behavioral pattern
Purpose: Object interaction and responsibility入力が Proxy の場合:
Unknown pattern: Proxyこのチャレンジは、継承、ポリモーフィズム、スマートポインタといった既に習得した概念を練習しながら、デザインパターンの3つのカテゴリについての理解を深めるものです。マルチファイル構造は、実際のプロジェクトで関連するクラスをどのように整理するかを反映しています。
チートシート
デザインパターンは、一般的なソフトウェア設計の問題に対する再利用可能な解決策です。これらは、直接コピーするコードではなく、クラスやオブジェクトを構造化するためのテンプレートとして機能します。
デザインパターンは、主に3つのカテゴリに分類されます:
| カテゴリ | 目的 | 例 |
|---|---|---|
| 生成 (Creational) | オブジェクト生成のメカニズム | Singleton, Factory, Builder |
| 構造 (Structural) | クラスとオブジェクトの構成 | Adapter, Decorator, Composite |
| 振る舞い (Behavioral) | オブジェクト間の相互作用と責務 | Observer, Strategy, Command |
デザインパターンの利点:
- 開発者間での共通語彙を提供します
- 一般的な問題に対する解決策の再発明を避けることができます
- コードの柔軟性と保守性を向上させます
C++において、デザインパターンは継承、ポリモーフィズム、抽象クラス、テンプレートなどのOOP(オブジェクト指向プログラミング)機能を活用します。
自分で試してみよう
#include <iostream>
#include <string>
#include "PatternClassifier.h"
int main() {
// パターン名を読み込む
std::string patternName;
std::cin >> patternName;
// TODO: PatternClassifier インスタンスを作成する
// TODO: 分類器を使用して入力パターンを分類する
// TODO: パターンが認識された場合、以下を出力する:
// "[PatternName] is a [CategoryName] pattern"
// "Purpose: [CategoryPurpose]"
// TODO: パターンが認識されない場合 (nullptr)、以下を出力する:
// "Unknown pattern: [PatternName]"
return 0;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
3コンストラクタとデストラクタ
デフォルトコンストラクタ引数付きコンストラクタコピーコンストラクタムーブコンストラクタコンストラクタ初期化リスト委譲コンストラクタデストラクタ詳解Rule of Three / Five / Zeroまとめ - Stringクラス6演算子オーバーロード
演算子オーバーロード入門算術演算子のオーバーロード比較演算子のオーバーロードストリーム演算子代入演算子のオーバーロード[] と () 演算子のオーバーロード型変換演算子まとめ:Matrix クラス