はじめに:なぜ設計パターンが必要なのか?
Javaで開発していると、こんな経験ありませんか?
- 「とりあえずクラス作って動いたけど、保守が大変…」
- 「似たような処理をコピペしている気がする」
- 「ベテランが書いたコード、何でこんな構造なの?」
これらはすべて、「設計パターン」を理解していないがゆえに起こる問題です。
本記事では、現場でよく使われる5つの設計パターンを取り上げ、「なんとなく設計」から脱却する第一歩を紹介します。
設計パターンとは?
設計パターンとは、よくある設計上の課題に対する“再利用可能な解決策”です。
GoF(Gang of Four)の23パターンが有名で、どれも長年のソフトウェア開発現場で実証されてきたベストプラクティスです。
本記事で扱う5つのパターン
パターン名 | 分類 | 主な役割 |
---|---|---|
Singleton(シングルトン) | 生成系 | インスタンスの一意性を保証 |
Factory Method(ファクトリーメソッド) | 生成系 | オブジェクト生成の委譲 |
Strategy(ストラテジー) | 振る舞い系 | アルゴリズムの切り替えを可能に |
Observer(オブザーバー) | 振る舞い系 | 状態変化を他オブジェクトに通知 |
Decorator(デコレーター) | 構造系 | 機能を追加しつつ柔軟に拡張可能 |
1. Singletonパターン|設定管理やDB接続に使われる鉄板
概要
あるクラスのインスタンスを一つだけ生成し、それを使い回すパターン。
ログ管理、設定読み込み、DB接続管理などに多用されます。
実装例(Java)
public class ConfigManager {
private static ConfigManager instance = new ConfigManager();
private ConfigManager() {}
public static ConfigManager getInstance() {
return instance;
}
public String getValue(String key) {
return "dummy"; // 実際はファイルから取得など
}
}
2. Factory Methodパターン|インスタンス生成の委譲
概要
オブジェクト生成をサブクラスに任せることで、依存度を下げて柔軟性を高めるパターンです。
実装例
interface Animal {
void speak();
}
class Dog implements Animal {
public void speak() { System.out.println("Woof!"); }
}
class AnimalFactory {
public static Animal createAnimal(String type) {
if (type.equals("dog")) return new Dog();
throw new IllegalArgumentException("Unknown type");
}
}
3. Strategyパターン|アルゴリズムの切り替えを柔軟に
概要
アルゴリズムや処理ロジックをインタフェースとして定義し、実装を切り替えるパターンです。
検索条件やソートロジックの切り替えなどに使われます。
実装例
interface SortStrategy {
void sort(List<Integer> list);
}
class QuickSort implements SortStrategy {
public void sort(List<Integer> list) {
Collections.sort(list); // 簡略化
}
}
class SortContext {
private SortStrategy strategy;
public SortContext(SortStrategy strategy) {
this.strategy = strategy;
}
public void executeSort(List<Integer> list) {
strategy.sort(list);
}
}
4. Observerパターン|イベント駆動や状態通知に強い
概要
あるオブジェクトの状態変化を、他の複数のオブジェクトに通知する仕組み。
GUIイベント処理やデータ更新通知などで使用されます。
実装例
interface Observer {
void update(String message);
}
class ConcreteObserver implements Observer {
public void update(String message) {
System.out.println("Received: " + message);
}
}
class Subject {
private List<Observer> observers = new ArrayList<>();
public void addObserver(Observer o) { observers.add(o); }
public void notifyObservers(String msg) {
for (Observer o : observers) o.update(msg);
}
}
5. Decoratorパターン|既存機能に後付けで機能拡張
概要
既存クラスを継承せずに、機能を追加できる柔軟な構造パターン。
IOストリーム、ログ出力、UIなどで多用されます。
実装例
interface Notifier {
void send(String message);
}
class BasicNotifier implements Notifier {
public void send(String message) {
System.out.println("Send: " + message);
}
}
class SMSDecorator implements Notifier {
private Notifier notifier;
public SMSDecorator(Notifier notifier) {
this.notifier = notifier;
}
public void send(String message) {
notifier.send(message);
System.out.println("Also sent via SMS: " + message);
}
}
まとめ:設計パターンを“引き出し”にしよう
設計パターンは、暗記する必要はありません。
問題に直面したとき「使えそうな型があるな」と思い出せるかが重要です。
次のステップ
- 実務プロジェクトの中で1つでも意識的に使ってみる
- GoFパターンを全体像でざっくり押さえる
- リファクタリング時に「何パターンにできるか?」と考える