Java 逆引き集 | インスタンス生成(new)とファクトリパターンの基礎 — オブジェクト管理

Java Java
スポンサーリンク

インスタンス生成の基本(new)

Javaでオブジェクトを作る最も基本的な方法は new。コンストラクタを呼び、必要な初期化を済ませて“使える状態”のインスタンスを得ます。

class User {
    private final String id;
    private final String name;
    public User(String id, String name) { // コンストラクタ
        this.id = id;
        this.name = name;
    }
}

User u = new User("U001", "Tanaka"); // 直接生成
Java
  • コンストラクタの役割: 必須フィールドを受け取り、クラスを“整合した初期状態”にする。
  • オーバーロード: 引数違いの複数コンストラクタを用意できるが、増えすぎると可読性が落ちる。
  • 可視性: 生成を制御したいときはコンストラクタを private にし、代わりにファクトリメソッドを使う。

静的ファクトリメソッド(of/valueOf/create)

コンストラクタの代わりに、クラス側で命名された生成メソッドを提供するスタイル。読みやすさ、柔軟性(キャッシュ・再利用)、返却型の自由度が増します。

class Money {
    private final long yen;
    private Money(long yen) { this.yen = yen; } // 直接生成を禁止
    public static Money of(long yen) {         // 命名で意図を明示
        if (yen < 0) throw new IllegalArgumentException("負の金額不可");
        return new Money(yen);
    }
    public static Money zero() { return new Money(0); } // 定型生成
}

Money m1 = Money.of(1000);
Money m0 = Money.zero();
Java
  • 利点:
    • 名前で意図を伝える: of, from, parse, empty, cached など。
    • 生成戦略を隠蔽: キャッシュ、プール、サブタイプ切り替えを内部で実施。
    • 返却型の柔軟性: インターフェース型を返し、実装クラスを隠す。

シンプルファクトリ(専用のファクトリクラス)

生成処理を専用クラスへ切り出す。UIやAPI層が“どの実装を使うか”を意識せずに済みます。

interface Transport { String ship(String item); }
class Truck implements Transport { public String ship(String item) { return "Truck: " + item; } }
class Ship implements Transport  { public String ship(String item) { return "Ship: "  + item; } }

class TransportFactory {
    public static Transport create(String mode) {
        switch (mode) {
            case "truck": return new Truck();
            case "ship":  return new Ship();
            default: throw new IllegalArgumentException("未知のモード: " + mode);
        }
    }
}

// 使う側
Transport t = TransportFactory.create("truck");
System.out.println(t.ship("Box"));
Java
  • 利点: 分岐と生成を一箇所に集約し、呼び出し側の依存を減らす。
  • 用途: 設定値や環境に応じた実装切り替え(開発/本番、オンメモリ/DBなど)。

ファクトリメソッドパターン(継承で生成を委譲)

“作り方”をサブクラスに任せる設計。親クラスは処理の流れを持ち、具体的な生成だけを差し替えます。

abstract class ReportGenerator {
    // ファクトリメソッド
    protected abstract Formatter createFormatter();
    public String generate(String data) {
        Formatter f = createFormatter();      // 生成点を一箇所に
        return f.format(data);
    }
}

interface Formatter { String format(String s); }
class CsvFormatter implements Formatter { public String format(String s) { return "CSV:" + s; } }
class JsonFormatter implements Formatter { public String format(String s) { return "{\"msg\":\"" + s + "\"}"; } }

class CsvReport extends ReportGenerator {
    protected Formatter createFormatter() { return new CsvFormatter(); }
}
class JsonReport extends ReportGenerator {
    protected Formatter createFormatter() { return new JsonFormatter(); }
}

// 使う側
String out = new CsvReport().generate("hello");
Java
  • 利点: テンプレート(親)+生成の差し替え(子)で拡張容易。
  • 適用: 生成以外の処理フローは同じだが、生成する種類だけ変わる場面。

抽象ファクトリ(製品の“族”をまとめて生成)

関連する複数オブジェクトを“セット”として一貫した組み合わせで生成します。

interface UiFactory {
    Button createButton();
    TextField createTextField();
}
class LightUiFactory implements UiFactory {
    public Button createButton() { return new LightButton(); }
    public TextField createTextField() { return new LightTextField(); }
}
class DarkUiFactory implements UiFactory {
    public Button createButton() { return new DarkButton(); }
    public TextField createTextField() { return new DarkTextField(); }
}

// 使う側(テーマを切り替えるだけで族が揃う)
UiFactory f = new DarkUiFactory();
Button btn = f.createButton();
TextField tf = f.createTextField();
Java
  • 利点: 不整合のない組み合わせを保証(テーマ、プラットフォーム、リージョン等)。
  • 用途: 複数コンポーネントの一貫性が重要なUIやプラグイン群。

実務で役立つ生成のテクニックと注意点

  • 依存反転(DI)の活用:
    • 概念: “どこで作るか”を外部(フレームワーク)に任せる。
    • 効果: テスト容易、実装差し替えが設定で可能(Springなど)。
  • ビルダーとの使い分け:
    • ビルダー: 多数の可任意パラメータ、手順を伴う組み立てに最適。
    • ファクトリ: “何を作るか”の選択と生成の隠蔽に最適。
  • キャッシュ/プール:
    • 静的ファクトリで再利用: 例)Interning、定数オブジェクト、接続プール。
  • スレッドセーフ/不変:
    • 設計: 生成直後から不変にしておくと安全かつ扱いやすい。
  • 例外と整合性:
    • 方針: 無効な引数は生成前に検証し、明確な例外で拒否(IllegalArgumentException など)。
  • テスト容易性:
    • 対処: 生成点をファクトリやDIに寄せ、テストではスタブ/モックを注入。

例題で身につける(解説つき)

例題1: コンストラクタの乱立を静的ファクトリに置き換える

class Customer {
    private final String id;
    private final String name;
    private Customer(String id, String name) { this.id = id; this.name = name; }
    public static Customer of(String id, String name) {
        if (id == null || name == null) throw new IllegalArgumentException("必須");
        return new Customer(id, name);
    }
    public static Customer guest() { return new Customer("GUEST", "Guest"); }
}
Java
  • ポイント: 命名で意図を明確化し、検証も一箇所に集約。

例題2: 設定値で実装を切り替えるシンプルファクトリ

class PaymentFactory {
    static Payment create(String provider) {
        switch (provider) {
            case "stripe": return new StripePayment();
            case "paypal": return new PaypalPayment();
            default: throw new IllegalArgumentException("未知: " + provider);
        }
    }
}
Java
  • ポイント: 呼び出し側から new を排除し、依存を減らす。

例題3: ファクトリメソッドでフローを共有

abstract class Exporter {
    protected abstract Writer writer();
    public void export(String data) {
        try (Writer w = writer()) { w.write(data); }
        catch (IOException e) { /* ハンドリング */ }
    }
}
class FileExporter extends Exporter {
    protected Writer writer() { try { return new FileWriter("out.txt"); } catch (IOException e) { throw new RuntimeException(e); } }
}
Java
  • ポイント: 生成だけ差し替え、処理フローは親で統一。

テンプレート集(すぐ使える雛形)

  • 静的ファクトリメソッド
public final class X {
    private X(...) { /* validate & init */ }
    public static X of(...) { /* validate & return new X */ }
    public static X from(Y y) { /* convert & return */ }
    public static X cached(K key) { /* cache lookup */ }
}
Java
  • シンプルファクトリ(分岐一元化)
public final class XFactory {
    private XFactory() {}
    public static X create(Config cfg) {
        if (cfg.useA()) return new XA(cfg);
        if (cfg.useB()) return new XB(cfg);
        throw new IllegalArgumentException("不正な設定");
    }
}
Java
  • ファクトリメソッド(テンプレート+生成差し替え)
abstract class Base {
    protected abstract Part createPart();
    public Result run(Input in) {
        Part p = createPart();
        return p.process(in);
    }
}
Java
  • 抽象ファクトリ(族の一貫性)
interface FamilyFactory { A createA(); B createB(); }
class Variant1 implements FamilyFactory { public A createA(){...} public B createB(){...} }
Java

まとめ

  • new: 直接生成。シンプルだが依存が硬直化しやすい。
  • 静的ファクトリ: 命名・検証・キャッシュ・返却型の柔軟性を得られる。
  • シンプル/ファクトリメソッド/抽象ファクトリ: 生成の責務を分離し、保守性と拡張性を高める。
  • 現場のコツ: 生成点を集約、命名で意図を明示、無効値は生成前に拒否、DIで差し替え可能に。

タイトルとURLをコピーしました