Принципы SOLID в ООП – OCP. Часть 2 из 5.

8 мин. на чтение

Принцип открытости-закрытости (от английского The Open Closed PrincipleOCP) по своей сути является расширением базового понимания ООП – полиморфизма + касаются такого понятия как область видимости и того, что ты вряд ли знал до этой статьи – Interface. Давайте для начала сформулируем принцип открытости-закрытости:

Сущности программирования (классы, методы, модули) должны быть открыты для расширения, но закрыты для модификации.

От себя лично из глубин написанного годами говнокода могу добавить пару нюансов:

  1. Не нужно путать процесс разработки вашего приложения и собственно саму логику работы. Т.е. если вы решили переизобрести велосипед, написав свой собственный MVC для натяжки фронта и забыли добавить поддержку различных методов подключения к БД (mysql, postgre, sqlite…), не нужно пользоваться постоянно extends.  Просто добавьте в само ядро вашего приложения. Это не будет нарушением принципа открытости-закрытости;
  2. Жизненный цикл вашего ПО скорее всего будет малопредсказуемым и строго, на все 100%, следовать принципу вряд ли удастся;
  3. Если вы единственный программист на проекте и это не мега проект, помните про принцип “just works”, KISS и пр. Используйте их куда чаще, чем SOLID. Безусловно круто похвастаться перед коллегами, что в вашем hello world есть отличный шаблонизатор и крутая защита от SQL injection, но в конечном итоге это все выльется в количество часов, которое вряд ли кто-то из клиентов захочет оплачивать. Такие телодвижения (в рамках hello world) приветствуются в академических целях, но не более;
  4. В современных паттернах программирования есть еще такие понятия как структурные шаблоны проектирования, например Facade или Decorator, которыми также можно структурировать функционал вашего веб приложения, если ядро уже написано и нет желания (возможности?) без особых проблем его переписать. На самом деле есть еще такие штуки как Adapter и даже Proxy, но все эти шаблоны заслуживают отдельных статей;
  5. К сожалению, все принципы SOLID, структурных шаблонов проектирования да и вообще ООП настолько непросты, что у 10 человек, которые пройдут одинаковый курс по ним в одной комнате у одного преподавателя сложится ровно 10 пониманий «как на самом деле все работает». Это приводит к тому, что могут возникать все новые и новые принципы и шаблоны (отложенный Facade, порождающая Factory, да мало ли что там еще есть), а старые приходится переосмысливать. Зато Вам всегда будет о чем поговорить с коллегами программистами за бутылкой пива, главное, чтобы обсуждение не переросло в драку 😅

Пример на PHP. Мне очень нравится пример с использованием Interface, потому что он не даст создать какой-то новый код без нужных методов. Еще разок, этот код будет работать и без Interface, но в этом случае, программист может случайно забыть написать нужный метод и упрется в глухую стену непонимания (если например, в классе будет магический метод __call(), а если еще и __callStatic(), то вообще жуть)

Суть задачи: Посчитать площадь круга, причем написать так размашисто, чтобы можно было всегда обращаться к этому функционалу и вдруг чего его даже дорабатывать (на секунду представим, что мы не знаем что такое стрелочные функции). Причем пишем код последовательно, если бы к нам требования поступали поэтапно.

Задача: посчитать площадь круга по радиусу.

<?php 

class Circle {

    public $radius;

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

    public function getArea() {
        
        return $this->radius * $this->radius * pi();
    }
}

$circle = new Circle('25');

echo $circle->getArea(); //1963.4954084936

Но тут приходит к нам PM и говорит, а можно еще написать подсчет площади прямоугольника по заданным размерам сторон? Материмся и пишем дальше.

class Rectangle {
    
    public $width;

    public $height;

    public function __construct($width, $height) {
        
        $this->width = $width;
        
        $this->height = $height;
    }

    public function getArea() {

        return $this->width * $this->height;

    }
}

$rectangle = new Rectangle(10, 20);

echo $rectangle->getArea(); //200

Не успели мы победоносно закрыть крышку любимого макбука и идти пить кофе да рубиться в плойку, как ПМ снова идет к нам к нам. Вы уже даже не моргаете…

– Слушай, а можно как-то унифицировать подсчет площади любой фигуры?

– Да господи ты боже мой!!!!!!! ;%:?*()_)(*?:%;, да какого ;%:?*()(*?:%!

Где-то на этом моменте в голову приходит мысль про OCP, а еще в ярком свете с неба спускается слово Interface. Кофе принесет коллега, а мы перепишем наш код (заметьте – перепишем, без всякого стиснения и изобретения велосипеда).

Для начала мы видимим, что как бы там не сложилось, нам нужно для любой фигуры считать площадь, и в каждом Классе есть getArea(). Так вот она основа для нашего интерфейса! Хрустим костяшками и пишем:

<?php

interface FigureInterface {
	public function getArea();
}

class Circle implements FigureInterface {

	public $radius;

	public function __construct($radius) {

		$this->radius = $radius;

	}

	public function getArea() {

		return $this->radius * $this->radius * pi();

	}
}

class Rectangle implements FigureInterface {

	public $width;

	public $height;

	public function __construct($width, $height) {

		$this->width = $width;

		$this->height = $height;
	}

	public function getArea() {

		return $this->width * $this->height;

	}
}

class SquareCalculator {

	public function calculator($figure) {

		return $figure->getArea();

	}
}

$circle = new Circle(5);

$rectangle = new Rectangle(10, 20);

$result = new SquareCalculator;

echo $result->calculator($circle);

echo $result->calculator($rectangle);

Вот относительно класса SquareCalculator мы и выполнили принцип OCP, отныне он закрыт для модификаций, но открыт для расширений 😎

Ihor Chyshkala
Пишу статьи про ИТ в свободное от работы время.
Оцените автора
Авторский блог Игоря Чишкалы
Добавить комментарий

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.