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

Принцип открытости/закрытости — принцип объектно-ориентированного программирования, устанавливающий следующее положение: «программные сущности (классы, модули, функции и т. п.) должны быть открыты для расширения, но закрыты для изменения»; это означает, что такие сущности могут позволять менять свое поведение без изменения их исходного кода. Это особенно значимо в производственной среде, когда изменения в исходном коде потребуют проведение пересмотра кода, модульного тестирования и других подобных процедур, чтобы получить право на использования его в программном продукте. Код, подчиняющийся данному принципу, не изменяется при расширении и поэтому не требует таких трудозатрат.

Это просто означает, что класс должен быть легко расширяемый без изменения самого класса. Давайте взглянем на класс AreaCalculator, особенно на метод sum.

public function sum() {
     foreach($this->shapes as $shape) {
         if(is_a($shape, 'Square')) {
             $area[] = pow($shape->length, 2);
         } else if(is_a($shape, 'Circle')) {
             $area[] = pi() * pow($shape->radius, 2);
         }
     }
 
     return array_sum($area);
}

Если мы захотим суммировать площади для большего количества фигур, нам прийдется добавить больше if/else блоков и это идет вразрез с принципом открытости/закрытости.

Для улучшения этого метода нам нужно удалить логику вычисления площади для каждой фигуры и добавить ее в класс фигуры.

class Square {
     public $length;
 
     public function __construct($length) {
         $this->length = $length;
     }
 
     public function area() {
         return pow($this->length, 2);
     }
}
    

Тоже самое нужно сделать для класса Circle. Теперь вычисление площади любой фигуры стало намного проще:

public function sum() {
     foreach($this->shapes as $shape) {
         $area[] = $shape->area;
     }
 
     return array_sum($area);
}

Теперь мы можем создавать классы для других фигур и вычислять их площадь, не нарушая код. Однако, возникает другая проблема, как мы узнаем, что новый класс представляет собой фигуру и у него есть метод вычисления площади?

Программирование с помощью интерфейсов является неотъемлемой частью S.O.L.I.D. Приведем пример классов наших фигур с использованием интерфейсов:

interface ShapeInterface {
     public function area();
}
 
class Circle implements ShapeInterface {
     public $radius;
 
     public function __construct($radius) {
         $this->radius = $radius;
     }
 
     public function area() {
         return pi() * pow($this->radius, 2);
     }
}

Теперь в методе sum нашего класса AreaCalculator мы можем проверить, являются ли представленные объекты реализацией интерфейса ShapeInterface, в противном случае можно создать исключение:

public function sum() {
     foreach($this->shapes as $shape) {
         if(is_a($shape, 'ShapeInterface')) {
             $area[] = $shape->area();
             continue;
         }
 
         throw new AreaCalculatorInvalidShapeException;
     }
 
     return array_sum($area);
}

Теги: Solid, Ocp, Open closed principle, Принцип открытости/закрытости


Похожие статьи

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

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

1. SPR (Single responsibility principle) - Принцип единственной обязанности

4. ISP (Interface segregation principle) - Принцип разделения интерфейса