Лекция в виде презентации в формате pdf с примерами - 27 слайдов.
ВолгГТУ, кафедра ПОАС, - 2010 год
В лекции рассмотрены все формы полиморфизма функций и методов т представлена их иерархия в виде схемы.
Class MyEllipse
{
public:
float area() const
};
{
public:
float area() const
{//использ. более эффективный алгоритм расчета
}
};
MyEllipse ellipse;
MyCircle circle;
// Будет вызван метод MyEllipse::area()
ellipse.print();
// ВНИМАНИЕ!!! Будет вызван метод MyEllipse::area()
circle.print();
Class MyEllipse
{
public:
virtual float area() const
{ /* численный метод расчета */ }
void print() { printf("area = %f\n", area()); }
};
class MyCircle: public MyEllipse
{
public:
float area() const
{ //использ. более эффективн. алгоритм расчета
return 3.14*Radius1*Radius2;
}
};
MyEllipse ellipse;
MyCircle circle;
// Будет вызван метод MyEllipse::area()
printf("Ellipse area= %f\n", ellipse.area());
// Будет вызван метод MyEllipse::area()
ellipse.print();
// Будет вызван метод MyCircle::area()
printf("Circle area= %f\n", circle.area());
// ВНИМАНИЕ!!! Будет вызван метод MyCircle::area()
circle.print();
Полиморфизм — одна из трех основных парадигм ООП. Если говорить кратко, полиморфизм — это способность обьекта использовать методы производного класса, который не существует на момент создания базового. Для тех, кто не особо сведущ в ООП, это, наверно, звучит сложно. Поэтому рассмотрим применение полиморфизма на примере.
Самые простые варианты, которые приходят в голову — написать три отдельных класса и работать с ними. Или написать один класс, в которым будут все свойства, присущие всем трем типам публикаций, а задействоваться будут только нужные. Но ведь для разных типов аналогичные по логике методы должны работать по-разному. Делать несколько однотипных методов для разных типов (get_news, get_announcements, get_articles) — это уже совсем неграмотно. Тут нам и поможет полиморфизм.
abstract class
Publication
{
// таблица, в которой хранятся данные по элементу
protected
$table
;
// свойства элемента нам неизвестны
protected
$properties
= array();
// конструктор
{
// обратите внимание, мы не знаем, из какой таблицы нам нужно получить данные
$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
"
"
;
echo
$this
->
properties
[
"text"
];
echo
"
Источник: "
.
$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
"
Внимание! Объявление действительно до "
.
$this
->
properties
[
"end_date"
];
echo
"
"
.
$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
"
"
;
echo
$this
->
properties
[
"text"
];
echo
"
"
.
$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 {
// исключение или обработка ошибки
}
}
Вот и все. Легким движением руки брюки превращаются в элегантные шорты:-).
Основная выгода полиморфизма — легкость, с которой можно создавать новые классы, «ведущие себя» аналогично родственным, что, в свою очередь, позволяет достигнуть расширяемости и модифицируемости. В статье показан всего лишь примитивный пример, но даже в нем видно, насколько использование абстракций может облегчить разработку. Мы можем работать с новостями точно так, как с объявлениями или статьями, при этом нам даже не обязательно знать, с чем именно мы работаем! В реальных, намного более сложных приложениях, эта выгода еще ощутимей.
s_a_p 20 августа 2008 в 19:09
Полиморфизм — одна из трех основных парадигм ООП. Если говорить кратко, полиморфизм — это способность обьекта использовать методы производного класса, который не существует на момент создания базового. Для тех, кто не особо сведущ в ООП, это, наверно, звучит сложно. Поэтому рассмотрим применение полиморфизма на примере.
Самые простые варианты, которые приходят в голову — написать три отдельных класса и работать с ними. Или написать один класс, в которым будут все свойства, присущие всем трем типам публикаций, а задействоваться будут только нужные. Но ведь для разных типов аналогичные по логике методы должны работать по-разному. Делать несколько однотипных методов для разных типов (get_news, get_announcements, get_articles) — это уже совсем неграмотно. Тут нам и поможет полиморфизм.
abstract class
Publication
{
// таблица, в которой хранятся данные по элементу
protected
$table
;
// свойства элемента нам неизвестны
protected
$properties
= array();
// конструктор
{
// обратите внимание, мы не знаем, из какой таблицы нам нужно получить данные
$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
"
"
;
echo
$this
->
properties
[
"text"
];
echo
"
Источник: "
.
$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
"
Внимание! Объявление действительно до "
.
$this
->
properties
[
"end_date"
];
echo
"
"
.
$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
"
"
;
echo
$this
->
properties
[
"text"
];
echo
"
"
.
$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 {
// исключение или обработка ошибки
}
}
Вот и все. Легким движением руки брюки превращаются в элегантные шорты:-).
Основная выгода полиморфизма — легкость, с которой можно создавать новые классы, «ведущие себя» аналогично родственным, что, в свою очередь, позволяет достигнуть расширяемости и модифицируемости. В статье показан всего лишь примитивный пример, но даже в нем видно, насколько использование абстракций может облегчить разработку. Мы можем работать с новостями точно так, как с объявлениями или статьями, при этом нам даже не обязательно знать, с чем именно мы работаем! В реальных, намного более сложных приложениях, эта выгода еще ощутимей.
Полиморфизм (программирование)
Кратко смысл полиморфизма можно выразить фразой: «Один интерфейс , множество реализаций».
Полиморфизм - один из четырёх важнейших механизмов объектно-ориентированного программирования (наряду с абстракцией , инкапсуляцией и наследованием).
Полиморфизм позволяет писать более абстрактные программы и повысить коэффициент повторного использования кода . Общие свойства объектов объединяются в систему, которую могут называть по-разному - интерфейс , класс . Общность имеет внешнее и внутреннее выражение:
Класс геометрических фигур (эллипс , многоугольник) может иметь методы для геометрических трансформаций (смещение , поворот, масштабирование).
В Haskell существует два вида полиморфизма - параметрический (чистый) и специальный, (на основе классов ). Специальный называют еще ad hoc (от лат. ad hoc - специально). Их можно отличить следующим образом:
В Haskell есть деление на классы и экземпляры (instance), которого нет в ООП . Класс определяет набор и сигнатуры методов (возможно, задавая для некоторых или всех из них реализации по умолчанию), а экземпляры реализуют их. Таким образом, автоматически отпадает проблема множественного наследования. Классы не наследуют и не переопределяют методы других классов - каждый метод принадлежит только одному классу. Такой подход проще, чем сложная схема взаимоотношений классов в ООП. Некоторый тип данных может принадлежать нескольким классам; класс может требовать, чтобы каждый его тип обязательно принадлежал к другому классу, или даже нескольким; такое же требование может выдвигать экземпляр. Это аналоги множественного наследования. Есть и некоторые свойства, не имеющие аналогов в ООП. Например, реализация списка, как экземпляра класса сравнимых величин, требует, чтобы элементы списка также принадлежали к классу сравнимых величин.
Программистам, переходящим от ООП к ФП , следует знать важное отличие их системы классов. Если в ООП класс «привязан» к объекту, т. е. к данным, то в ФП - к функции. В ФП сведения о принадлежности к классу передаются при вызове функции, а не хранятся в полях объекта. Такой подход, в частности, позволяет решить проблему метода нескольких объектов (в ООП метод вызывается у одного объекта). Пример: метод сложения (чисел, строк) требует двух аргументов, причем одного типа.
В некоторых языках программирования (например, в Python и Ruby) применяется так называемая утиная типизация (другие названия: латентная, неявная), которая представляет собой разновидность сигнатурного полиморфизма. Таким образом, например, в языке Python полиморфизм не обязательно связан с наследованием.
(упоминается в классической книге Саттера и Александреску, которая является источником).
Полиморфизм может пониматься как наличие точек кастомизации в коде, когда один и тот же написанный программистом фрагмент кода может означать разные операции в зависимости от чего-либо.
В одном случае конкретный смысл фрагмента зависит от того, в каком окружении код был построен. Это т.н. статический полиморфизм. Перегрузка функций, шаблоны в Си++ реализуют именно статический полиморфизм. Если в коде шаблонного класса вызвана, например, std::sort, то реальный смысл вызова зависит от того, для каких именно типовых параметров будет развернут данный шаблон - вызовется одна из std::sort
В другом случае конкретный смысл фрагмента определяется только на этапе исполнения и зависит от того, как именно и где именно был построен данный объект. Это обычный, динамический полиморфизм, реализуется через виртуальные методы.
Этот полиморфизм называют чистым полиморфизмом. Применяя такую форму полиморфизма, родственные объекты можно использовать обобщенно. С помощью замещения и полиморфизма включения можно написать один метод для работы со всеми типами объектов TPerson. Используя полиморфизм включения и замещения можно работать с любым объектом, который проходит тест «is-A». Полиморфизм включения упрощает работу по добавлению к программе новых подтипов, так как не нужно добавлять конкретный метод для каждого нового типа, можно использовать уже существующий, только изменив в нем поведение системы. С помощью полиморфизма можно повторно использовать базовый класс; использовать любого потомка или методы, которые использует базовый класс.
Используя Параметрический полиморфизм можно создавать универсальные базовые типы. В случае параметрического полиморфизма, функция реализуется для всех типов одинаково и таким образом функция реализована для произвольного типа. В Параметрическом полиморфизме рассматриваются параметрические методы и типы.
Параметрические методы
Если полиморфизм включения влияет на наше восприятие объекта, то параметрические полиморфизм влияет на используемые методы, так как можно создавать методы родственных классов, откладывая объявление типов до времени выполнения. Для избежания написания отдельного метода каждого типа применяется параметрический полиморфизм, при этом тип параметров будет являться таким же параметром, как и операнды.
Параметрические типы
Вместо того, чтобы писать класс для каждого конкретного типа следует создать типы, которые будут реализованы во время выполнения программы то есть мы создаем параметрический тип.
Абстрактные методы часто относятся к отложенным методам. Класс, в котором определен этот метод может вызвать метод и полиморфизм обеспечивает вызов подходящей версии отложенного метода в дочерних классах. Специальный полиморфизм допускает специальную реализацию для данных каждого типа.
Это частный случай полиморфизма. С помощью перегрузки одно и то же имя может обозначать различные методы, причем методы могут различаться количеством и типом параметров, то есть не зависят от своих аргументов. Метод может не ограничиваться специфическими типами параметров многих различных типов.
Ок. Полиморфизм ни в коем случае нельзя рассматривать отдельно от других фундаментальных понятий - абстракция, инкапсуляция и наследование. Объект и подобные прилагаются из аксиом (хотя это-то тоже аксиомы).
Собственно, представим себе рядом стакан, кружку, чайник, кофемашину, велосипед и скейт. Что между ними всеми общего? Ну как минимум то, что они есть. То есть это - объекты, которые были созданы. Но как они были созданы? Скорее всего на заводе производителя по чертежам. Ок, чертежём назовём конструктор. Ну а класс? А что это такое? А его нет в нашей вселенной - эта сущность есть абстракция, что живёт лишь в наших мыслях. В реальном мире её нет и никогда не будет, такова уж физика - ей по барабану, что птицы и млекопитающие имеют дальних родственников - она лишь обеспечивает возможность естесственного отбора. А уж родственников друг другу находим мы, люди.
С объектами и классами разобрались, а что же там с нашими стаканами и велосипедами. Мы уже поняли, что всё это объект, то есть грубо можно все объекты наследовать от какого-нибудь суперпредка, суперкласса, что и реализовано в некоторых языках. Но что другого общего между скейтом и стаканом, например? Конечно, можно углубляться и считать, что они все из молекул, и они все из твёрдых веществ. Однако это всё бред и СПГС , так что ответ прост - да ничего. То есть это совершенно разные объекты с совершенно разным функционалом. Более того - естесственно компьютерные модели и иерархии будут сильно отличатся от физик и химий. И это нормально, вопрос об адекватностях моделей ставиться лишь когда модель неадекватна, а до тех пор пилить можно что угодно, лишь бы работало.
Вот. У нас есть супер-предок Object, от которого дефолтно наследуются все объекты. Допустим, то что объекты состоят из атомов и есть то, что наследуют все объекты. Но все дополнения и правки - полиморфизм. Так, из атомов мы слепили колёса и приделали на доску - ок, это скейт. На него можно встать и катиться, а сильно извернувшись и полетать в трёх метрах над землёй, прямо таки излучая своё яркое эго. В то время как стакан - это мы слепили из атомов плотную ёмкость, из которой вода не выливается под действием силы тяжести. И прямое применение стакана - налив воды опрокинуть его над ртом, чтобы вода вытекла прямо в желудок. Так делают настоящие пацаны, не заботясь об икоте или страхе утонуть, так что вот - полиморфизм.
Однако что с остальным? У нас ещё абстракция, инкапсуляция и наследование. Ок, начнём с наследования, так оно наиболее близко. Вот что у нас общего между стаканом и кружкой? Ну в оба можно налить воду, но у кружки есть ручка чтобы держаться. То есть можно придумать некий общий класс - ёмкость. Однако что это за класс? Можно например за этот класс взять стакан, тогда все ёмкости по дефолту стаканы, а всё остальное - видоизменённые стаканы. Но кому-то больше нравяться кувшины, например некоторые чики насят их на голове, считая что это удобно. Ну и пусть носят, но как-то же решить надо, что главнее и идеальнее. Так вот - недостяжимый идеал и есть главный - это называется абстрактный класс. То есть ёмкость, что невозможно создать, для которого нет полного чертежа. А все чертежи, что дополнили до полного - есть наследованные классы от класса ёмкость.
Тут мы подошли к абстракции. Вот такое иерархическое наследование приводит нас к, возможно главной, идее ООП. Вот мы взяли и выделили всё, куда можно налить воду в отдельный класс, нарисовали общий чертёж, но специально не доделали его, оставив зазор для будущих творцов, и назвали чертёж - ёмкость. Тысячи лет изобретатили всех миров создают свои ёмкости, одна лучше другой. Для разных людей - по разному, конечно. Но каждый раз группировать молекулы стекла определённым образом - непростая задача. Поэтому ремесленники пошли на хитрость, они создали тайный совет ремесленников мира и решили делиться друг с другом своими наработками. То есть создавать мелкие чертежи и объявлять классом, например, извлистой ручки в форме ленты Мёбиуса, например. Возможно такая ручка удобно только инопланетным существам, но чертёж создан и к нему можно ссылаться при создании своего чертежа. Таким образом мы абстрагируемся от низкоуровневой задачи "формирования ёмкостей посредством перемещения молекул" к "конструированию ёмкости посредством совмещения деталей, элементов". Это и есть абстракция.
Но мы подошли к последнему пункту - инкапсуляция. Она неразрывна с абстракцией, и по сути благодаря ей она и работает. Инкапсуляция - это своеборазный клей (или синяя изолента), которым склеивают разные чертежи в один. То есть совмещение деталей для создания своей - это и есть инкапсуляция. Причём при совмещении мы можем не описывать детали этого совмещения (то есть члены класса могут быть приватными), таким образом помогая абстрагироваться тем, кто этот чертёж использует. Вот посмотрим на чайник - что это такое? Это стакан (или кружка) к которому снизу (а может внутри по середине?) приклеен нагревательный элемент. Пустив по нему ток, согласно инкапсулированному в нагревательный элемент закону Ома, будет выделяться тепло и нагреваться вода. А кофемашина? Это куда более сложное устройство, с множеством насосов, ёмкостей, шлюзов, измельчителей и чайников. И всё склееное клеем. А может синей изолентой. Это снова инкапсуляция.
Таким образом, абстракция невозможна без инкапсуляции и наследовании, как невозможен полиморфизм без, собственно, наследования. Ну а полиморфизм невозможен ещё и без инкапсуляции, которая банально бесполезна без наследования и полиморфизма. Вот такие тут треугольники с пирогами. Жаль только про пирог наврали. И про день рожденье.