「デザインパターンの本は読んだけど、実際に使えるかというと…」
そんな悩みを持っていませんか?Factory Method、Strategy——名前は知っているし、サンプルコードも見た。でも、自分でゼロから設計しようとすると、気づけばまたif/elseの山。
この記事では、ウイスキー香味プロファイル生成器を作りながら、2つのデザインパターンを手で覚えます。「動く→破綻→パターン導入」のサイクルを繰り返すことで、「なぜこのパターンが必要なのか」が腹落ちする体験をお届けします。
完成したら、友人との飲み会で「俺が作ったシステムでこのウイスキーを分析してやろうか?」と自慢しましょう。
この記事で習得できること
| パターン | 役割 | 本記事での実装 |
|---|---|---|
| Factory Method | オブジェクト生成の責務分離 | ウイスキータイプ名からプロファイルオブジェクト生成 |
| Strategy | アルゴリズムの切り替え | テイスティングノートの記述スタイル切り替え |
対象読者
- 「デザインパターン学習シリーズ」を一通り読んだが、まだ自力で使いこなせない方
- if/elseから脱却したい方
- 手を動かしながらパターンを定着させたい方
- Whiskyが好きな方(オプション)
技術スタック
| 項目 | バージョン・ライブラリ |
|---|---|
| Perl | v5.36以降(signatures、postfix dereference対応) |
| オブジェクト指向 | Moo |
| 外部依存 | なし(標準ライブラリのみ) |
第1章: スコッチの香りを言葉にしよう
今回の目標
- WhiskyDescriberクラスを作成する
- スコッチウイスキーのテイスティングノートを詩的に表現する
動く:最初の実装
まずは1種類のウイスキーだけを扱うシンプルな実装から始めます。
| |
実行結果
| |
動きました!スコッチウイスキーの詩的なテイスティングノートが出力されています。
今回のポイント
- Mooの
hasで属性を定義し、subでメソッドを実装 $selfを使ったオブジェクト指向の基本構造- signatures(
$self)で引数を明示的に宣言
次の章では、ウイスキーの種類と記述スタイルを増やしていきます。

第2章: バーボンもアイリッシュも!条件分岐地獄
前章の振り返り
WhiskyDescriberクラスを作成し、スコッチウイスキーのテイスティングノートを出力しました。
今回の目標
- バーボン、アイリッシュ、ジャパニーズを追加する
- 詩的/技術的/初心者向けの3つの記述スタイルを追加する
動く:if/elseで全部対応
新しいウイスキーと記述スタイルを追加してみましょう。
| |
破綻:問題が発生
一応動きますが、問題が見えてきました。
問題点1: 組み合わせ爆発
- 4種類 × 3スタイル = 12通りの条件分岐
- カナディアン、ライ、シングルモルト… 増やすたびに地獄
問題点2: describeメソッドが肥大化
- 修正箇所の特定が困難
- 1つの変更が別のケースに影響するリスク
問題点3: テストが書けない
- 各ケースを網羅的にチェックする必要
- モック化が困難
| |
今回のポイント
- 機能追加のたびにif/elseが増える「条件分岐の爆発」が発生
- タイプとスタイルの2軸で分岐すると、組み合わせが掛け算で増える
- 次章でこの問題をFactory Methodパターンで解決します

第3章: ウイスキー工場を作ろう
前章の振り返り
ウイスキータイプと記述スタイルの組み合わせで、if/elseが爆発する問題が発生しました。
今回の目標
- 各ウイスキータイプを独立したクラスにする
- Factory Methodパターンで生成を整理する
破綻からの脱出:Factory Methodの導入
まず、ウイスキータイプの問題を解決しましょう。各タイプを独立したクラスにし、ファクトリで生成します。
| |
何が改善されたか?
- if/elseが消えた:
%profile_mapハッシュでタイプとクラスをマッピング - 各タイプが独立: ScotchProfile、BourbonProfileなどが別々のクラスに
- 拡張が容易: 新しいタイプは新クラス + マップ登録だけ
| |
Factory Methodパターンとは
classDiagram
class WhiskyFactory {
+create_profile(type, args)
}
class `WhiskyProfile` {
<<interface>>
+name
+age
+type
+get_flavors()
+describe()
}
class ScotchProfile {
+get_flavors()
}
class BourbonProfile {
+get_flavors()
}
class IrishProfile {
+get_flavors()
}
class JapaneseProfile {
+get_flavors()
}
WhiskyFactory ..> `WhiskyProfile` : creates
`WhiskyProfile` <|.. ScotchProfile
`WhiskyProfile` <|.. BourbonProfile
`WhiskyProfile` <|.. IrishProfile
`WhiskyProfile` <|.. JapaneseProfile
Factory Methodパターンは、オブジェクト生成の責務を専用のファクトリクラスに委譲するパターンです。クライアントは具象クラスを直接知る必要がなく、ファクトリを通じてオブジェクトを取得します。
今回のポイント
- Moo::Roleでインターフェース(共通契約)を定義
requiresで具象クラスに実装を強制- ファクトリのハッシュマップでif/elseを排除
- 新規タイプの追加が「新クラス + マップ登録」だけで完結
ただし、まだ記述スタイルの切り替えができていません。次章でStrategyパターンを導入します。

第4章: 記述スタイルを切り替えよう
前章の振り返り
Factory Methodパターンで、ウイスキータイプごとにクラスを分離しました。しかし、記述スタイル(詩的/技術的/初心者向け)の切り替えがまだできていません。
今回の目標
- 記述スタイルを戦略(Strategy)として抽出する
- 実行時にスタイルを切り替え可能にする
Strategyパターンの導入
記述スタイルを「戦略」として独立させます。
| |
実行結果
| |
Strategyパターンとは
classDiagram
class `WhiskyProfile` {
+strategy: DescriptionStrategy
+describe()
}
class `DescriptionStrategy` {
<<interface>>
+format_description(profile)
}
class PoeticStrategy {
+format_description(profile)
}
class TechnicalStrategy {
+format_description(profile)
}
class BeginnerStrategy {
+format_description(profile)
}
`WhiskyProfile` --> `DescriptionStrategy` : has
`DescriptionStrategy` <|.. PoeticStrategy
`DescriptionStrategy` <|.. TechnicalStrategy
`DescriptionStrategy` <|.. BeginnerStrategy
Strategyパターンは、アルゴリズム(この場合は記述方法)をクラスとしてカプセル化し、実行時に切り替え可能にするパターンです。
今回のポイント
does => 'DescriptionStrategy'でRole型制約を設定- 戦略クラスに処理を委譲(
$self->strategy->format_description($self)) - 新しい記述スタイルは新クラス追加だけで対応可能
第5章: 完成!香味プロファイル生成器
前章までの振り返り
- Factory Method: ウイスキータイプごとにクラスを分離
- Strategy: 記述スタイルを戦略として抽出
今回の目標
- 両パターンを統合した完成版を作成する
- 4種類のウイスキー × 3種類のスタイルを自在に組み合わせる
完成版コード
| |
2パターンの相乗効果
| パターン | 解決する問題 | 本記事での実装 |
|---|---|---|
| Factory Method | 生成ロジックの集中化 | 4種類のウイスキータイプを7クラスで管理 |
| Strategy | アルゴリズムの交換 | 3種類の記述スタイルを実行時に切り替え |
| 組み合わせ | 柔軟な拡張性 | 4タイプ × 3スタイル = 12通りを少ないコードで実現 |
統合クラス図
classDiagram
class WhiskyFactory {
+create_profile(type, args)
+create_with_strategy(type, strategy_type, args)
}
class `WhiskyProfile` {
<<interface>>
+name
+age
+type
+strategy: DescriptionStrategy
+get_flavors()
+describe()
}
class `DescriptionStrategy` {
<<interface>>
+format_description(profile)
}
class ScotchProfile
class BourbonProfile
class IrishProfile
class JapaneseProfile
class PoeticStrategy
class TechnicalStrategy
class BeginnerStrategy
WhiskyFactory ..> `WhiskyProfile` : creates
`WhiskyProfile` <|.. ScotchProfile
`WhiskyProfile` <|.. BourbonProfile
`WhiskyProfile` <|.. IrishProfile
`WhiskyProfile` <|.. JapaneseProfile
`WhiskyProfile` --> `DescriptionStrategy` : has
`DescriptionStrategy` <|.. PoeticStrategy
`DescriptionStrategy` <|.. TechnicalStrategy
`DescriptionStrategy` <|.. BeginnerStrategy
拡張例:新しいタイプとスタイルの追加
| |
今回のポイント
- Factory Method + Strategy の組み合わせで柔軟な拡張性を実現
- 新規タイプ・スタイルの追加は「クラス追加 + マップ登録」だけ
- 開放閉鎖原則(OCP): 既存コードを変更せずに機能拡張
まとめ
この記事では、ウイスキー香味プロファイル生成器を作りながら、2つのデザインパターンを体験しました。
学んだこと
| パターン | 解決する問題 | Perlでの実装ポイント |
|---|---|---|
| Factory Method | 生成ロジックの集中化、if/else排除 | ハッシュマップ + Moo::Role |
| Strategy | アルゴリズムの交換、条件分岐削減 | doesによる型制約 + 委譲 |
デザインパターンを「手で覚える」とは
- 動く: まずは素朴に実装する
- 破綻: 機能追加で問題が発生する
- パターン導入: 問題を解決する設計に改善する
- 完成: パターンの効果を実感する
このサイクルを体験することで、「なぜこのパターンが必要なのか」が腹落ちします。
次のステップ
- 他のウイスキータイプを追加してみる
- 自分だけのテイスティングノートデータベースを作る
- 友人との飲み会で自慢する
🥃 乾杯!
