SOLID Principles

blog

1.S - Принцип на единствената отговорност (Single Responsibility Principle, SRP)

Един клас трябва да има само една причина да се променя.

Пример:

class User {
    public function createUser() { /* ... */ }
    public function deleteUser() { /* ... */ }
    public function logError($error) { /* записване на грешка в лог */ }
}

Това нарушава SRP, тъй като имаме две различни отговорности. По-добър подход би бил:

class User {
    public function createUser() { /* ... */ }
    public function deleteUser() { /* ... */ }
}

class Logger {
    public function logError($error) { /* записване на грешка в лог */ }
}

2. O - Принцип на отвореност-затвореност (Open-Closed Principle, OCP)

Класовете трябва да са отворени за разширение, но затворени за промяна.
Пример:

Ако имаме клас, който изчислява общата сума на поръчка:

class OrderTotal {
    public function calculateTotal($order) {
        return $order->price + ($order->price * 0.2); // добавя 20% ДДС
    }
}

Ако искаме да добавим различни такси, трябва да променяме класа. Вместо това можем да разширим функционалността чрез полиморфизъм:

interface Tax {
    public function apply($price);
}

class VATTax implements Tax {
    public function apply($price) {
        return $price * 0.2;
    }
}

class OrderTotal {
    protected $tax;

    public function __construct(Tax $tax) {
        $this->tax = $tax;
    }

    public function calculateTotal($order) {
        return $order->price + $this->tax->apply($order->price);
    }
}

L - Принцип на заменяемост на Лисков (Liskov Substitution Principle, LSP)

Обекти от базов клас трябва да могат да бъдат заменени с обекти от подклас без да променят коректното поведение на програмата.
Пример:

class Rectangle {
    protected $width;
    protected $height;

    public function setWidth($width) { $this->width = $width; }
    public function setHeight($height) { $this->height = $height; }
    public function getArea() { return $this->width * $this->height; }
}

class Square extends Rectangle {
    public function setWidth($width) {
        $this->width = $width;
        $this->height = $width; // това нарушава LSP
    }

    public function setHeight($height) {
        $this->height = $height;
        $this->width = $height; // това нарушава LSP
    }
}

Вместо да наследяваме "Rectangle", трябва да имаме разделени абстракции за двата типа фигури или да използваме композиция.

I - Принцип на разделение на интерфейсите (Interface Segregation Principle, ISP)

Никой не трябва да бъде принуден да зависи от интерфейси, които не използва.
Пример:

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() { /* Robots don't eat! This method doesn't make sense. */ }
}

Вместо това:

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() { /* ... */ }
}

D - Принцип на обратната зависимост (Dependency Inversion Principle, DIP)

Нивата на високо ниво не трябва да зависят от нивата на ниско ниво. И двете трябва да зависят от абстракции.
Пример:

class LightBulb {
    public function turnOn() { /* ... */ }
    public function turnOff() { /* ... */ }
}

class Switch {
    private $bulb;

    public function __construct(LightBulb $bulb) {
        $this->bulb = $bulb;
    }

    public function operate() {
        // ...
    }
}

Това нарушава DIP, защото "Switch" зависи директно от "LightBulb". По-добро решение би било да използваме интерфейс:

interface Switchable {
    public function turnOn();
    public function turnOff();
}

class LightBulb implements Switchable {
    public function turnOn() { /* ... */ }
    public function turnOff() { /* ... */ }
}

class Switch {
    private $device;

    public function __construct(Switchable $device)



Share: