Решил написать краткую напоминалку себе и другим, а то в просторах инета любят расписывать замудрено.
Советую начать с статьи про полиморфизм и инкапсуляцию и ниже закрепить знания + прочитать про SOLID.
Инкапсуляцию можно сравнить с работой автомобиля с точки зрения типичного водителя. Многие водители не разбираются в подробностях внутреннего устройства машины, но при этом управляют ею именно так, как было задумано. Таким образом многие подробности "внутреннего устройства" скрываются от пользователя, что позволяет пользователю сосредоточиться на решении конкретных задач. В ООП эта возможность обеспечивается классами, объектами и различными средствами выражения иерархических связей между ними.
В программном смысле этого слова, инкапсуляция - это скрытие данных и/или работы с данными в классе. Например: все переменные (свойства) класса Вы делаете скрытыми (private), а методы открытыми (public), т.е. можно будет взаимодействовать со свойствами класса через методы.
Что такое полиморфизм в PHP:
Пример параметрического полиморфизма: пользователь отслеживает последние обновления публикаций, причем ему не важно, будут это статьи или новости.
abstract class Publication {
// определяем правило, что все публикации должны печататься, т.е. иметь метод do_print()
abstract protected function do_print();
// используем final чтобы инкапсулировать логику работы данного метода
final protected function getClassName(){
$this->do_print();// SOLID - Liskov substitution - первое предложение
return __CLASS__;
}
}
class News extends Publication {
// переопределяем абстрактный метод печати
public function do_print() {
echo 'Новость';
}
}
class Announcement extends Publication {
// переопределяем абстрактный метод печати
public function do_print() {
echo 'Объявление';
}
}
//Наполняем массив публикаций объектами, производными от Publication
$publications[] = new News();
$publications[] = new Announcement();
// пример полиморфизма:
foreach ($publications as $publication) {
if ($publication instanceof Publication) { // Если мы работаем с наследниками Publication
$publication->do_print(); // то мы можем смело выводить данные на печать
} else {
//исключение или обработка ошибки
}
}
function printPublicationName(Publication $publication){
// пример использования метода, который инкапсулирован:
$publication->getClassName();
}
printPublicationName(new News());
printPublicationName(new Announcement());
Обусловимся: программная сущность - компонент/модуль/класс/функция.
SRP (Single Responsibility Principle) - принцип единственной обязанности говорит о том, что программная сущность не должна делать много дел сразу. Однако, некоторые разработчики злоупотребляют данным принципом, в результате появляется классы в которых есть только одна функция, что приводит к излишней сложности приложения.
Таким образом: классы нужно делать небольшие, но не мелкие.
OCP (Open Closed Principle) гласит, программная сущность должна быть открыта для расширения, но закрыта для изменения (например для класса/функции это означает наличие инструкции final). Принцип описывает подход к разработке, который говорит - однажды разработанная реализация программной сущности в дальнейшем требует только исправления ошибок, а разработка нового функционала или его улучшение требуют создания новой программной сущности. Эта новая программная сущность может переиспользовать существующую программную сущность через механизм наследования/композиции ( инъекция зависимости).
Таким образом: следует использовать композицию (инъекцию зависимостей), не следует использовать наследование (extend), исключение - абстрактный класс. Если нужно изменить логику класса, то создаем декоратор.
Liskov substitution (принцип подстановки Барбары Лисков)
Функции, которые используют базовый тип, должны иметь возможность использовать подтипы базового типа, не зная об этом.
другими словами: если класс B является подтипом А, то в программе все места использования класса А могут быть замещены на использование класса B
Означает: если отнаследовал какой-то класс, то не меняй ему контракт
Подклассы не могут замещать поведения базовых классов. Подтипы должны дополнять базовые типы.
Означает: расширяя базовый класс (используя extend), не стоит переопределять методы базового класса (в примере выше, мы написали final, но это нарушает первый пункт Барбары Лисков).
Dependency inversion гласит:
Зависимости внутри системы строятся на основе абстракций
Означает: класс (например Publication) должен быть абстрактным или интерфейсом, и затем именно его как тип данных, нужно объявлять при передаче аргумента в функции (полиморфизм - см. выше вызовы printPublicationName).
Модули верхнего уровня не зависят от модулей нижнего уровня. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций
Означает: наследоваться от абстрактного класса (или интерфейса) можно, а наследовать абстрактный класс от обычного нельзя (иначе можно попасть в полную задницу наследований).
SDP (Stable Dependencies Principle) - принцип устойчивых зависимостей.
SAP (Stable Abstractions Principle) принцип устойчивости абстракций, устанавливает связь между устойчивостью и абстрактностью.
Устойчивость компонента пропорциональна его абстрактности. Если высокоуровневые правила поместить в устойчивые компоненты, это усложнит изменение исходного кода, реализующего их. Это может сделать всю архитектуру негибкой. Как компонент с максимальной устойчивостью (I = 0) сделать гибким настолько, чтобы он сохранял устойчивость при изменениях? Ответ заключается в соблюдении принципа открытости/закрытости (OCP). Этот принцип говорит, что можно и нужно создавать классы, достаточно гибкие, чтобы их можно было наследовать (расширять) без изменения. Какие классы соответствуют этому принципу? Абстрактные.
Принципы устойчивости абстракций (SAP) и устойчивых зависимостей (SDP) вместе соответствуют принципу инверсии зависимостей (DIP) для компонентов. Это верно, потому что принцип SDP требует, чтобы зависимости были направлены в сторону устойчивости, а принцип SAP утверждает, что устойчивость подразумевает абстрактность. То есть зависимости должны быть направлены в сторону абстрактности.
Однако принцип DIP сформулирован для классов, и в случае с классами нет никаких полутонов. Класс либо абстрактный, либо нет. Принципы SDP и SAP действуя в отношении компонентов и допускают ситуацию, когда компонент частично абстрактный или частично устойчивый.
Источники 1 - 2 - 3 - 4 - 5 - 6
Раньше думал и описывал так:
Liskov substitution (принцип подстановки Барбары Лисков)
Функции, которые используют базовый тип, должны иметь возможность использовать подтипы базового типа, не зная об этом.
Означает: пиши класс так, что его методы подменят при наследовании.
Это скорее требования к языку программирования:
1. защищенный метод (final/private) должен иметь возможность вызывать методы, которые реализованы в классах, которые унаследованы от текущего класса (в PHP это достигается с помощью ключевого слова static, например static::myFunction() ).
2. программная сущность пользующаяся явной зависимостью A, не должна ломаться в следствии замены A на B (при условии что B наследуется от A)