Найти в Дзене

SOLID принципы (примеры на PHP)

1. Single Responsibility (Принцип единственной ответственности) 📌 Один класс — одна задача. ❌ Плохо: Класс Order занимается всем: валидацией, сохранением в БД, отправкой email. class Order
{
public function validate(): bool { /* ... */ }
public function save(): void { /* ... */ }
public function sendEmail(): void { /* ... */ }
} ✅ Хорошо: Разделяем логику на отдельные классы. class OrderValidator { /* ... */ }
class OrderRepository { /* ... */ }
class OrderNotifier { /* ... */ } 2. Open-Closed (Принцип открытости/закрытости) 📌 Классы должны быть открыты для расширения, но закрыты для изменений. ❌ Плохо: Добавление нового типа скидки требует правки метода. class DiscountCalculator
{
public function applyDiscount(string $type, float $price): float
{
if ($type === 'black_friday') {
return $price * 0.7;
} elseif ($type === 'new_year') {
return $price * 0.8;
}
// Новый тип? Еще один `elseif`? Нет, спасибо!
}

1. Single Responsibility (Принцип единственной ответственности)

📌 Один класс — одна задача.

Плохо: Класс Order занимается всем: валидацией, сохранением в БД, отправкой email.

class Order
{
public function validate(): bool { /* ... */ }
public function save(): void { /* ... */ }
public function sendEmail(): void { /* ... */ }
}

Хорошо: Разделяем логику на отдельные классы.

class OrderValidator { /* ... */ }
class OrderRepository { /* ... */ }
class OrderNotifier { /* ... */ }

2. Open-Closed (Принцип открытости/закрытости)

📌 Классы должны быть открыты для расширения, но закрыты для изменений.

Плохо: Добавление нового типа скидки требует правки метода.

class DiscountCalculator
{
public function applyDiscount(string $type, float $price): float
{
if ($type === 'black_friday') {
return $price * 0.7;
} elseif ($type === 'new_year') {
return $price * 0.8;
}
// Новый тип? Еще один `elseif`? Нет, спасибо!
}
}

Хорошо: Используем стратегии (интерфейсы).

interface DiscountStrategy
{
public function apply(float $price): float;
}

class BlackFridayDiscount implements DiscountStrategy { /* ... */ }
class NewYearDiscount implements DiscountStrategy { /* ... */ }

class DiscountCalculator
{
public function applyDiscount(DiscountStrategy $strategy, float $price): float
{
return $strategy->apply($price);
}
}

3. Liskov Substitution (Принцип подстановки Барбары Лисков)

📌 Наследники должны работать так же, как родители.

Плохо: Дочерний класс нарушает контракт родителя.

class Bird
{
public function fly(): void { /* ... */ }
}

class Penguin extends Bird
{
public function fly(): void
{
throw new Exception("Пингвины не летают!"); // Нарушение LSP!
}
}

Хорошо: Разделяем интерфейсы.

interface Bird
{
public function eat(): void;
}

interface FlyingBird extends Bird
{
public function fly(): void;
}

class Penguin implements Bird { /* ... */ }
class Eagle implements FlyingBird { /* ... */ }

4. Interface Segregation (Принцип разделения интерфейсов)

📌 Клиенты не должны зависеть от методов, которые они не используют.

Плохо: Один «жирный» интерфейс.

interface Worker
{
public function work(): void;
public function eat(): void;
public function sleep(): void;
}

class Robot implements Worker
{
public function eat(): void
{
throw new Exception("Роботы не едят!"); // Зачем это роботу?
}
}

Хорошо: Разделяем на маленькие интерфейсы.

interface Workable
{
public function work(): void;
}

interface Eatable
{
public function eat(): void;
}

class Human implements Workable, Eatable { /* ... */ }
class Robot implements Workable { /* ... */ }

5. Dependency Inversion (Принцип инверсии зависимостей)

📌 Зависите от абстракций, а не от конкретики.

Плохо: Прямая зависимость от MySQL.

class OrderService
{
private MySQLDatabase $db;

public function __construct()
{
$this->db = new MySQLDatabase(); // Жесткая привязка
}
}

Хорошо: Работаем через интерфейс.

interface DatabaseInterface {
public function save(Order $order): void;
}

class OrderService {
public function __construct(private DatabaseInterface $db) {}
}