В отличие от двух предыдущих лекций, я не буду заставлять вас самостоятельно искать определение полиморфизма.
При подготовке к лекции я убедился, что поиск "полиморфизм php" занятие гибельное.
Поэтому прочтите лучше статьи о "полиморфизм java". Мне понравились следующие: Полиморфизм в Java, Объектно-ориентированное программирование. Полиморфизм.
Но главная суть полиморфизма: "Один интерфейс, множество реализаций". Давайте рассмотрим примеры на php.
Начнем с самого простого. Как я говорил раньше, с php5 появился стандартный интерфейс Countable. Он состоит из 1 метода count(). Этот интерфейс могут имплементировать совершенно разные классы: от объектов коллекций до обычных моделей.
В приведенном выше примере у нас есть классы и есть какой-то интерфейс, и мы обязываем классы их реализовать.
Но к этому вопросу можно подойти и с другой стороны. У нас есть задача для некоего сервиса: есть данные и нужно их отсортировать, при этом желательно иметь несколько реализаций.
Но у этого решения проблема с динамическим выбором алгоритма. А также, алгоритмов сортировки много, а сервис у нас отвечает не только за сортировку.
Несовсем корректно копить столько кода в классе. Привлекательнее вариант с тем, чтобы разнести алгоритмы по классам.
При таком исполнении не очень понятно, а что стало лучше. Бросается в глаза проблема с наименованием метода в классе сортировок.
Разумно их заменить на просто count.
Но теперь случилась ситуация, что у нас несколько реализаций одного и того же, при этом наш код имеет только условные договоренности, что метод будет называться count. И возникает естественное желание эту условную договоренность перевести в письменные обязательства.
Теперь осталась одна проблема, рассмотрение которой не совсем входит в текущий курс.
Данный код тяжело будет протестировать. Так как объект сортировке создается непосредственно в методе doSomething.
<?php/** * Dmitry Petrov <dmitry.petrov@opensoftdev.ru> */$sortAlgorithm=newQuickSort();//$sortAlgorithm = new ShellSort();$service=newService($sortAlgorithm);$service->doSomething();
Дальше мы не будем производить рефакторинг.
Отмечу еще раз факт, во втором примере мы не создаем еще одну реализацию интерфейса.
Мы в процессе рефакторинга приходим к тому, что у нас существуют как минимум две разные реализации одного и того же, которые хочется стандартизировать. Стоит обратить внимание на конструктор класса Service. Он принимает не конкретную реализацию, и сам класс работает теперь не с чем-то конкретным. Наш Service теперь лишь знает о интерфейсе SortInterface и его интересует лишь только это. Он сможет работать с любым классом, который заключил контракт и обязался выполнять его условия, а значит Service сможет воспользоваться любым количеством реализаций.
У нас на проекте есть более сложные примеры, где в аналогичной ситуации вынесено несколько методов под интерфейс, и несколько классов его имплементируют. Уместно задать вопрос, а почему тогда не воспользоваться наследованием?
В принципе, вам никто не мешает это сделать. Но, если общие методы говорят вам о преимуществах полезной пищи, при этом один класс говорит о тараканах, другой о человеке, третий о автомобилях, четвертый о войне. В этой ситуации, на мой взгляд, наличие общего родителя лишь введет в заблуждение.