Объясните принципы SOLID на примерах PHP.php-33

SOLID — это пять основных принципов объектно-ориентированного программирования, которые делают код более понятным, гибким и поддерживаемым. Разберем каждый принцип с реалистичными примерами на PHP.

1. Single Responsibility Principle

Принцип единственной ответственности: Класс должен иметь только одну причину для изменения.

Нарушение принципа:

class Order {
    public function calculateTotal() { /* ... */ }
    public function saveToDatabase() { /* ... */ }
    public function sendEmail() { /* ... */ }
}

Соблюдение принципа:

class Order {
    public function calculateTotal() { /* ... */ }
}

class OrderRepository {
    public function save(Order $order) { /* ... */ }
}

class OrderNotifier {
    public function sendConfirmation(Order $order) { /* ... */ }
}

2. Open/Closed Principle

Принцип открытости/закрытости: Классы должны быть открыты для расширения, но закрыты для модификации.

Нарушение принципа:

class AreaCalculator {
    public function calculate($shapes) {
        foreach ($shapes as $shape) {
            if ($shape instanceof Rectangle) {
                $area += $shape->width * $shape->height;
            } elseif ($shape instanceof Circle) {
                $area += $shape->radius * $shape->radius * pi();
            }
            // Добавляем новые условия для новых фигур
        }
    }
}

Соблюдение принципа:

interface Shape {
    public function area();
}

class Rectangle implements Shape {
    public function area() {
        return $this->width * $this->height;
    }
}

class Circle implements Shape {
    public function area() {
        return $this->radius * $this->radius * pi();
    }
}

class AreaCalculator {
    public function calculate(Shape ...$shapes) {
        foreach ($shapes as $shape) {
            $area += $shape->area();
        }
    }
}

3. Liskov Substitution Principle

Принцип подстановки Барбары Лисков: Подклассы должны быть заменяемы на свои базовые классы.

Нарушение принципа:

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

class Penguin extends Bird {
    public function fly() {
        throw new Exception('Penguins cannot fly!');
    }
}

Соблюдение принципа:

class Bird {
    // Общие методы для всех птиц
}

class FlyingBird extends Bird {
    public function fly() { /* ... */ }
}

class Penguin extends Bird {
    // Пингвины не летают, но имеют другие методы
}

4. Interface Segregation Principle

Принцип разделения интерфейсов: Клиенты не должны зависеть от методов, которые они не используют.

Нарушение принципа:

interface Worker {
    public function work();
    public function eat();
}

class HumanWorker implements Worker {
    public function work() { /* ... */ }
    public function eat() { /* ... */ }
}

class RobotWorker implements Worker {
    public function work() { /* ... */ }
    public function eat() { /* Не имеет смысла для робота */ }
}

Соблюдение принципа:

interface Workable {
    public function work();
}

interface Eatable {
    public function eat();
}

class HumanWorker implements Workable, Eatable {
    public function work() { /* ... */ }
    public function eat() { /* ... */ }
}

class RobotWorker implements Workable {
    public function work() { /* ... */ }
}

5. Dependency Inversion Principle

Принцип инверсии зависимостей: Зависимости должны строиться на абстракциях, а не на конкретных реализациях.

Нарушение принципа:

class MySQLConnection {
    public function connect() { /* ... */ }
}

class UserRepository {
    private $connection;

    public function __construct() {
        $this->connection = new MySQLConnection();
    }
}

Соблюдение принципа:

interface DatabaseConnection {
    public function connect();
}

class MySQLConnection implements DatabaseConnection {
    public function connect() { /* ... */ }
}

class UserRepository {
    private $connection;

    public function __construct(DatabaseConnection $connection) {
        $this->connection = $connection;
    }
}

Комплексный пример SOLID-приложения

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

class CreditCardPayment implements PaymentMethod {
    public function pay(float $amount): bool {
        // Логика оплаты кредитной картой
        return true;
    }
}

class PayPalPayment implements PaymentMethod {
    public function pay(float $amount): bool {
        // Логика оплаты через PayPal
        return true;
    }
}

class PaymentProcessor {
    private $paymentMethod;

    public function __construct(PaymentMethod $paymentMethod) {
        $this->paymentMethod = $paymentMethod;
    }

    public function process(float $amount): bool {
        return $this->paymentMethod->pay($amount);
    }
}

// Использование
$paymentMethod = new CreditCardPayment();
$processor = new PaymentProcessor($paymentMethod);
$processor->process(100.00);

Резюмируем:

  • SRP: Каждый класс решает одну задачу
  • OCP: Расширяйте функциональность через новые классы, а не изменяя существующие
  • LSP: Подклассы должны полностью заменять родительские классы
  • ISP: Много специализированных интерфейсов лучше одного общего
  • DIP: Зависите от абстракций, а не от конкретных реализаций

Следование SOLID принципам делает код более:

  1. Понятным и читаемым
  2. Легко тестируемым
  3. Гибким к изменениям
  4. Удобным для повторного использования
  5. Устойчивым к ошибкам