LASSIC Media らしくメディア
デザインパターンとは|代表例と使いどころ
デザインパターンでつまずきやすいポイント
開発会社から提出された設計書やレビューコメントに「ここはFactory Methodで実装します」「通知の仕組みはObserverパターンで組みます」といった記述が出てくると、実装に直接関わらない発注担当者やプロジェクトマネージャーは戸惑うことが少なくありません。用語の意味がつかめないまま話を進めてしまうと、提案内容が妥当なのか、単に凝った作りにしているだけなのかを判断しづらくなるのが実情です。
「デザインパターン」という言葉自体は耳にしたことがあっても、それが具体的に何を指すのか、なぜエンジニアたちがわざわざ名前を付けて共有しているのかは、案外説明されないまま現場で使われがちな概念でしょう。特定のプログラミング言語やフレームワークの機能とは違い、パターンそのものはソースコードの一行一行を指すものではなく、設計上の考え方そのものを指す言葉だからです。
見積書や設計書にパターン名が並んでいても、それを鵜呑みにするのではなく「なぜその設計を選んだのか」を尋ねられる立場になっておくと、開発会社とのやり取りが一方通行になりにくくなります。逆に、パターンという言葉を知らないまま議論を進めると、実装の妥当性よりも見た目の複雑さで判断してしまい、本来もっと単純に済む設計を過剰に評価してしまう場合もあるでしょう。
本記事では、デザインパターンとは何かという基礎知識から、代表的な3つの分類と具体例、業務で役立つ使いどころ、そして導入時に気を付けたい注意点までを整理します。想定する読者は、IT事業部門の担当者や、開発会社に発注する立場のプロジェクトマネージャーです。自らコードを書く機会が少なくても、パターンの考え方を理解しておくと、設計レビューや見積もりの妥当性を検討する場面での判断材料になります。
特にシステムのリニューアルや、複数の開発会社が入れ替わりで関わるプロジェクトでは、過去の設計意図が正しく引き継がれているかどうかが後々の保守コストを左右します。デザインパターンという共通の物差しを持っておくと、引き継ぎ資料や打ち合わせの場で「以前の担当者がなぜこの構造にしたのか」を読み解く手がかりが増えるでしょう。
デザインパターンとは何か
デザインパターンとは、ソフトウェア設計においてよく起きる典型的な問題に対して、再利用可能な解決策をあらかじめ整理し、名前を付けてまとめたカタログのようなものです。特定の完成品のソースコードそのものではなく、「こういう状況ではこう設計しておくと後々破綻しにくい」という考え方の雛形を指します。同じ種類の問題に対して開発者ごとにばらばらな解決策を毎回一から考えるのではなく、先人が整理した定石を土台にして設計を組み立てられる点に価値があります。
この考え方の起点になったのが、1994年にエリック・ガンマ、リチャード・ヘルム、ラルフ・ジョンソン、ジョン・ブリシディースの4名が著した書籍です。4人の著者を指す通称から「GoF(Gang of Four)」と呼ばれ、書籍でまとめられた23個のパターンは「GoFデザインパターン」の名で広く知られています。書籍の刊行から30年以上が経った現在でも、設計を語るうえでの共通の土台として参照され続けているのは、扱われている問題そのものが古びていないことの表れといえるでしょう。
特筆すべきは、これらのパターンが特定のプログラミング言語やフレームワークに縛られた技法ではないという点です。もともとオブジェクト指向の考え方をベースに整理されたものですが、クラスとインターフェースという概念さえ備えていれば、Java、C#、TypeScript、PHPなど言語を問わず同じ発想を適用できます。技術スタックが変わっても設計上の会話で共通の語彙として機能し続けるのは、この言語非依存な性質によるところが大きいものです。
ただし、デザインパターンを覚えることが設計力そのものではありません。パターンはあくまで「よくある問題」と「その定石」を結び付けた辞書のような存在であり、目の前の課題がどの問題に近いのかを見極める判断力のほうが本質だといえるでしょう。この点は後述する注意点でも改めて取り上げます。
混同されやすい言葉に「アルゴリズム」がありますが、両者は指す範囲が異なります。アルゴリズムは特定の手順を細かく定めた計算方法そのものを指すのに対し、デザインパターンはクラスやオブジェクトの役割分担・関係性という、もう一段抽象度の高い設計上の型を指す言葉です。ソートの手順書がアルゴリズムだとすれば、デザインパターンは「誰が何の責任を持つか」という組織図に近い存在といえるでしょう。
3つの分類と代表的なパターン
GoFの23パターンは、それぞれが解決する問題の性質によって「生成に関するパターン」「構造に関するパターン」「振る舞いに関するパターン」という3つに大別されます。全体像をまず次の表に整理しました。
| 分類 | 何を扱うか | 代表的なパターン |
|---|---|---|
| 生成に関するパターン | オブジェクトの生成方法を柔軟にし、呼び出し側と生成処理を切り離す | Factory Method、Singleton など |
| 構造に関するパターン | クラスやオブジェクトの組み合わせ方を整理し、部品同士の依存を緩める | Adapter、Facade など |
| 振る舞いに関するパターン | オブジェクト間の役割分担やアルゴリズムの流れを整理する | Strategy、Observer など |
以下、各分類から代表例を挙げ、平易な言葉で何を解決するパターンなのかを見ていきます。添えているコードはあくまで理解の助けとする擬似コードで、特定の言語における正式な実装例ではないという点です。記号は適宜HTML表記に置き換えています。
なお、実務では一つのパターンだけを単独で使うとは限らず、複数のパターンを組み合わせて一つの機能を成立させる場面も珍しくありません。たとえばFactory Methodで生成したオブジェクトをStrategyとして差し替え可能な形にする、というように、生成・構造・振る舞いの分類は互いに排他的なものではなく、設計を整理するための切り口として捉えておくとよいでしょう。
生成に関するパターン ― Factory MethodとSingleton
Factory Methodは、オブジェクトの生成処理を専用の窓口(ファクトリ)にまとめ、呼び出し側が「何のクラスをnewするか」を意識しなくて済むようにするパターンです。次のような擬似コードで表せます。
interface Shape { draw; }
class ShapeFactory {
create(kind) {
if (kind == "circle") return new Circle;
if (kind == "rectangle") return new Rectangle;
}
}
呼び出し側はShapeFactory.create("circle")と書くだけで済み、Circleクラスの詳細やコンストラクタの引数を意識せずに済みます。将来Triangleという新しい図形を追加したくなった場合も、ファクトリの内部を書き換えるだけで対応でき、呼び出し側のコードには手を入れずに済む設計です。注文種別ごとに異なる処理オブジェクトを組み立てる業務システムなどで、よく見かける発想でしょう。
Singletonは、あるクラスのインスタンスをアプリケーション内でひとつだけに限定するパターンです。設定情報を一元管理するオブジェクトや、接続先が一つに定まるログ出力の窓口など、複数のインスタンスが存在すると挙動の食い違いが起きやすい対象に使われます。便利な一方で、どこからでもアクセスできてしまう性質がテストのしづらさや、意図しない依存関係につながる面もあり、設計レビューの場では乱用に注意すべきパターンとして名前が挙がることも多いものです。プロジェクト管理ツールの設定値保持など、対象が本当に一つに限定される性質を持つかどうかを見極めたうえで採用するかどうかを判断したいパターンといえます。
構造に関するパターン ― AdapterとFacade
Adapterは、インターフェースが異なる二つの部品をつなぐ「変換アダプタ」の役割を持つパターンです。海外製の電源プラグを国内のコンセント形状に変換する道具と同じ発想で、既存のクラスに手を加えずに、呼び出し側が期待する形式へ変換する層を間に挟みます。
class LegacyPrinter { printOldFormat(text) { ... } }
class PrinterAdapter implements NewPrinterInterface {
constructor(legacy) { this.legacy = legacy; }
print(text) { this.legacy.printOldFormat(text); }
}
古いシステムを刷新する際、外部の新しいライブラリのインターフェースと既存コードのインターフェースが噛み合わないという場面はよく起こります。Adapterを間に挟むことで、既存資産を丸ごと作り直さずに新しい部品と接続できるのが利点です。レガシーシステムと新規サービスを段階的につなぎながら移行を進めたい場合の選択肢として名前が挙がることもあるでしょう。
Facadeは、複数のクラスが絡み合った複雑な内部処理に対して、ひとつの簡潔な窓口を用意するパターンです。利用者側は窓口(ファサード)が提供するメソッドを呼ぶだけで済み、内部でどのクラスがどう連携しているかを意識せずに機能を使えます。在庫確認・与信照会・通知送信など複数の内部処理が連なる決済機能を、外部からは「決済を実行する」という一つの呼び出しで完結させたい場合などに使われる考え方です。窓口の裏側にある処理を丸ごと入れ替えても、外部から見た呼び出し方が変わらなければ、連携している他システムへの影響を抑えたまま内部を刷新できる点も利点として挙げられます。
振る舞いに関するパターン ― StrategyとObserver
Strategyは、アルゴリズムや処理方法を「戦略」として部品化し、実行時に切り替えられるようにするパターンです。配送料金の計算方法や割引ルールの適用方法など、条件によってロジックを差し替えたい場面で使われます。
interface DiscountStrategy { apply(price); }
class SeasonalDiscount implements DiscountStrategy { apply(price) { return price * 0.9; } }
class MemberDiscount implements DiscountStrategy { apply(price) { return price - 500; } }
if文を延々と重ねて条件分岐を作り込む代わりに、割引ロジックそれぞれを独立した部品として用意し、状況に応じて差し替える形にしておくと、新しい割引ルールを追加する際に既存のコードへ手を入れる範囲を抑えやすくなります。キャンペーンの追加や条件変更が頻繁なシステムほど、この構造の恩恵を受けやすいでしょう。
Observerは、ある対象(サブジェクト)の状態が変化したとき、それを見ている複数の相手(オブザーバー)へ変化を知らせる仕組みです。在庫数が一定を下回ったら担当者へ通知する、注文ステータスが更新されたら関連する複数の画面やシステムへ連携する、といった「一つの出来事を複数の相手に伝える」場面で使われます。通知を受け取る相手を増やしたいときも、サブジェクト側のコードを書き換えずに新しいオブザーバーを登録するだけで対応できるのが特徴です。連携先が今後増える見込みがある通知機能では、あらかじめこの構造を選んでおくことで、追加のたびにコードの中心部分を触らずに済むという恩恵を受けやすくなります。
デザインパターンの使いどころ
実務でデザインパターンが役立つのは、コードを書く場面だけに限りません。発注する側の視点でも意味のある効果がいくつかあります。
- 保守性・拡張性の向上: 定石に沿った設計は、後から機能を追加したり仕様を変更したりする際に、影響範囲を見積もりやすくなります。パターンに当てはめて考えることで、変更が一箇所に閉じるか、あちこちに波及するかを事前に把握しやすくなるのが利点です。
- 共通言語としての機能: 「Strategyパターンで実装します」と一言伝えれば、エンジニア同士は設計の意図をすり合わせられます。開発会社との打ち合わせでも、パターン名を軸にした会話は、実装の細部を逐一説明するより意思疎通が早まる場面が多いでしょう。
- コードレビュー・設計議論の効率化: レビューの場で「この部分はどのパターンに近い作りか」を確認できると、担当者ごとに異なる独自ルールの積み重ねよりも、他の担当者が理解しやすい設計かどうかを判断しやすくなります。
- 引き継ぎのしやすさ: 担当者が交代しても、広く知られたパターンに則った設計であれば、新しい担当者が既存資料や外部の解説記事を手掛かりに理解を進めやすくなるものです。
- 発注・受託の場面での判断材料: 見積もりや設計書にパターン名が登場した際、それが何を意図した設計なのかを理解できるかどうかが、質問や指摘の質を左右します。
- テストのしやすさ: Strategyのようにロジックを独立した部品として切り出しておくと、部品ごとに単体でテストしやすくなり、不具合が起きた際の切り分けも進めやすくなります。
発注側の担当者にとっては、「なぜここでSingletonを使う設計になっているのか」「Factory Methodにする代わりに単純な条件分岐で済ませられないか」といった問いを投げかけられる立場になっておくと、開発会社との対話が一方通行になりにくくなるでしょう。パターンの名前を知っていることは、こうした対話の入り口として機能します。
複数の開発会社が関わる案件や、社内の情報システム部門と外部ベンダーが混在するプロジェクトでは、担当者ごとに設計の呼び方がばらばらだと、同じ内容を指しているのか、微妙に違う設計を指しているのかを確認する手間が積み重なりがちです。パターン名という共有された語彙を仲立ちにすることで、こうした確認のやり取りを減らせる場面は多いでしょう。
導入時の注意点―パターンありきにしない
デザインパターンは役立つ考え方ですが、使うこと自体を目的にしてしまうと、かえって設計を複雑にする恐れがあります。次の注意点を押さえておくとよいでしょう。
- 過剰適用(オーバーエンジニアリング)を避ける: 将来の拡張に備えて何でもFactoryやStrategyで抽象化しておく、という発想は一見丁寧に見えますが、実際には使われない抽象化がコードの見通しを悪くする原因にもなります。
- 解決したい問題が先、パターンは後: 「このパターンを使いたいから当てはめる」のではなく、目の前の設計課題を整理した結果として、既存のパターンに近い形が見えてくる、という順序で考えるのが妥当です。
- 名前を知っていること自体を目的化しない: パターンの名称を覚えることよりも、それが解決しようとしている課題の性質を理解しているかどうかのほうが、実務では重んじられます。
- 費用対効果を踏まえる: 小規模で仕様変更の見込みが少ない機能にまで手厚い抽象化を持ち込むと、実装や理解にかかる工数が見合わなくなりやすいものです。
- チーム内の習熟度に合わせる: パターンに不慣れなメンバーが多いチームにいきなり複雑な組み合わせを持ち込むと、レビューの負担が増え、かえって設計の狙いが伝わりにくくなることもあります。
なお、本記事は特定言語や特定フレームワークの実装解説ではなく、設計の定石という概念と使いどころに焦点を当てるものです。各言語特有の書き方や、フレームワークが提供する仕組みとの対応関係を知りたい場合は、採用している技術スタックに沿った資料を別途あたる必要があります。
開発会社から提出された設計にパターン名が多用されている場合、それぞれが実際にどのような課題への対処として選ばれたのかを尋ねてみるとよいでしょう。理由が明確に説明できる設計であれば、パターンは道具として適切に機能している証と見てよいものです。逆に理由の説明が曖昧なまま名前だけが並んでいる場合は、設計の妥当性を今一度確認する余地があります。
見積書の工数がパターン名の多さに比例して膨らんでいるように見えるときも、注意しておきたい場面です。パターンを採用すること自体が付加価値を生むわけではなく、そのパターンによって将来のどの作業がどれだけ楽になるのかという説明とセットで初めて工数の妥当性を判断できます。工数の内訳と設計上の狙いを一緒に確認する姿勢を持っておくと、発注側として納得感のある合意形成につながりやすいでしょう。
まとめ
デザインパターンは、覚える対象というより、設計を検討する際の物差しとして手元に置いておく知識です。本記事で取り上げた内容を、次の観点で振り返ります。
- デザインパターンとは、ソフトウェア設計でよく起きる問題に対する再利用可能な解決策を整理したカタログであり、GoFの23パターンが広く知られている
- 特定の言語やフレームワークに縛られず、生成・構造・振る舞いという3つの分類で整理されている
- Factory MethodやSingletonは生成、AdapterやFacadeは構造、StrategyやObserverは振る舞いに関するパターンの代表例
- 保守性・拡張性の向上に加え、エンジニアと発注者、開発会社同士の共通言語として機能し、レビューや設計議論の効率を高める
- パターンありきの過剰適用は避け、解決したい問題を起点に考える姿勢が欠かせない
- 設計書にパターン名が登場した際は、選ばれた理由を尋ねる視点を持っておくと発注側としての判断がしやすくなる
設計レビューや技術選定でお困りの際は
提出された設計書にパターン名が登場しても、それが妥当な選択なのか、過剰な抽象化になっていないかを発注者側だけで見極めるのは簡単ではありません。LASSICでは、ニアショア開発の体制を生かし、設計レビューの第三者チェックから実装、保守フェーズまで一貫して支援しています。開発会社から提出された設計の妥当性を確認したい場合や、既存システムの設計を見直したい場合は、社内リソースだけで判断を抱え込まず、一度話を聞いてみるのも選択肢の一つでしょう。設計段階から実装・保守フェーズまでを見通したうえで、無理のない粒度のパターン適用を一緒に検討します。
よくある質問
デザインパターンを知らないとシステム開発はできませんか。
そのようなことはありません。パターンを意識せずに書かれたコードでも問題なく動作するシステムは数多く存在するでしょう。ただし、複数人での開発や長期的な保守が想定される場面では、パターンに沿った設計のほうが後から手を入れる担当者にとって理解しやすい、という傾向があります。知らなくても開発は進められますが、知っておくと設計の会話が円滑になる場面が増える、という位置づけで捉えるとよいでしょう。発注担当者としては、実装できるかどうかよりも、開発会社の説明を理解し、妥当性を判断できるかどうかのほうが重要な観点になります。
GoFの23パターン以外にもデザインパターンは存在しますか。
存在します。GoFの23パターンはオブジェクト指向設計における代表的な整理ですが、その後もクラウド環境向けの設計パターンや、特定のアーキテクチャに特化したパターン集など、目的に応じた新しいカタログも生まれてきたのが実情です。GoFのパターンは、そうした後発のパターン集を理解する際の土台としても位置づけられています。まずはGoFの3分類を押さえておき、必要に応じて自社のシステムに近い領域のパターン集へ視野を広げていく進め方が現実的でしょう。
開発会社の見積もりや提案にパターン名が出てきたら、どう確認すればよいですか。
そのパターンを選んだ理由と、想定している将来の変更内容を尋ねてみるのが実務的な確認方法です。「拡張が見込まれるためStrategyで実装し、割引ルールの追加時にコード全体を書き換えずに済むようにしている」といった具体的な説明が返ってくれば、設計として筋が通っている可能性が高いといえます。理由が抽象的なまま名前だけが並ぶ場合は、担当者に掘り下げて確認してみましょう。質問に対して具体的な業務シナリオを添えて回答できる相手であれば、設計の意図をきちんと言語化できていると判断してよいものです。
デザインパターンを使うと開発コストは下がりますか。
一概には言えません。適した場面で使えば将来の変更コストを抑える効果が期待できますが、不要な場面で使うとかえって設計が複雑になり、初期の実装コストが増える場合もあります。導入するかどうかは、対象のシステムがどの程度の変更や拡張を見込んでいるかを踏まえて判断する必要があるテーマです。目先の実装コストだけでなく、数年単位での保守フェーズまで含めた総費用で見積もる視点を持っておくと、判断の精度が上がるでしょう。
著者:テレリモ総研編集部 鈴木 亮佑
設計の定石を踏まえたシステム開発や、既存システムの設計レビューをお考えの場合は、ニアショア開発によるコスト最適化と品質確保を両立するLASSICにご相談ください。要件のヒアリングから設計、実装まで伴走いたします。
出典
本記事の内容は、下記の公式・解説サイトで整理されているデザインパターンの定義や3分類の考え方を参考にしています。より詳しい実装例を確認したい場合は、各リンク先を参照してください。
- Refactoring.Guru「デザインパターンとは?」
- Refactoring.Guru「デザインパターン・カタログ」
- Microsoft Learn「クラウド設計パターン – Azure Architecture Center」