Начинаю путь программиста. Сказали выучить полиморфизм. Начал читать с разных источников, везде до конца не понятно. Я так понял это очень широкое понятие, что есть основных 3 вида и каждый из них делится еще на многие виды.
Я просто напишу как я понял из того что прочитал и из того что подсказал друг. Прошу подравить меня если я в чем-то ошибся. Буду рад если опишите вкратце что это и 3 основных вида.
Я так понял есть 3 основных вида полиморфизма (примеры на 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
В 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 полиморфизм (мнимая форма).
Это когда есть методы в разных классах которые делают то же самое, им надо дать одинаковое имя и сделать чтобы эти классы реализовывали единый интерфейс или наследовали единый абстрактный класс с этим абстрактным методом.
Еще немного не понятно причем тут Subtype связанный с интерфейсами когда остальные два связанны с параметрами и Параметрический это истинный, а Ad hoc его мнимая форма как я читал.
Честно говоря не силён в современных терминах, но раньше то что вы привели:
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
не работает.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты