はじめに
今回はデザインパターン・アンチパターンについて話したいと思います。 まず、今回の趣旨を伝えておきます。 今回は「デザインパターン・アンチパターン」というものを「認知」していただければ成功かと思います。 この後、いくつか実際のパターンなども紹介しますが、すべて覚える必要はないです。また忠実に守る必要もないです。
実際に問題に直面したときや、設計前などに、存在することを知っていれば、その時に詳しく調べればいいのです。 その助けになれば良いかなと思います。
1. プログラミングにおけるデザインパターン
デザインパターンとは、ソフトウェア設計において繰り返し現れる問題に対する、先人たちが積み上げた「再利用可能な解決策の型(テンプレート)」 のことです。1994年に出版されたエリック・ガンマらによる書籍(通称:GoF)によって広く普及しました。 デザインパターンを採用する目的は、単に「コードを綺麗にする」ことだけではありません。以下の3つの質を向上させることにあります。
- 保守性: 修正が必要な際、影響範囲を最小限に抑えられる。
- 拡張性: 新しい機能を追加する際、既存のコードを壊さずに済む。
- 共通言語: 開発者同士が「ここはObserverで」と言うだけで、複雑な設計意図を瞬時に共有できる。
有名なパターン(抜粋)
| パターン名 | 概要 | 主な使いどころ | メリット |
|---|---|---|---|
| Singleton | インスタンスを1つに制限 | DB接続、ログ出力、設定管理 | リソース重複防止、状態の一元管理 |
| Factory Method | 生成ロジックをクラスに委譲 | OS毎のUI生成、プラグイン | 呼び出し側が具体名を知らずに済む |
| Observer | 状態変化の自動通知 | イベント通知、SNS、MVC | 疎結合なリアルタイム連携が可能 |
| Strategy | アルゴリズムの動的切替 | 決済手段切替、ソート選択 | if/switch文を排除し拡張性向上 |
| Adapter | インターフェースの変換 | レガシーコードの再利用 | 既存コードを壊さず新環境に適合 |
2. プログラミングにおけるアンチパターン
アンチパターンとは、一見すると効率的または合理的な解決策に見えますが、実際には長期的には悪影響(保守性の低下、バグの増加、パフォーマンス劣化など)を及ぼすことが証明されている、避けるべき設計・実装の型のことです。 デザインパターンが「ベストプラクティス(最善策)」の集積であるのに対し、アンチパターンは 「ワーストプラクティス(失敗の典型例)」 を体系化したものです。 アンチパターンは、悪意を持って作られるものではありません。多くの場合、以下のような「一見正当な理由」から発生します。
- 知識不足: 適切なデザインパターンや言語のベストプラクティスを知らない。
- 短期的な効率: 「今は動けばいい」「納期が最優先」というプレッシャー。
- 文脈の誤解: ある場面では正解だった手法を、不適切な場面に無理やり適用する。
- 過剰な一般化: 将来使うかもしれないという憶測で、過度に複雑な抽象化を行う。
有名なパターン(抜粋)
| パターン名 | 特徴 | 弊害 | 対策 |
|---|---|---|---|
| Spaghetti Code | 複雑に絡み合った制御構造 | 解読不能、修正の影響範囲不明 | 関数分割、単一責任の原則を適用 |
| God Object | 一つのクラスに機能が集中 | 保守性低下、修正時の連鎖バグ | 責任に応じたクラスの適切な分割 |
| Golden Hammer | 特定の技術を無理に適用 | 効率低下、不適切なアーキテクチャ | 適材適所の技術選定と多角的な視点 |
| Cargo Cult | 理解せずコピペで実装 | 不要なコード、予期せぬ挙動 | コードの意味を理解し、不要を削る |
| Magic Numbers | 生の数値が直接記述される | 意図が不明、変更漏れの発生 | 定数(const/enum)に名前を付ける |
3. データベース設計のアンチパターン
データベース設計におけるアンチパターンとは、RDB(リレーショナルデータベース)の数学的基礎である「リレーショナルモデル」や「正規化理論」を軽視した結果、データの整合性、クエリのパフォーマンス、およびアプリケーションの拡張性を損なう設計のことを指します。 DBの構造は一度運用が始まると変更が非常に困難(破壊的変更になりやすい)なため、設計段階でこれらの罠を回避することが極めて重要です。
DB設計においてアンチパターンが紛れ込む背景には、特有の力学が働いています。
- アプリケーション視点への偏り: データベースを単なる「データの永続化先(ストレージ)」と捉え、オブジェクト指向のデータ構造をそのままテーブルに投影しようとする。
- 不適切な柔軟性の追求: 「将来どんなデータが来るかわからない」という不安から、テーブル構造を汎用化しすぎる(例:EAV)。
- SQLへの不慣れ: 複雑なJOINを避けようとして、一つのカラムにデータを詰め込んだり、非正規化を過剰に行ったりする。
有名なパターン(抜粋)
| パターン名 | 特徴 | 弊害 | 対策 |
|---|---|---|---|
| Jaywalking | カンマ区切りの文字列保存 | 検索低速、集計が困難 | 交差テーブルへの正規化 |
| Naive Trees | parent_idのみの階層管理 | 複雑な再帰クエリ、削除の困難さ | 閉包テーブル等の階層モデル採用 |
| EAV (汎用属性) | 属性と値を別々の行で保持 | 型の不備、結合過多による鈍化 | 正当な正規化、またはJSONB活用 |
| Multi-Column | tag1, tag2とカラムを増設 | 保存数の制限、検索処理の複雑化 | 従属テーブルへの切り出し |
| Polymorphic | 1つの外部キーが複数表を指す | 外部キー制約が使えず整合性破壊 | 親ごとの交差テーブル作成 |
