Приветствую вас в 4-ом эпизоде сериала “ООП здорового человека”. Здесь речь пойдет о 2-ом принципе SOLID – открытость-закрытость кода…
Теория
Принцип открытости-закрытости. Книга: Вы должны иметь возможность расширять поведение классов, не изменяя его. Вероятно, сложно и непонятно, по крайней мере – это мои ощущения при первой встрече с этой формулировкой. Но немного подумав, я понял, что это объясняется так: класс может быть расширен новыми методами, но при этом дочерний класс никоем образом не переопределяет классы родителя.
Здесь я немного припух: как так – дядя Боб(на деле – первая формулировка сиего принципа принадлежит Бертрану Мейеру) запрещает использовать одну из возможностей ООП? Ну-да… полазив в интернете, посмотрев иные трактовки(а также пример в книге Роба) – нет! Речь идет не о наследовании, а о порождении объекта класса для дальнейшего использования(что, в принципе, не перечит моей трактовке… однако, я не представляю – как не переопределять методы родителя в некоторых ситуациях…).
Речь, как я понял(если откинуть мою трактовку) об объектном полиморфизме. Смысл в том, чтоб при создании метода (вызывающего класс в процессе работы) была предусмотрена возможность изменить вызываемый класс, не изменяя, при этом, сам метод. Кста, мы уже знаем о SRP, поэтому в его контексте: при соблюдении OCP также будет соблюден SRP, поскольку точка зависимости от чужого класса останется одной.
Примеры
№1: печатная машинка и компьютер с принтером; и там и там пользуясь клавиатурой происходит набор текста, затем краска на бумаге отображает набранное. Интерфейс одинаков, результат тоже – различные только механизмы между точками.
Практика
Для иллюстрации этого принципа рассмотрим класс Product
(из этой статьи); а именно – метод получения данных о продукте. Дело в том ,что эти данные где-то лежат; соответственно, нужно установить связь с хранилищем, составить запрос и исполнить его – и всем этим занимается отдельный(-е) класс. Представим это так
<?php class Product { // достать инфо о продукте: // -- дергаем Storage и строим запрос // -- приказываем выполнить его // дергаем Currency и пересчитываем цену продукта } class Storage { // Устанавливаем соединение с хранилищем // выполняем запрос }
Теперь, хранилища могут быть разные(MySQL, MongoDB, обычный файл…); у каждого свой способ соединения и свой способ задать вопрос – в общем свой класс. Этот принцип о том, что класс Storage
должен проектироваться так, чтоб он мог работать с классом любой БД:
<?php class Storage { // Приказ об устанавлении соединения с хранилищем: // -- получим инфу об используемой БД // -- инициализируем новый экземпляр для работы с БД // выполняем запрос }
ну и, как все понимают, классы работы с хранилищами должны иметь одинаковые методы. Для реализации данного принципа построения кода придуманы дизайн-паттерны кода – Стратегия и Шаблонный метод; разберу эти паттерны позже – в этой же статейке я хотел показать, что в подобных ситуациях(когда логика говорит о возможности появлении вариантов развития системы) – используй порождение объекта, а не наследование его класса. Можно конечно и начальный способ оставить – но это чревато ошибками в будущем… и это уже не про мой сериал.