Comments 82
Спасибо, исправился. Просто первый мой пост на хабре. Надеюсь, не последний.
Наглядный пример.

Вывести трехмерный куб можно используя OpenGL или D3D или и т.д. В результате будет несколько разных кодов для вывода куба: код под OpenGL, код под D3D, код под и т.д. Функция которую вы вызываете для вывода куба назвали RenderCube(x, y, z, width).

В своей программе чтобы нарисовать куб вы вызываете RenderCube причем выполняться будет та реализация рендеринга, которая вам нужна: OpenGL или D3D или и т.д. Получается неважно как вы рендерите в результате на экране появляется куб ну и конечно он должен выглядеть одинакого, не зависимо чем вы его вывели OpenGL, D3D и т.д.

Псевдо код:

interface Render {
virtual void RenderCube(x, y, z, width) = PURE // Пустой метод
}

class OpenGLRender наследуем от Render {
void RenderCube(x, y, z, width) {
// Реализация вывода куба через OpenGL
}
}

class D3DRender наследуем от Render {
void RenderCube(x, y, z, width) {
// Реализация вывода куба через D3D
}
}

// Выводим куб.

Render *pRender; // Указатель на абстракный рендер

// Я хочу выводить через OpenGL

pRender = new OpenGLRender;
pRender->RenderCube(20, 50, 100, 64);

// Тут же в коде решил вывести через D3D

pRender = new D3DRender;
pRender->RenderCube(20, 50, 100, 64);

Главное преимущество то, что можно разнести реализацию по модулям и повысить скорость разработки программной части. Архитектор планирует всю систему оперируя сущностями. Он все делает на базе пустых методов ему важно где этот куб показать, но не КАК он будет там выводится, двум другим разработчикам он дает задание: ты делаешь вывод куба для OpenGL, а ты для D3D. Работа мгновенно распараллелилась. Каждый делает свою работу. Причем программист для OpenGL пишет свою библиотеку, а программист для D3D свою. Потом уже архитектор динамически подключает их БЕЗ КОМПИЛЯЦИИ основного приложения и видит результат.

На этом базируется COM к примеру.
дельное дополнение :) по предыдущей статье не скажешь что это про полиморфизм.
Напишите пожалуйста про ввод метода в действие с учетом сигнатуры.
UFO landed and left these words here
UFO landed and left these words here
UFO landed and left these words here
Достаточно
public function func($a, $b = null) { if ($a ... }
или
public function func() { if (func_get_arg()... if (func_get_args()... }
>Достаточно
>public function func($a, $b = null) { if ($a… }

это чуть не то, это для задания значения по умолчанию

>или
>public function func() { if (func_get_arg()… if (func_get_args()… }

как вариант. только несильно очевидно из-за пустых скобок func(), в случае с __call там сразу видно что все не так просто, а тут () могут сбить с толку.
В оригинале было:
public int func(int a, int b){ } public int func(int a){ } public int func(double a, double b){ }
Я реализую это так:
public function($a, $b = null) { if (is_int($a) && is_null($b)) { вторая функция из оригинала } }
В оригинале было:
public int func(int a, int b){}
public int func(int a){ }
public int func(double a, double b){ }

Я реализую это так:

public function($a, $b = null) {
if (is_int($a) && is_null($b)) { первая функция из оригинала }
elseif (is_int($a) && is_int($b)) { вторая функция из оригинала }
elseif (is_float($a) && is_float($b)) { третья функция из оригинала }
else trigger_error('Bad usage', E_USER_ERROR);
}

Я знаю про двойную проверку is_int($a), но так код удобен и понятен а потеря в скорости смешна.

Таким образом и подсказки в IDE есть и всё работает. Именно это я имел в виду с самого начала.
UFO landed and left these words here
Нет, интерфейс только определяет публичные функции и переменные, тогда как абстрактный класс может содержать их конкретную реализацию.
может но не должен. я к тому что в статье понятия не мешали — при желании можно использовать абстракные классы как каркас(интерфейс). (естественно при наличии в языке отдельной сущности интерфейса и отсутствии множественного наследования логичней использовать интерфейсы)
Ну а где противоречие? ;) corp не сказал ведь, что асбтрактный класс — это по сути интерфейс. Тут бы Ваш аргумент подошел. А сейчас нет.
Ничего автор не смешивает. Для объяснения полиморфизма можно испльзовать как понятие класса, так и интерфейса. Для взаимозаменяемость объектов важен тип. А он уже может быть как интерфейсным, так и нет. Хотя понятно, что интерфейс для этого подходит гораздо лучше, потому что, как Вы правильно заметили, реализовывать можно несколько интерфейсов. Кстати утверждение «класс — это интерфейс + реализация» абсолютно не верно, т.к. класс не обязательно реализовывает какой-то интерфейс.
> т.к. класс не обязательно реализовывает какой-то интерфейс.

смотря что вы понимаете под определением интерфейса…
«Множество сигнатур всех определенных для объекта операцией называют интерфейсом это го объекта.»

Приемы объектно-ориентированного проектирования. Паттерны проектирования
Гамма, Хелм, Джонсон, Влиссидес

Не вижу противоречия в том, что каждый класс объявляет и реализует свой интерфейс.
Этот интерфейс будет определять общее поведение всех своих наследников

Я думаю, что понятия стоит разделять.
Интерфейс — это интерфейс, он ничего не говорит о реализации.
А поведение — реализация, которой интерфейс лишен.

Все таки, пример с циклом не показателен в PHP. В массив можно поместить объекты, которые не реализуют одинаковый интерфейс, но имеют одноименные методы. Надо было привести пример с функцией, требующей в качестве параметра объект, реализующий определенный интерфейс.

Еще не плохо бы привести пример с наследованием интерфейса, а не класса.
Только интерфейс не наследуется а реализуется. В php это имеет ключевое значение.
Все отличие использования именно интерфейса, а не абстрактного класса сводится к следующему:
вместо
abstract class Publication {
пишем
interface Publication {

и вместо строк вида
class News implements Publication {

за очевидностью этот пример даже не приводил
> за очевидностью этот пример даже не приводил
для вас очевидно, для других совсем не очевидно, что два класса могут разделять интерфейс и не иметь общего предка.
Поскольку такая масса конструктивных комментариев то
Неплохо бы организовать статью в которую включить все дополонения и примеры из комментариев. своего рода вики по ООП в PHP
Зачем делать то, что сделано за нас: есть первокласная документация на php.net, в ней, правда на английском языке есть комментарии, и если вы сравните документацию и данный мегааафлууд;) в документации все тоже, коротенько и с примерами, при том примеры интереснее намного, да еще куча нюансов в коментах.
вы сами подумали, что написали? или это такая манера выпендриться
зачем тогда вообще пишут статьи, если практически есть доки?
зачем вы сами пошли читать эту статью?
Прежде чем написать я подумал и если почитать те ссылки, что я давал в обсуждении прошлой статьи, то становится видно, что данная статья и предыдущая, так же как и коментарии из разряда «лень залезть в документацию». В то время как в той же документации есть ряд моментов, которые действительно требуют статей и решений. Если все-таки посмотреть документации целиком, а так же поизучать зендовые разработки в области фреймворков то становится видно, что приведенный пример — выдранный с мясом кусок из новых возможностей разработке на php. Если грамотно объединить, сократить и дополнить все, что здесь написано -то получится страница документации.
P.S.статью прочел исключительно из любопытства.
P.P.S Кстати… прежде чем так меня критиковать, вы внимательно прочли www.php.net/manual/ru/language.oop5.php :)
кому и что вы сейчас пытаетесь доказать? или просто пиар?
про «один метод с определенным именем» не понял. т.е. пхп не позволит объявить два метода
class PhpClass() { }
я не знаю как удалять а мессага сама собой отправилась… блин.
скажите пожалуйста, а есть ли в PHP какие-нибудь средства для поиска ошибок, связанных с неправильным определением полиморфных функций?

например, вы переопределили функцию в классе наследнике, но немного ошиблись со списком аргументов и новая функция стала отдельной, не полиморфной

подскажет ли кто-нибудь мне о моей ошибке, потому как самому такое будет не очень просто найти?
Если класс имплементирует интерфейс (как это должно быть сделано по идее), то PHP выкинет фатальную ошибку, сказав, что объявление метода в классе не соответствует объявлению в интерфейсе.
ну с интерфейсом или абстрактным классом все понятно
а если это просто переопределение функции в классах-наследниках?
Тогда программист пожнет плоды своего нежелания пользоваться абстрактными классами и интерфейсами :)
вывод понятен ) нету, в общем )

кстати, не все наследование можно заменить на реализацию абстрактных функций
Ситуация чуть интереснее:
Будет вываливаться E_WARNING каждый раз, когда методу будет передано меньше параметров, чем он принимает. Вне зависимости от того, вызывается ли метод родительского или дочернего класса.

Вот пример:
Warning: Missing argument 3 for c:: f(), called in /tmp/script.php on line 23 and defined in /tmp/script.php on line 14
А вы возьмите приведённый пример, «ошибитесь» со списком аргументов и увидите, что будет. :)

Новая функция не может стать «отдельной».
1) есть интерфейсы, они определяют обязательный набор методов и свойств
www.php.net/manual/ru/language.oop5.interfaces.php
2) в пятерке есть возможность определять типы передаваемых в метод данных
www.php.net/manual/ru/language.oop5.typehinting.php
3) и наконец есть очень мощный инструмент проверки
www.php.net/manual/ru/language.oop5.reflection.php
Хотелось бы отметить, что в динамических языках программирования полиморфизм есть как бы «всегда». Можно накидать в массив объекты, классы которых не имеющие общих предков. И если у них у всех есть метод с одинаковой сигнатурой, его можно вызвать у всех объектов. Это, конечно же, не best-practice, и пример автора с интерфейсами куда правильнее.

Но в динамическом языке программирование работает именно позднее связывание, т.е. возможность вызова метода у объекта определяется только во время выполнения.

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

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

PHP не может проконтролировать это на этапе компиляции, посколько еще не известен тип значения переменной.
Да, согласен, прошу прощения, не очень четко написал. Хотел сказать: как можно раньше найти ошибку, не ошибку вида «у объекта нет метода хххх», а вида «ожидали объект Класс1, а получили типа Класс2». Спасибо.
Вы написали хорошую статью в продолжение темы, плюс вам.

Но — «что такое полиморфизм на самом деле» —звучит так, как будто вы опровергаете мою статью и пишете что-то противоположное. Это же не так, ваша статья во многом перекликается с моей и, по-моему, нигде ее не опровергает :)
Просто на самом деле вы написали скорее о наследовании, абстракции и переопределении методов, но сама суть полиморфизма продемонстрирована не была. Полиморфизм проявляется в использовании написанных методов/классов, у вас же были только сами эти методы/классы.
Да в АС3 такая фигня тоже есть, и в яваскрипте наверняка тоже, это же классика ООП, неужели такие вещи, которые обучно ВУЗах преподают необходимо публиковать на хабре?
PHP даже школьники вовсю используют. А что касается обучения в ВУЗах, то оно уже совсем не то. Я уверен, что многих, «прошедших» эту тему в ВУЗе, вопрос «что такое полиморфизм» поставит в тупик.
Вы что? Это про какой же ВУЗ интересно. Наследование, полимофизм, классы, интерфейсы, пакаджы — одни из основных концепций обьектно ориентированого программирования. И неважно на сиплюсплюс человек пишет или на пиейчпи, если он учился на программиста, то для него это будет обычным вопросом. В ВУЗах похлеще преподают, от низкоуровнего, системного программирования, ООП — это так, баловство с изящным синтаксисом, по сравнению с более принципиальными концепциями.
«Наследование, полимофизм, классы, интерфейсы, пакаджы — одни из основных концепций обьектно ориентированого программирования.» — разве с этим кто-то спорит?
«И неважно на сиплюсплюс человек пишет или на пиейчпи...» — с этим я тоже согласен. Но ключевое здесь, что человек ПИШЕТ. Зачастую тема дается, но она проходит мимо студента. И бывает так, что человек начинает в относительно больших количествах писать код через определенный срок после прохождения темы.
Словом если говорить о школьниках, надо все таки понимать что знакть как пишется строчка static class blablabla{ }; Это одно. А понимать до конца что из себя представляет статический класс, или статический метод, это уже другое.
Да в АС3 такая фигня тоже есть, и в яваскрипте наверняка тоже, это же классика ООП, неужели такие вещи, которые обучно ВУЗах преподают необходимо публиковать на хабре?

Уж лучше публиковать такие топики, чем потребительские топики про побрякушки «скоро выйдет новый гаджет, хочу, хочу, хочу». Это IT-ресурс и кому-то наверняка это будет полезно. Или на Хабр не пускают тех, кто ещё не окончил ВУЗ?
Возможно я ошибаюсь, но мне кажется что более подошел бы такой пример для иллюстрации полиморфизма:

class Publication {
private $text;
protected function setText($text){
$this->text = $text;
}
public function __construct($text) {
$this->setText($text);
}
public function do_print(){
echo $this->text;
};
}

class News extends Publication {
public function __construct($text) {
parent::__construct($text);
}
}
class Announcement extends Publication {
public function __construct($text) {
parent::__construct($text);
}
}

class Article extends Publication {
public function __construct($text) {
parent::__construct($text);
}
}

$publications[] = new News('Новость');
$publications[] = new Announcement('Объявление');
$publications[] = new Article('Статья');

foreach ($publications as $publication) {
if ($publication instanceof Publication)
$publication->do_print();
}

Если ошибаюсь, то поправьте
В том то и дело что нет :)
Вы пользуетесь другой парадигмой — наследованием, а полиморфизм это то, о чем написал автор
ОФФТОП:
Латерея на Хабре! Найди топик, где первое сообщение не:
— Хабракат, уважаемый!
а ответ не:
— Извиняюсь, эт ППНХ!
И получи уникальный утюг Super Plus 3000 и сборно-разборный скворечник! Первые 10 получат еще по килограмму вишни!
Респект! Все правильно расписано про полиморфизм. Вся его прелесть как раз и состоит во взаимозаменяемости объектов с одинаковым интерфейсом. Очень многие (обычно новички) действительно ошибочно полагают, что полиморфизм это всего лишь переопределение методов. Ничего не могу сказать относительно примера на php, так как сам пишу на C#.
Да но без приведения типов полиморфизм получается какойто неполный. Отсюда и путаница. Просто php программистам надо заглюнуть в Java, C# И все сразу встанет на свои места.
И вообще, полезно изучать программирование как таковое, не просто веб-программирование или php-программирование.
Раскрыт только один аспект применения полиморфизма через динамическое связывание и наследование.

Но совершенно забыли про перегрузку функций и методов.

Вообще это типичная ошибка объяснения полиморфизма только через наследование и переопределение поведения.

Шаблоны и подобные им инструменты также являются примером полиморфизма, но более высокого порядка.
Если вы про плюсовые шаблоны, то это статический полиморфизм и не более высокого порядка, а просто другой тип полиморфизма. Что касается PHP, то трудно описывать реализации полиморфизма в терминах языков с более жесткой типизацией ;)
Не путайте теплое с мягким. Перегрузка методов никогда не отражает сути полиморфизма.
Я бы в этом примере создал Iterator «PublicationCollection» который наследовал бы ArrayIterator и переписал метод append добавив туда type hinting:

public function append(Publication $Publication) {
    parent:: append($Publication);
}

Таким образом в foreach цыкле мы бы были уверены, что в коллекции содержаться только обьекты этого типа. Дополнительную логику которая относится к коллекции публикации можно было было также сделать методом этой коллекции. Например создать метод print, который проходится по всем публикациям в коллекции и вызывает на каждой соответсвенно метод do_print.

$publications = new PublicationCollection;
$publications->append(new News);
$publications->append(new Announcement);
$publications->append(new Article);

$publications->print();

Это усложнило бы пример, зато ярче показало бы пользу полиморфизма.

Полиморфизм это также перегрузка методов(method overloading), то есть, возможность вызвать метод одного класса с абсолютно разным набором аргументов(в идеале в IDE других языков, при наборе функции появляюся несколько возможных вариантов вызова). Есть пример реализации подобной возможности в PHP 5.
Как сказанно выше в PHP можно использовать методы с динамическим количеством аргументов и проверять instanceof. Таким образом можно писать методы зависимые от типов аргументов. Это конечно не настолько мощно как в Java, но всё же элегантнее чем __call.
После проверки обоих вариантов «на прочность», согласен. В случае instanceof мы хотя бы видим, что такая функция вообще существует. Да и код менее «раскидан».
Сама же его суть заключается в том, что мы гарантируем себе, что все методы интерфейса будут вести себя «правильно» независимо от того, какой конкретно производный класс используется.


Про непосредственно поведение вызываемого метода полиморфизм ничего не говорит. Полиморфизм связан только с интерфейсом. Он не дайт никаких гарантий, что, имплементируя один и тот же интерфейс, или, определяя абстрактные методы, программист не наплодит классов, функции которых имеют совершенно разное поведение, и соответсвенно объекты будут несовместимы между собой в рамках используемого интерфейса. То, что вы имеете в виду называется принцип подстановки Лисков, который и гооврит о построении концептуально правильных иерархий.
Мне кажется, что вместо абрактного класса следует использовать интерфейс, поскольку все классы наследующие интерфейс должны обязательно реализовать описанные методы в интерфейсе. Что даёт гарант того, что класс нельзя будет использовать до тех пор, пока он не будет готов хотя бы с методами заглушками.
Абстрактные методы тоже надо обязательно перекрывать, но я за пример с интерфейсом, как более общий.
За что минус в карму? Мне было интересно, действительно интересно читать эту статью (особенно — после статьи s-a-p'а). Может я не сильно в теме, объясните тогда, пожалуйста… но что-то ведь всё же стало для меня лично яснее, и я — как могу — выражаю свою благодарность автору. «Любой прохожий может стать твоим учителем»(не помню, кто так говорил). Так вот, а если этому прохожему сказать «СПАСИБО» за миг просветления, разве мир станет хуже? Или хабра станет хуже?
UFO landed and left these words here
UFO landed and left these words here
Я лично под полиморфизмом понимаю, что есть класс родитель и есть класс наследник, в классе наследнике мы хотим мы хотим переопределить( в некоторых случаях просто дополнить ) метод класса наследника, но если мы не будем использовать полиморфизм произойдет затирание сигнатуры метода в памяти, после чего мы не сможем его вызвать. Ни знаю как в php, но в других языках нужно использовать модификатор virtual, что бы метод был полиморфным, если такой модификатор используется, то в памяти создается таблица виртуальных методов. А имплементация интерфейса, думаю, к полиморфизму не относится.

Сильно не критикуйте, это всего лиш моя мысль, а не утверждение.
Правильно ля я размышляю, если нет, то где моя ошибка?
Only those users with full accounts are able to leave comments. Log in, please.