生成パターン: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フィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- SOLID原則の実践
- 生成パターン:Factory、Builder、Singleton
- 構造パターン:Adapter、Decorator、Facade
- 振る舞いパターン:Strategy、Observer、Command