Природа возложила на каждую особь обязанность. Если обязанность не выполнена — особь умирает. Но выполнив ее, она все равно умирает. Природа равнодушна.
Обращаю ваше внимание на то, что эта запись является архивной. Ей уже больше 4 лет. Это означает, во-первых, то, что мое мнение по поднятым в ней вопросам могло измениться (хотя, конечно, не обязательно), а во-вторых, то, что я не испытываю никакого желания эти вопросы обсуждать, и поэтому комментарии и оценки я отключил.
Полиморфизм — одна из трех основных парадигм ООП. Если говорить кратко, полиморфизм — это способность обьекта использовать методы производного класса, который не существует на момент создания базового. Для тех, кто не особо сведущ в ООП, это, наверно, звучит сложно. Поэтому рассмотрим применение полиморфизма на примере.
Предположим, на сайте нужны три вида публикаций — новости, объявления и статьи. В чем-то они похожи — у всех них есть заголовок и текст, у новостей и объявлений есть дата. В чем-то они разные — у статей есть авторы, у новостей — источники, а у объявлений — дата, после которой оно становится не актуальным.
Самые простые варианты, которые приходят в голову — написать три отдельных класса и работать с ними. Или написать один класс, в которым будут все свойства, присущие всем трем типам публикаций, а задействоваться будут только нужные. Но ведь для разных типов аналогичные по логике методы должны работать по-разному. Делать несколько однотипных методов для разных типов (get_news, get_announcements, get_articles) — это уже совсем неграмотно. Тут нам и поможет полиморфизм.
Грубо говоря, это класс-шаблон. Он реализует функциональность только на том уровне, на котором она известна на данный момент. Производные же классы ее дополняют. Но, пора перейти от теории к практике. Сразу оговорюсь, рассматривается примитивный пример с минимальной функциональностью. Все объяснения — в комментариях в коде.
abstract class Publication
{
// таблица, в которой хранятся данные по элементу
protected $table;
// свойства элемента нам неизвестны
protected $properties = array();
// конструктор
public function __construct($id)
{
// обратите внимание, мы не знаем, из какой таблицы нам нужно получить данные
$result = mysql_query ('SELECT * FROM `'.$this->table.'` WHERE `id`="'.$id.'" LIMIT 1');
// какие мы получили данные, мы тоже не знаем
$this->properties = mysql_fetch_assoc($result);
}
// метод, одинаковый для любого типа публикаций, возвращает значение свойства
public function get_property($name)
{
if (isset($this->properties[$name]))
return $this->properties[$name];
return false;
}
// метод, одинаковый для любого типа публикаций, устанавливает значение свойства
public function set_property($name, $value)
{
if (!isset($this->properties[$name]))
return false;
$this->properties[$name] = $value;
return $value;
}
// а этот метод должен напечатать публикацию, но мы не знаем, как именно это сделать, и потому объявляем его абстрактным
abstract public function do_print();
}
Теперь можно перейти к созданию производных классов, которые и реализуют недостающую функциональность.
class News extends Publication
{
// конструктор класса новостей, производного от класса публикаций
public function __construct($id)
{
// устанавливаем значение таблицы, в которой хранятся данные по новостям
$this->table = 'news_table';
// вызываем конструктор родительского класса
parent::__construct($id);
}
// переопределяем абстрактный метод печати
public function do_print()
{
echo $this->properties['title'];
echo '<br /><br />';
echo $this->properties['text'];
echo '<br />Источник: '.$this->properties['source'];
}
}
class Announcement extends Publication
{
// конструктор класса объявлений, производного от класса публикаций
public function __construct($id)
{
// устанавливаем значение таблицы, в которой хранятся данные по объявлениям
$this->table = 'announcements_table';
// вызываем конструктор родительского класса
parent::__construct($id);
}
// переопределяем абстрактный метод печати
public function do_print()
{
echo $this->properties['title'];
echo '<br />Внимание! Объявление действительно до '.$this->properties['end_date'];
echo '<br /><br />'.$this->properties['text'];
}
}
class Article extends Publication
{
// конструктор класса статей, производного от класса публикаций
public function __construct($id)
{
// устанавливаем значение таблицы, в которой хранятся данные по статьям
$this->table = 'articles_table';
// вызываем конструктор родительского класса
parent::__construct($id);
}
// переопределяем абстрактный метод печати
public function do_print()
{
echo $this->properties['title'];
echo '<br /><br />';
echo $this->properties['text'];
echo '<br />© '.$this->properties['author'];
}
}
Суть в том, что один и тот же код используется для обьектов разных классов.
// наполняем массив публикаций объектами, производными от Publication
$publications[] = new News($news_id);
$publications[] = new Announcement($announcement_id);
$publications[] = new Article($article_id);
foreach ($publications as $publication) {
// если мы работаем с наследниками Publication
if ($publication instanceof Publication) {
// то печатаем данные
$publication->do_print();
} else {
// исключение или обработка ошибки
}
}
Вот и все. Легким движением руки брюки превращаются в элегантные шорты :-).
Основная выгода полиморфизма — легкость, с которой можно создавать новые классы, «ведущие себя» аналогично родственным, что, в свою очередь, позволяет достигнуть расширяемости и модифицируемости. В статье показан всего лишь примитивный пример, но даже в нем видно, насколько использование абстракций может облегчить разработку. Мы можем работать с новостями точно так, как с объявлениями или статьями, при этом нам даже не обязательно знать, с чем именно мы работаем! В реальных, намного более сложных приложениях, эта выгода еще ощутимей.
Евгений, 14-го августа 2008 года в 23:28
Самая лучшая статья про полиморфизм, к тому же с понятным примером :—).
vasa_c, 16-го сентября 2008 года в 13:57
Полиморфизм это несколько другое.
А наследование это одно из множества инструментов для достижения полиморфизма.
sap, 16-го сентября 2008 года в 15:22
Прошу не забывать, что это для начинающих. Наследование, в данном случае, самый понятный пример.
test1"><script>, 3-го ноября 2008 года в 16:14
</textarea>test
sap, 3-го ноября 2008 года в 22:41
Делать нефиг?)