Какие паттерны проектирования вы знаете и применяли?

Ответ

В своей практике на PHP я чаще всего применяю следующие паттерны:

1. Singleton (Одиночка) Использовал для глобального доступа к конфигурации или подключению к базе данных, чтобы избежать множественных инстансов.

class DatabaseConnection {
    private static ?self $instance = null;
    private function __construct() { /* ... */ }
    private function __clone() {}

    public static function getInstance(): self {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
}
// Использование
$db = DatabaseConnection::getInstance();

2. Factory (Фабрика) Применял для создания семейства связанных объектов, например, разных типов уведомлений (Email, SMS, Push).

interface Notification {
    public function send(string $message): void;
}

class NotificationFactory {
    public static function create(string $type): Notification {
        return match($type) {
            'email' => new EmailNotification(),
            'sms' => new SmsNotification(),
            default => throw new InvalidArgumentException('Unknown notification type')
        };
    }
}

3. Strategy (Стратегия) Использовал для инкапсуляции различных алгоритмов сортировки или способов оплаты (CreditCard, PayPal), которые можно подменять в рантайме.

interface PaymentStrategy {
    public function pay(float $amount): bool;
}

class Order {
    private PaymentStrategy $paymentStrategy;
    public function setPaymentStrategy(PaymentStrategy $strategy): void {
        $this->paymentStrategy = $strategy;
    }
    public function processPayment(float $amount): bool {
        return $this->paymentStrategy->pay($amount);
    }
}

4. Repository (Репозиторий) Активно применяю в Laravel-проектах для абстракции доступа к данным, что упрощает тестирование и смену источника данных.

// AppRepositoriesUserRepository.php
class UserRepository {
    public function findById(int $id): ?User {
        return User::find($id);
    }
    public function getAllActive(): Collection {
        return User::where('is_active', true)->get();
    }
}

Также сталкивался и применял Observer (для событийной модели), Decorator (для расширения функциональности) и Adapter (для интеграции со сторонними API).

Ответ 18+ 🔞

А, паттерны, говоришь? Ну, это ж как инструменты в гараже — одни нужны постоянно, другие лежат на всякий пожарный, а третьи вообще хер поймёшь, зачем нужны, пока в конкретную жопу не влезешь. Расскажу, с чем чаще всего вожусь, а то сидишь тут, подозрение ебать чувствую, что думаешь, будто я только на костылях из глобальных переменных и пишу.

1. Singleton (Одиночка) Этот, бля, как тот одинокий стул на кухне — все им пользуются, но больше одного на всю квартиру не надо. Я его обычно для подключения к базе юзаю, чтобы не плодить сущностей, а то приложение жрёт память, как не в себя. Смотри, как просто:

class DatabaseConnection {
    private static ?self $instance = null;
    private function __construct() { /* ... */ }
    private function __clone() {}

    public static function getInstance(): self {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
}
// Использование
$db = DatabaseConnection::getInstance();

Главное — конструктор приватный, чтобы какой-нибудь распиздяй не начал новые инстансы плодить. Один на весь проект и всё, доверия ебать ноль ко всем остальным.

2. Factory (Фабрика) Ну, это когда тебе нужно что-то создать, но заранее хрен знает что именно. Типа, приходят уведомления: то письмо, то смс, то ещё какая мартышлюшка. Вместо того чтобы везде if-ами мозги выносить, заводишь фабрику — она тебе нужную хрень и склепает.

interface Notification {
    public function send(string $message): void;
}

class NotificationFactory {
    public static function create(string $type): Notification {
        return match($type) {
            'email' => new EmailNotification(),
            'sms' => new SmsNotification(),
            default => throw new InvalidArgumentException('Unknown notification type')
        };
    }
}

Красота же! Пришёл тип 'sms' — получи объект для смсок. Пришла хуйня — получи исключение в морду. Всё честно.

3. Strategy (Стратегия) Вот это, бля, мощная штука. Представь, у тебя есть заказ и куча способов оплаты: картой, через PayPal, криптой ещё, ёпта. Можно, конечно, нагородить switch на пол-экрана, а можно сделать по-человечески. Стратегия — это когда ты выносишь каждый алгоритм в отдельный класс, а потом подсовываешь его основному объекту, как патрон в ствол.

interface PaymentStrategy {
    public function pay(float $amount): bool;
}

class Order {
    private PaymentStrategy $paymentStrategy;
    public function setPaymentStrategy(PaymentStrategy $strategy): void {
        $this->paymentStrategy = $strategy;
    }
    public function processPayment(float $amount): bool {
        return $this->paymentStrategy->pay($amount);
    }
}

Захотел сменить платёжку — просто подсунул другую стратегию. Удивление пиздец, как сразу жить проще становится.

4. Repository (Репозиторий) Особенно в Laravel'е это прям святое. Вместо того чтобы тыкать запросы к базе по всему коду, как слепой кот, ты собираешь их в одном месте — в репозитории. Тестировать в разы легче, да и если завтра базу поменять, не придётся пол-проекта переписывать.

// AppRepositoriesUserRepository.php
class UserRepository {
    public function findById(int $id): ?User {
        return User::find($id);
    }
    public function getAllActive(): Collection {
        return User::where('is_active', true)->get();
    }
}

Выглядит просто, но, бля, сколько нервов экономит. Это как иметь отдельный ящик для всех отвёрток, а не искать их по всему полу.

Ну и ещё, конечно, всякое бывало: Observer для событий (типа, "эй, все, кто подписался, вот вам уведомление!"), Decorator, чтобы обернуть функциональность, как луковицу, и Adapter, когда нужно впихнуть невпихуемое — старый кривой API заставить работать с твоей красивой системой. В общем, инструментов овердохуища, главное — понимать, какой и когда в руки брать, а не тыкать всем подряд, как полупидор.