PHP: полиморфизм, инкапсуляция и SOLID

Решил написать краткую напоминалку себе и другим, а то в просторах инета любят расписывать замудрено.

Советую начать с статьи про полиморфизм и инкапсуляцию и ниже закрепить знания + прочитать про SOLID.

Инкапсуляция в PHP

Инкапсуляцию можно сравнить с работой автомобиля с точки зрения типичного водителя. Многие водители не разбираются в подробностях внутреннего устройства машины, но при этом управляют ею именно так, как было задумано. Пусть они не знают, как устроен двигатель, тормоз или рулевое управление, — существует специальный интерфейс, который автоматизирует и упрощает эти сложные операции. Сказанное также относится к инкапсуляции и ООП — многие подробности "внутреннего устройства" скрываются от пользователя, что позволяет ему сосредоточиться на решении конкретных задач. В ООП эта возможность обеспечивается классами, объектами и различными средствами выражения иерархических связей между ними.

В программном смысле этого слова, инкапсуляция - это скрытие переменных в классе от пользователя. Т.е. все переменные (свойства) класса Вы делаете скрытыми (private), а методы открытыми (public). Т.е. можно будет взаимодействовать со свойствами класса через методы.
Прелесть данного подхода в том, что в методах можно вставить код для проверки всяких ошибок, и потом о них не думать, когда вызывается метод.

Полиморфизм в PHP

Что такое полиморфизм в PHP:

  1. возможность использовать вместо экземпляров родительского класса экземпляры подкласса (наследование)
  2. возможность функции принимать на вход разные типы данных, это достигается с помощью таких видов полиморфизма:
    • параметрический - функция, принимает разные объекты, которые имеют одноименную функцию (обычно это реализуется с помощью абстрактного класса или интерфейса с функцией, которую нужно реализовать)
    • приведение типов - функция выполняет неявное приведение входящих данных к типу, который она понимает
    • полиморфизм включения (inclusion polymorphism) - когда функция проверяя тип входящих данных, работает по-разному

Пример: пользователь хочет отследить последние обновления публикаций, причем ему не важно, будут это статьи или новости или что-то еще.

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());

Касательно SOLID

Хочется внести ясность в принципы Liskov substitution и Dependency inversion, т.к. теоретически сложно понять в чем их отличие, но я попробую.

Liskov substitution гласит: 

Функции, которые используют базовый тип, должны иметь возможность использовать подтипы базового типа, не зная об этом.

Означает, что защищенному методу (private, final) разрешается вызывать методы, которые реализованы в классах, которые отнаследованны от текущего класса.

Подклассы не могут замещать поведения базовых классов. Подтипы должны дополнять базовые типы.

Означает, что расширяя базовый класс (используя extend), переопределять методы базового класса нельзя (инкапсуляция), поэтому в примере выше, мы написали final (см. код выше).

Dependency inversion гласит:

Зависимости внутри системы строятся на основе абстракций

Означает, что класс Publication должен быть абстрактным или интерфейсом, и затем именно его как тип данных, нужно объявлять при передаче аргумента в функции (полиморфизм - см. выше вызовы printPublicationName).

Модули верхнего уровня не зависят от модулей нижнего уровня. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций

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

Источник: 1 - 2 - 3 - 4

Оцени публикацию:
  • 12,58
Оценили: 12


Предложения и пожелания:

 

youtube.com/watch?v=7hFivbgIEqk

При полном или частичном использовании материалов данного сайта, ссылка на сайт "yapro.ru" обязательна как на источник информации.
Автоматический импорт материалов и информации с сайта запрещен.
Copyrights © 2007 - 2019 YaPro.Ru

Лебеденко Николай Николаевич
Ошибка в тексте? Выделите её мышкой и нажмите: Ctrl + Enter