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