0Pricing
PHP Academy · レッスン

生成パターン:Factory、Builder、Singleton

生成パターンを使って、オブジェクト生成をすっきり制御します。

「生成パターン:Factory、Builder、Singleton」はCoddyKit上の無料PHP Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはPHP Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 PHP Academyコースには全4レッスンが含まれています。

生成を制御する

生成パターンは、必要なものが何かと、どのように構築するかを分離します。生成に条件分岐や複数の手順が必要な場合、または生成処理を一元化する必要がある場合、コードベースのあちこちにnewを記述すると、呼び出し側が具象クラスに依存してしまいます。ここでは、Factory(およびFactory Method)、複雑な組み立てに使うBuilder、そしてSingleton(その注意点を含む)を扱います。

Simple Factory

Simple Factoryは、生成に関する判断を1つのメソッドの背後に集約します。呼び出し側は論理的な型を要求するだけで、クラス名を知ることなく、設定済みの具象インスタンスを受け取れます。

<?php
interface Notifier { public function send(string $msg): string; }
final class EmailNotifier implements Notifier {
    public function send(string $m): string { return "email: $m"; }
}
final class SmsNotifier implements Notifier {
    public function send(string $m): string { return "sms: $m"; }
}

final class NotifierFactory {
    public static function make(string $channel): Notifier {
        return match ($channel) {
            'email' => new EmailNotifier(),
            'sms'   => new SmsNotifier(),
            default => throw new InvalidArgumentException($channel),
        };
    }
}
echo NotifierFactory::make('sms')->send('Hi'), PHP_EOL;

Factory Method

Factory Methodパターンは、インスタンス化をサブクラスに委ねます。基底クラスは抽象create()を呼び出すアルゴリズムを定義し、各サブクラスが具象プロダクトを決定します。これはOCPと相性がよく、新しいクリエーターを追加しても既存のクラスを編集せずに済みます。

<?php
abstract class ReportExporter {
    abstract protected function create(): string;
    public function export(): string {
        return 'Exported as ' . $this->create();
    }
}
final class PdfExporter extends ReportExporter {
    protected function create(): string { return 'PDF'; }
}
final class CsvExporter extends ReportExporter {
    protected function create(): string { return 'CSV'; }
}
echo (new PdfExporter())->export(), PHP_EOL;
echo (new CsvExporter())->export(), PHP_EOL;

Abstract Factory

Abstract Factoryは、互いに関連し、一緒に使う必要があるオブジェクトのファミリーを生成します(例:MySqlConnectionとMySqlQueryBuilder)。1つのファクトリーがセット全体を生成するため、各部品に互換性があることを保証できます。プロダクトファミリー内の整合性を保つ必要がある場合に使用します。

Builder:課題

オブジェクトに多数のオプションパラメーターが必要になると、コンストラクターは読みにくい位置引数のリストや、段階的に増えるオーバーロードだらけになります。しかもPHPにはオーバーロード自体がありません。Builderパターンは、複雑なオブジェクトを段階的に組み立て、最後に完成した有効なプロダクトを返します。

Fluent Builder

Fluent Builderでは、各セッターから$thisを返し、最後にbuild()で不変の結果を生成します。これによりプロダクト自体を単純に保ち、組み立てのロジックをBuilderに移せます。

<?php
final class Query {
    public function __construct(public readonly string $sql) {}
}
final class QueryBuilder {
    private string $table = '';
    private array $where = [];
    public function from(string $t): self { $this->table = $t; return $this; }
    public function where(string $c): self { $this->where[] = $c; return $this; }
    public function build(): Query {
        $sql = "SELECT * FROM {$this->table}";
        if ($this->where) { $sql .= ' WHERE ' . implode(' AND ', $this->where); }
        return new Query($sql);
    }
}
echo (new QueryBuilder())->from('users')->where('age > 18')->build()->sql, PHP_EOL;

Builderと名前付きコンストラクター

より単純なケースでは、PHP 8の名前付き引数や名前付きコンストラクター(静的ファクトリーメソッド)のほうが、完全なBuilderより適していることがよくあります。

  • すべてのデータが一度にそろっている場合は、名前付き引数を使用します。
  • 組み立てが複数段階にわたる場合、条件付きの場合、または複数の呼び出し箇所で再利用する場合は、Builderを使用します。
<?php
final class Money {
    private function __construct(public readonly int $cents, public readonly string $cur) {}
    public static function fromDollars(float $d): self {
        return new self((int) round($d * 100), 'USD');
    }
    public static function zero(string $cur = 'USD'): self {
        return new self(0, $cur);
    }
}
var_dump(Money::fromDollars(19.99)->cents); // int(1999)

Singleton

Singletonは、グローバルなアクセスポイントを通じてインスタンスが1つだけ存在することを保証します。プライベートコンストラクターと静的アクセサーを使って実装します。プロセス内で本当に1つだけでなければならないものには有用ですが、最も濫用されているパターンでもあります。

<?php
final class Config {
    private static ?self $instance = null;
    private array $data = ['debug' => true];
    private function __construct() {}
    public static function instance(): self {
        return self::$instance ??= new self();
    }
    public function get(string $k): mixed { return $this->data[$k] ?? null; }
}
var_dump(Config::instance()->get('debug'));
var_dump(Config::instance() === Config::instance()); // true

Singletonが問題を引き起こす理由

Singletonは隠れたグローバル状態を導入します。呼び出し側は依存関係を宣言する代わりにConfig::instance()へ直接アクセスするため、テスト容易性と並列性が損なわれます。現代のPHPでは、DIコンテナに共有インスタンスを1つ登録し、「単一インスタンス」のライフサイクルで管理して注入する方法を優先します。グローバルな結合なしに、インスタンスを1つに保てます。

適切なツールを選ぶ

  • Simple Factory/Factory Method:生成に関する判断の背後に具象クラスを隠します。
  • Abstract Factory:互換性のあるプロダクトファミリーを生成します。
  • Builder:複雑なオブジェクトを段階的に組み立てます。
  • Singleton:最後の手段とし、コンテナが管理する共有サービスを優先します。

これら4つはいずれも具象コンストラクターへの結合を減らし、コードを拡張しやすく、テストしやすくします。

レジストリベースのファクトリー

より動的なファクトリーでは、レジストリからクリエーターを検索するため、新しい型をファクトリー自体を編集せずに登録できます(真のOCP)。プラグインシステムやシリアライザーが、実行時に型名をコンストラクターへ解決する仕組みはこれです。

<?php
interface Shape { public function name(): string; }
final class Circle implements Shape { public function name(): string { return 'circle'; } }
final class Square implements Shape { public function name(): string { return 'square'; } }

final class ShapeRegistry {
    private array $creators = [];
    public function register(string $key, callable $factory): void {
        $this->creators[$key] = $factory;
    }
    public function create(string $key): Shape {
        return ($this->creators[$key] ?? throw new InvalidArgumentException($key))();
    }
}
$r = new ShapeRegistry();
$r->register('circle', fn() => new Circle());
$r->register('square', fn() => new Square());
echo $r->create('square')->name(), PHP_EOL; // square

理解度チェック

このシナリオに最適な生成ツールを選んでください。

まとめ

生成パターンを使ってオブジェクトの生成を制御しました。ファクトリーで具象型を隠し、プロダクトファミリーを選択し、Builderで複雑なオブジェクトを流れるように組み立て、Singletonではそのトレードオフを確認しました。現代的な要点は、ファクトリーとBuilderは積極的に使い、SingletonはDIコンテナの共有インスタンス・ライフサイクルに置き換えて、依存関係を明示的かつテスト可能に保つことです。

よくある質問

「生成パターン:Factory、Builder、Singleton」レッスンは無料ですか?

はい。「生成パターン:Factory、Builder、Singleton」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、PHP Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 PHP Academyコースには全4レッスンが含まれています。

「生成パターン:Factory、Builder、Singleton」で何を学びますか?

生成パターンを使って、オブジェクト生成をすっきり制御します。 ブラウザで直接実行するハンズオンコードでPHP Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

PHP Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのPHP Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「生成パターン:Factory、Builder、Singleton」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このPHP Academyレッスンでコードを書いて実行できますか?

はい。すべてのPHP Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. SOLID原則の実践
  2. 生成パターン:Factory、Builder、Singleton
  3. 構造パターン:Adapter、Decorator、Facade
  4. 振る舞いパターン:Strategy、Observer、Command
← PHP Academyに戻る