Структурные паттерны проектирования в PHP
Структурные паттерны помогают организовать классы и объекты так, чтобы их проще было использовать и расширять.
1. Adapter (Адаптер)
Позволяет объекту с одним интерфейсом работать с другим несовместимым интерфейсом.
📌 Когда использовать? Когда у вас есть существующий класс, но его интерфейс не совместим с новой системой.
Пример
// Старая система (некоторые классы мы изменить не можем)
class OldPaymentSystem {
public function makePayment(int $amount): string {
return "Payment of $amount processed through OldPaymentSystem";
}
}
// Новый интерфейс, который ожидает наша система
interface PaymentGateway {
public function pay(int $amount): string;
}
// Адаптер между старой системой и новым интерфейсом
class PaymentAdapter implements PaymentGateway {
private OldPaymentSystem $oldPayment;
public function __construct(OldPaymentSystem $oldPayment) {
$this->oldPayment = $oldPayment;
}
public function pay(int $amount): string {
return $this->oldPayment->makePayment($amount);
}
}
// Использование
$oldPayment = new OldPaymentSystem();
$adapter = new PaymentAdapter($oldPayment);
echo $adapter->pay(100); // "Payment of 100 processed through OldPaymentSystem"
2. Decorator (Декоратор)
Добавляет новую функциональность объекту без изменения его кода.
📌 Когда использовать? Когда нужно динамически расширять объект.
Пример
interface Coffee {
public function cost(): int;
}
class SimpleCoffee implements Coffee {
public function cost(): int {
return 10;
}
}
// Декоратор добавляет новую функциональность
class MilkDecorator implements Coffee {
private Coffee $coffee;
public function __construct(Coffee $coffee) {
$this->coffee = $coffee;
}
public function cost(): int {
return $this->coffee->cost() + 5;
}
}
// Использование
$coffee = new SimpleCoffee();
echo $coffee->cost(); // 10
$milkCoffee = new MilkDecorator($coffee);
echo $milkCoffee->cost(); // 15
3. Facade (Фасад)
Предоставляет упрощенный интерфейс для сложной системы.
📌 Когда использовать? Когда у вас сложная система, и вы хотите сделать удобный входной API.
Пример
phpКопироватьРедактироватьclass SubsystemA {
public function operationA(): string {
return "Subsystem A";
}
}
class SubsystemB {
public function operationB(): string {
return "Subsystem B";
}
}
class Facade {
private SubsystemA $a;
private SubsystemB $b;
public function __construct() {
$this->a = new SubsystemA();
$this->b = new SubsystemB();
}
public function operation(): string {
return $this->a->operationA() . " + " . $this->b->operationB();
}
}
// Использование
$facade = new Facade();
echo $facade->operation(); // "Subsystem A + Subsystem B"
4. Proxy (Заместитель)
Контролирует доступ к объекту, например, добавляет кеширование, ленивую загрузку или проверку прав.
📌 Когда использовать? Когда объект дорогой в создании или нужно контролировать к нему доступ.
Пример
interface Image {
public function display(): string;
}
// Реальный класс
class RealImage implements Image {
private string $filename;
public function __construct(string $filename) {
$this->filename = $filename;
$this->loadFromDisk();
}
private function loadFromDisk(): void {
echo "Loading image from disk: $this->filename\n";
}
public function display(): string {
return "Displaying: $this->filename";
}
}
// Заместитель (ленивая загрузка)
class ProxyImage implements Image {
private ?RealImage $realImage = null;
private string $filename;
public function __construct(string $filename) {
$this->filename = $filename;
}
public function display(): string {
if ($this->realImage === null) {
$this->realImage = new RealImage($this->filename);
}
return $this->realImage->display();
}
}
// Использование
$image = new ProxyImage("photo.jpg");
echo $image->display(); // "Loading image from disk: photo.jpg", "Displaying: photo.jpg"
echo $image->display(); // "Displaying: photo.jpg" (повторная загрузка не происходит)
5. Composite (Компоновщик)
Позволяет объединять группу объектов и работать с ними как с единым объектом.
📌 Когда использовать? Когда элементы системы можно представить в виде дерева.
Пример
interface Component {
public function show(): string;
}
class Leaf implements Component {
private string $name;
public function __construct(string $name) {
$this->name = $name;
}
public function show(): string {
return $this->name;
}
}
class Composite implements Component {
private array $children = [];
public function add(Component $component): void {
$this->children[] = $component;
}
public function show(): string {
return implode(", ", array_map(fn($child) => $child->show(), $this->children));
}
}
// Использование
$tree = new Composite();
$tree->add(new Leaf("File 1"));
$tree->add(new Leaf("File 2"));
$subFolder = new Composite();
$subFolder->add(new Leaf("File 3"));
$tree->add($subFolder);
echo $tree->show(); // "File 1, File 2, File 3"
6. Bridge (Мост)
Разделяет абстракцию и реализацию, позволяя изменять их независимо.
📌 Когда использовать? Когда нужно разделить код на два независимых слоя, например, интерфейс и его реализацию.
Пример
// Реализация
interface Renderer {
public function render(string $text): string;
}
class HTMLRenderer implements Renderer {
public function render(string $text): string {
return "<p>$text</p>";
}
}
class JSONRenderer implements Renderer {
public function render(string $text): string {
return json_encode(["text" => $text]);
}
}
// Абстракция
class Page {
protected Renderer $renderer;
public function __construct(Renderer $renderer) {
$this->renderer = $renderer;
}
public function renderPage(): string {
return $this->renderer->render("Hello, World!");
}
}
// Использование
$htmlPage = new Page(new HTMLRenderer());
echo $htmlPage->renderPage(); // "<p>Hello, World!</p>"
$jsonPage = new Page(new JSONRenderer());
echo $jsonPage->renderPage(); // '{"text":"Hello, World!"}'