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