Структурные паттерны проектирования в PHP

·

4 min read

Структурные паттерны помогают организовать классы и объекты так, чтобы их проще было использовать и расширять.


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!"}'