Порождающие паттерны в PHP
Порождающие (Creational) паттерны предназначены для управления созданием объектов. Они помогают сделать код более гибким, удобным для расширения и тестирования.
1. Singleton (Одиночка)
Гарантирует, что у класса есть только один экземпляр.
Все реализации одиночки сводятся к тому, чтобы скрыть конструктор по умолчанию и создать публичный статический метод, который и будет контролировать жизненный цикл объекта-одиночки.
Если у вас есть доступ к классу одиночки, значит, будет доступ и к этому статическому методу. Из какой точки кода вы бы его ни вызвали, он всегда будет отдавать один и тот же объект.
Пример
class Singleton {
private static ?self $instance = null;
private function __construct() {} // Закрытый конструктор
private function __clone() {} // Запрещаем клонирование
private function __wakeup() {} // Запрещаем десериализацию
public static function getInstance(): self {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
}
// Использование
$instance1 = Singleton::getInstance();
$instance2 = Singleton::getInstance();
var_dump($instance1 === $instance2); // true
2. Factory Method (Фабричный метод)
Определяет интерфейс для создания объектов, но оставляет выбор типа создаваемого объекта подклассам.
Пример
interface Transport {
public function deliver(): string;
}
class Truck implements Transport {
public function deliver(): string {
return "Delivery by truck";
}
}
class Ship implements Transport {
public function deliver(): string {
return "Delivery by ship";
}
}
abstract class Logistics {
abstract public function createTransport(): Transport;
public function planDelivery(): string {
$transport = $this->createTransport();
return $transport->deliver();
}
}
class RoadLogistics extends Logistics {
public function createTransport(): Transport {
return new Truck();
}
}
class SeaLogistics extends Logistics {
public function createTransport(): Transport {
return new Ship();
}
}
// Использование
$logistics = new RoadLogistics();
echo $logistics->planDelivery(); // "Delivery by truck"
3. Abstract Factory (Абстрактная фабрика)
Позволяет создавать семейства взаимосвязанных объектов без указания их конкретных классов.
Пример
interface Button {
public function render(): string;
}
class WindowsButton implements Button {
public function render(): string {
return "Windows Button";
}
}
class MacButton implements Button {
public function render(): string {
return "Mac Button";
}
}
interface GUIFactory {
public function createButton(): Button;
}
class WindowsFactory implements GUIFactory {
public function createButton(): Button {
return new WindowsButton();
}
}
class MacFactory implements GUIFactory {
public function createButton(): Button {
return new MacButton();
}
}
// Использование
function renderGUI(GUIFactory $factory) {
$button = $factory->createButton();
echo $button->render();
}
$factory = new WindowsFactory();
renderGUI($factory); // "Windows Button"
4. Builder (Строитель)
Позволяет создавать сложные объекты поэтапно.
Пример
class House {
public array $parts = [];
public function show(): void {
echo implode(", ", $this->parts) . "\n";
}
}
interface Builder {
public function buildWalls(): void;
public function buildRoof(): void;
public function getResult(): House;
}
class ConcreteHouseBuilder implements Builder {
private House $house;
public function __construct() {
$this->house = new House();
}
public function buildWalls(): void {
$this->house->parts[] = "Walls";
}
public function buildRoof(): void {
$this->house->parts[] = "Roof";
}
public function getResult(): House {
return $this->house;
}
}
class Director {
public function construct(Builder $builder): House {
$builder->buildWalls();
$builder->buildRoof();
return $builder->getResult();
}
}
// Использование
$builder = new ConcreteHouseBuilder();
$director = new Director();
$house = $director->construct($builder);
$house->show(); // "Walls, Roof"
5. Prototype (Прототип)
Позволяет копировать объекты вместо создания новых.
Пример
class Prototype {
public string $name;
public array $data;
public function __construct(string $name, array $data) {
$this->name = $name;
$this->data = $data;
}
public function __clone() {
$this->data = array_merge([], $this->data);
}
}
// Использование
$original = new Prototype("Prototype 1", ["item1", "item2"]);
$clone = clone $original;
$clone->name = "Cloned Prototype";
$clone->data[] = "item3";
print_r($original); // ['name' => 'Prototype 1', 'data' => ['item1', 'item2']]
print_r($clone); // ['name' => 'Cloned Prototype', 'data' => ['item1', 'item2', 'item3']]