Что такое полиморфизм и какие его основные виды?

273
12 марта 2019, 10:30

Начинаю путь программиста. Сказали выучить полиморфизм. Начал читать с разных источников, везде до конца не понятно. Я так понял это очень широкое понятие, что есть основных 3 вида и каждый из них делится еще на многие виды.

Я просто напишу как я понял из того что прочитал и из того что подсказал друг. Прошу подравить меня если я в чем-то ошибся. Буду рад если опишите вкратце что это и 3 основных вида.

Я так понял есть 3 основных вида полиморфизма (примеры на php):

  1. Ad hoc полиморфизм — несколько объявлений функции с одинаковым именем но с отличающимися параметрами, пример:

При вызове функции выберется та которой соответствует тип аргумента.

function test (string $a, string $b) {
    return $a . $b;
}
function test (int $a, int $b) {
    return $a + $b;
}
// если бы в пхп поддерживалось, то вывело бы
test('a', 'b') // 'ab'
test(1, 4) // 5
  1. Параметрический полиморфизм — когда будет выполнятся одна и та же функция вне зависимости от типов аргументов.

В php он поддерживается.

function test (string $a, string $b) {
    return $a . $b;
}
function test (int $a, int $b) {
    return $a + $b;
}
// если бы в пхп поддерживалось, то вывело бы
test('a', 'b') // 'ab'
test(1, 4) // 5

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

  1. Subtype полиморфизм — насколько понял это самое популярное понятие полиморфизма.

Это когда есть методы в разных классах которые делают то же самое, им надо дать одинаковое имя и сделать чтобы эти классы реализовывали единый интерфейс или наследовали единый абстрактный класс с этим абстрактным методом.

Еще немного не понятно причем тут Subtype связанный с интерфейсами когда остальные два связанны с параметрами и Параметрический это истинный, а Ad hoc его мнимая форма как я читал.

Answer 1

Честно говоря не силён в современных терминах, но раньше то что вы привели:

Ad hoc полиморфизм

Параметрический полиморфизм

Называлось "перегрузка функции/метода". Что же до

Subtype полиморфизм

Это действительно и сегодня важно. В чём суть - в комментарии дали хорошую ссылку на статью. Но не круто, когда для обучения дают неживые примеры: такое в PHP не увидишь.

А вот примеры, которые я добавлю: вижу в PHP каждый день, и мне кажется они помогут раскрыть суть этого понятия. В БД почти каждого сайта есть пользователи, статьи, фотографии. Для универсальной работы с рядами БД есть паттерн ActiveRecord , который полиформизм использует наиболее явно. К примеру есть классы, описывающие ряды в БД:

class User extends ActiveRecord
class Article extends ActiveRecord
class Photo extends ActiveRecord

Сам ActiveRecord имеет на борту методы save() , setFromArray(), getId() . Примеры, как работают с ActiveRecord-ами:

//1
$user = new User(1234);//получение юзера с id=1234 из БД
$user->first_name = 'Евпатий';
$user->save();
//2
$article = new Article(1234);
$article->setFromArray($formdata);
$article->save();
echo('Article '. $article->getId(). ' saved');

Например перед сохранением конкретно статьи мы хотим сделать триггер, и сбросить кеш, для этого расширяем поведение метода save:

class Article extends ActiveRecord
{
...
public function save()
{
 $this->resetSomeArticleCache();
 return parent::save();
}
}

Таким образом где-то в коде может существовать универсальный контроллер, задача которого обновить некое поле, которое есть у многих записей:

public function upgradeModifyDateAction()
{
  $type = $_GET['type'];
  $id = (int)$_GET['id'];
  //полиморфное получение ряда тут максимально наглядно описано, обычно это как-то Core::getRow($type, $id) 
  if (is_subclass_of($type, 'ActiveRecord') 
      && ($row = new $type($id)) 
      && $row->isExists()
      && $row->fieldExists('modify_date')
  ){
     $row->modify_date = date('Y-m-d H:i:s');
     $row->save();// !! полиформизм здесь !!
  }else{/*ошибка*/}
}

Суть в том, что контроллеру совершенно неважно, какой конкретно ActiveRecord его попросили обновить, и если у объекта есть "специальное поведение" на обновление, то оно выполнится: то есть для статьи в примере вызовется метод resetSomeArticleCache. При этом специальное поведение прозрачно для контроллера - в этом суть.

Главная суть, это экономия памяти программиста при использовании полиморфизма: представьте, что в системе не 3, а 100 различных ActiveRecord-ов. Вам чтобы поработать одним из таких объектов(или группой) не надо помнить ни название таблицы в которой он хранится, ни название колонки id, не надо думать - однороден ли список объектов прежде чем прогонять его в цикле: достаточно запомнить - что есть метод getId() который вернёт id, и есть метод save() - который сохранит объект в БД.

Как и не надо думать, про то что "так, а если я обновлю статью в коде - мне ещё и кешик надо чистануть", вставлять ненужные if-ы. Поэтому это важно - с полиморфизмом жить проще, говорить что это нечто особенное нельзя, сегодня это понятие в общем то неотделимо от наследования в любых совр. языках.

Говоря про типы полиморфизма, в PHP часто ещё используют "утиный полиморфизм" - некоторые считают это антипаттерном, а некоторые восхваляют. По мне так он в меру полезен:

/*например функция проверки прав $user на $object*/
public function checkRights($user, $object, $right = 'edit')
{
  ...
  if (method_exists('isExtraAllowed', $object)){
   if (($result = $object->isExtraAllowed($user, $right)) != self::IGNORE)
    return $result;
  }
  ...
}

В примере дело в том том что заводить базовую ф-ю isExtraAllowed в ActiveRecord было бы кощунственно, так как ActiveRecord не относится к правам в принципе - и мы размажем вполне себе чёткий класс. А необходимость добавления вот такой утино-полиморфной вставки как isExtraAllowed возникает в реальных проектах на моей памяти всегда (где, конечно широко используется ООП), особенно если систему прав ACL ввели далеко после начала проекта.
Да, в примере можно было бы вместо утиного подхода добавить интерфейс назвав к его к примеру IACLExtendedProvider, но во первых это забьёт глобальный список классов и интерфейсов(и мозг программиста вместе с тем), во вторых программист, который не привык к интерфейсам войдёт в ступор, когда не будет понимать, почему добавленный им метод isExtraAllowed не работает.

READ ALSO
не работает Php exec

не работает Php exec

Пытаюсь выполнить команды например:

165
Получить переменную из PHP в скрипт

Получить переменную из PHP в скрипт

В файле readphp находится массив

127
DOM - вывести текст с тегом

DOM - вывести текст с тегом

Пример: Текст<span>1</span>

148
Как вывести ноль перед числом С++ [дубликат]

Как вывести ноль перед числом С++ [дубликат]

На данный вопрос уже ответили:

139