Что такое полиморфизм. Основы объектно-ориентированного программирования
JAVA основывается на концепциях объектно-ориентированного программирования, что позволяет перейти на более высокий уровень абстракции, чтобы разрешить любую проблему реалистичным путем. Объектно-ориентированный подход концептуализирует решение проблемы в плоскости объектов реального мира, которые легче повторно использовать в приложении. Например, Chair (стул), Fan (вентилятор), Dog (Собака), Computer (компьютер) и так далее. В JAVA класс представляет собой макет, шаблон или прототип, который определяет общее поведение объекта данного типа. Экземпляр - это отдельная реализация класса, и все экзепляры класса имеют одинаковые свойства, которые описаны в определении класса. Например, вы можете опрделить класс с именем House (дом) с количеством комнат в качестве атрибута и создать экземпляры класса, такие как дом с двумя комнатами, дом с тремя комнатами и так далее. Преимущества: Ниже перечислены некоторые плюсы объектно-ориентированной разработки программного обеспечения (ПО).
- Снижение затрат на поддержку ПО, в основном за счет того, что она осуществляется модульно.
- Усовершенствованное повторное использование кода благодаря таким качествам, как наследование, и, как результат, более быстрая разработка ПО.
- Повышенные надежность и гибкость кода.
- Легкость понимания вследствие моделирования реального мира.
- Лучшая абстракция на уровне объекта.
- Уменьшение сложности перехода от одной фазы разработки к другой.
- Инкапсуляция
- Наследование
- Полиморфизм
- Абстракция
Инкапсуляция
Инкапсуляция выступает договором для объекта, что он должен скрыть, а что открыть для доступа другими объектами. В JAVA мы используем модификатор доступа private для того, чтобы скрыть метод и ограничить доступ к переменной из внешнего мира. JAVA также располагает различными модификаторами доступа: public , по умолчанию, protected , private , которые используются для ограничения видимости на разных уровнях. Но конечной целью является инкапсуляция тех вещей, которые не должны быть изменены. Лучше всего работает подход, при котором, у класса должна быть только одна причина для изменения, и инкапсулирование воплощает в реальность проектирование этой “одной причины”. Правильным в инкапсуляции считается сокрытие часто изменяющихся вещей во избежание повреждения других классов. Преимущества: Ниже представлены некоторые преимущества инкапсуляции:- Мы можем защитить внутреннее состояние объекта с помощью сокрытия его атрибутов.
- Это улучшает модульное построение кода, так как предотвращает взаимодействие объектов неожиданными способами.
- Повышается практичность кода.
- Это поддерживает договорные отношения конкретного объекта.
- Инкапсуляция облегчает поддержку ПО.
- Изменения в коде могут производиться независимо друг от друга.
Полиморфизм
Полиморфизм в программировании - это способность предоставлять один и тот же интерфейс для различных базовых форм (типов данных). Это означает, что классы, имеющие различную функциональность, совместно используют один и тот же интерфейс и могут быть динамически вызваны передачей параметров по ссылке. Классический пример - это класс Shape (фигура) и все классы, наследуемые от него: square (квадрат), circle (круг), dodecahedron (додекаэдр), irregular polygon (неправильный многоугольник), splat (клякса) и так далее. В этом примере каждый класс будет иметь свой собственный метод Draw() и клиентский код может просто делать: Shape shape = new Shape () ; Shape.area() чтобы получить корректное поведение любой фигуры Красота полиморфизма заключается в том, что код, работая с различными классами, не должен знать, какой класс он использует, так как все они работают по одному принципу. Процесс, применяемый объектно-ориентированными языками программирования для реализации динамического полиморфизма, называется динамическим связыванием. Примечание: Полиморфизм - это способность выбирать более конкретные методы для исполнения в зависимости от объекта. Полиморфизм осуществляется тогда, когда не задействованы абстракные классы. Преимущества:- Создание повторно используемого кода. То есть, как только класс создан, реализован и протестирован, он может свободно использоваться без заботы о том, что конкретно в нем написано.
- Это обеспечивает более универсальный и слабосвязанный код.
- Понижается время компиляции, что ускоряет разработку.
- Динамическое связывание.
- Один и тот же интерфейс может быть использован для создания методов с разными реализациями.
- Вся реализация может быть заменена с помощью использования одинаковых сигнатур метода.
Наследование
Наследование - это включение поведения (т.е. методов) и состояния (т.е. переменных) базового класса в производный класс, таким образом они становятся доступны в этом производном классе. Главное преимущество наследования в том, что оно обеспечивает формальный механизм повторного использования кода и избегает дублирования. Унаследованный класс расширяет функциональность приложения благодаря копированию поведения родительского класса и добавлению новых функций. Это делает код сильно связанным. Если вы захотите изменить суперкласс, вам придется знать все детали подклассов, чтобы не разрушить код. Наследование - это форма повторного использования программного обеспечения, когда из уже существующего класса (суперкласса) создается новый класс (подкласс), который расширяет свою функциональность и при этом использует некоторые свойства суперкласса. Так что, если у вас есть класс-родитель, а потом появляется класс-наследник, то наследник наследует все вещи, которыми обладает родитель. Преимущества:- Усовершенствованное повторное использование кода.
- Устанавливается логическое отношение «is a» (является кем-то, чем-то). Например: Dog is an animal . (Собака является животным).
- Модуляризация кода.
- Исключаются повторения.
- Сильная связанность: подкласс зависит от реализации родительского класса, что делает код сильно связанным.
Что еще почитать: |
---|
Абстракция
Абстракция означает разработку классов исходя из их интерфейсов и функциональности, не принимая во внимание реализацию деталей. Абстрактный класс представляет собой интерфейсы без включения фактической реализации. Он отличает реализацию объекта от его поведения. Абстракция упрощает код, скрывая несущественные детали. Преимущества:- Применяя абстракцию, мы можем отделить то, что может быть сгруппировано по какому-либо типу.
- Часто изменяемые свойства и методы могут быть сгруппированы в отдельный тип, таким образом основной тип не будет подвергаться изменениям. Это усиливает принцип ООП: «Код должен быть открытым для Расширения, но закрытым для Изменений» .
- Абстракция упрощает представление доменных моделей.
- Множественное наследование .
- Слабая связанность . Происходит абстракция операции, такая как разделение на уровни, а конкретной реализацией может быть что угодно: JDBC, JPA, JTA и т.д.
- Программа-интерфейс не реализуется .
- Полиморфизм с динамическим связыванием : раскрывается програмный интерфейс объекта без раскрытия его фактической реализации.
- Абстрактные уровни , разделение функциональностей.
- Интерфейс - это договорные отношения с классами, которые этот интерфейс реализуют, о том, что реализация происходит путём, обозначенным интерфейсом. Это пустая оболочка с объявленными методами.
- Абстрактный класс определяет некоторое общее поведение и просит свои подклассы определить нетипичное или конкретное поведение для своего класса.
- Методы и члены абстрактного класса могут быть обозначены любым модификатором доступа, в свою очередь все методы интерфейса обязаны быть открытыми (public).
- Когда происходит наследование абстрактного класса, класс-наследник должен определить абстрактные методы, в то время как интерфейс может наследовать другой интерфейс и при этом не обязательно определять его методы.
- Класс-наследник может расширять только один абстрактный класс, а интерфейс может расширять или класс может реализовывать множество других интерфейсов.
- Класс-наследник может определять абстрактные методы с тем же или менее ограниченным модификатором доступа, при этом класс, реализующий интерфейс, должен определять методы с тем же уровнем видимости.
- Интерфейс не содержит конструкторы, в том время, как они есть в абстрактном классе.
- Переменные, объявленные в Java-интерфейсе по умолчанию являются final. Абстрактный класс может содержать переменные, которые не являются final.
- Все участники Java-интерфейса по умолчанию являются public . Участники абстрактного класса могут позволить себе быть public , protected и др.
Композиция
Повторное использование кода может быть достигнуто с помощью как наследования, так и композиции. Но при этом задействование композиции обеспечивает более высокий уровень инкапсуляции, чем наследование, так как изменения в back-end классе не обязательно затронут код, который относится к front-end классу. Композиция - это техника проектирования, применяющая в классах отношения типа “has-a” (имеет, включает в себя). Для повторного использования кода могут применяться как наследование в java, так и композиция объекта. Суть композиции заключается в выражении отношения "has a" между объектами. Подумайте о стуле. У стула есть (has a) сидение. У стула есть (has a) спинка. У стула есть (has a) определенное количество ножек. Фраза ”has a” / “есть” предполагает отношения, в которых стул имеет или, как минимум, использует другой объект. Это как раз и есть отношения “has-a”, являющиеся основой композиции. Преимущества:- Контроль видимости
- Реализация может быть заменена во время выполнения (run-time)
- Слабая связанность, так как класс-интерфейс не зависит от реализации.
№ | Композиция (has a / имеет) | Наследование (is a / является) |
---|---|---|
1 | Поддерживает полиморфизм и повторное использование кода. | |
2 | Объект во время выполнения (run-time) уже создан. | Объект создается динамически во время компиляции. |
3 | Реализация может быть заменена во время выполнения (run-time). | Реализация может быть заменена во время компиляции. |
4 | Подкласс не зависит от класса-родителя, что благоприятствует слабому связыванию (особенно под управлением интерфейса). | Подкласс завизист от реализации класса-родителя, поэтому связывание считается сильным. |
5 | Использование: в Доме есть Ванная комната. Неправильно говорить, что Дом - это Ванная комната. | Наследование является однонаправленным: Дом - это Здание. Но здание не является домом. |
s_a_p 20 августа 2008 в 19:09
Полиморфизм для начинающих
- PHP
Полиморфизм — одна из трех основных парадигм ООП. Если говорить кратко, полиморфизм — это способность обьекта использовать методы производного класса, который не существует на момент создания базового. Для тех, кто не особо сведущ в ООП, это, наверно, звучит сложно. Поэтому рассмотрим применение полиморфизма на примере.
Постановка задачи
Предположим, на сайте нужны три вида публикаций — новости, объявления и статьи. В чем-то они похожи — у всех них есть заголовок и текст, у новостей и объявлений есть дата. В чем-то они разные — у статей есть авторы, у новостей — источники, а у объявлений — дата, после которой оно становится не актуальным.Самые простые варианты, которые приходят в голову — написать три отдельных класса и работать с ними. Или написать один класс, в которым будут все свойства, присущие всем трем типам публикаций, а задействоваться будут только нужные. Но ведь для разных типов аналогичные по логике методы должны работать по-разному. Делать несколько однотипных методов для разных типов (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». Полиморфизм включения упрощает работу по добавлению к программе новых подтипов, так как не нужно добавлять конкретный метод для каждого нового типа, можно использовать уже существующий, только изменив в нем поведение системы. С помощью полиморфизма можно повторно использовать базовый класс; использовать любого потомка или методы, которые использует базовый класс.
Параметрический полиморфизм
Используя Параметрический полиморфизм можно создавать универсальные базовые типы. В случае параметрического полиморфизма, функция реализуется для всех типов одинаково и таким образом функция реализована для произвольного типа. В Параметрическом полиморфизме рассматриваются параметрические методы и типы.
Параметрические методы
Если полиморфизм включения влияет на наше восприятие объекта, то параметрические полиморфизм влияет на используемые методы, так как можно создавать методы родственных классов, откладывая объявление типов до времени выполнения. Для избежания написания отдельного метода каждого типа применяется параметрический полиморфизм, при этом тип параметров будет являться таким же параметром, как и операнды.
Параметрические типы
Вместо того, чтобы писать класс для каждого конкретного типа следует создать типы, которые будут реализованы во время выполнения программы то есть мы создаем параметрический тип.
Полиморфизм переопределения
Абстрактные методы часто относятся к отложенным методам. Класс, в котором определен этот метод может вызвать метод и полиморфизм обеспечивает вызов подходящей версии отложенного метода в дочерних классах. Специальный полиморфизм допускает специальную реализацию для данных каждого типа.
Полиморфизм-перегрузка
Это частный случай полиморфизма. С помощью перегрузки одно и то же имя может обозначать различные методы, причем методы могут различаться количеством и типом параметров, то есть не зависят от своих аргументов. Метод может не ограничиваться специфическими типами параметров многих различных типов.
Полиморфи́зм (в языках программирования) - возможность объектов с одинаковой спецификацией иметь различную реализацию.
Язык программирования поддерживает полиморфизм, если классы с одинаковой спецификацией могут иметь различную реализацию - например, реализация класса может быть изменена в процессе наследования.
Кратко смысл полиморфизма можно выразить фразой: «Один интерфейс, множество реализаций».
Полиморфизм - один из четырёх важнейших механизмов объектно-ориентированного программирования (наряду с абстракцией, инкапсуляцией и наследованием).
Полиморфизм позволяет писать более абстрактные программы и повысить коэффициент повторного использования кода. Общие свойства объектов объединяются в систему, которую могут называть по-разному - интерфейс, класс. Общность имеет внешнее и внутреннее выражение:
внешняя общность проявляется как одинаковый набор методов с одинаковыми именами и сигнатурами (именем методов и типами аргументов и их количеством);
внутренняя общность - одинаковая функциональность методов. Её можно описать интуитивно или выразить в виде строгих законов, правил, которым должны подчиняться методы. Возможность приписывать разную функциональность одному методу (функции, операции) называется перегрузкой метода (перегрузкой функций, перегрузкой операций).
Класс геометрических фигур (эллипс, многоугольник) может иметь методы для геометрических трансформаций (смещение, поворот, масштабирование).
Класс потоков имеет методы для последовательной передачи данных. Потоком может быть информация, вводимая пользователем с терминала, обмен данными по компьютерной сети, файл (если требуется последовательная обработка данных, например, при разборе исходных текстов программ).
В объектно-ориентированных языках
В объектно-ориентированных языках класс является абстрактным типом данных.[Прим. 1] Полиморфизм реализуется с помощью наследования классов и виртуальных функций. Класс-потомок наследует сигнатуры методов класса-родителя, а реализация, в результате переопределения метода, этих методов может быть другой, соответствующей специфике класса-потомка. Другие функции могут работать с объектом как с экземпляром класса-родителя, но если при этом объект на самом деле является экземпляром класса-потомка, то во время исполнения будет вызван метод, переопределенный в классе-потомке. Это называется поздним связыванием. [Примером использования может служить обработка массива, содержащего экземпляры как класса-родителя, так и класса-потомка: очевидно, что такой массив может быть объявлен только как массив типа класса-родителя и у объектов массива могут вызываться только методы этого класса, но если в классе-потомке какие-то методы были переопределены, то в режиме исполнения для экземпляров этого класса будут вызваны именно они, а не методы класса-родителя.]
Класс-потомок сам может быть родителем. Это позволяет строить сложные схемы наследования - древовидные или сетевидные.
Абстрактные (или чисто виртуальные) методы не имеют реализации вообще (на самом деле некоторые языки, например C++, допускают реализацию абстрактных методов в родительском классе). Они специально предназначены для наследования. Их реализация должна быть определена в классах-потомках.
Класс может наследовать функциональность от нескольких классов. Это называется множественным наследованием. Множественное наследование создаёт известную проблему (в C++), когда класс наследуется от нескольких классов-посредников, которые в свою очередь наследуются от одного класса (так называемая «Проблема ромба»): если метод общего предка был переопределён в посредниках, неизвестно, какую реализацию метода должен наследовать общий потомок. Решается эта проблема путём отказа от множественного наследования для классов и разрешением множественного наследования для полностью абстрактных классов (то есть интерфейсов) (C#, Delphi, Java), либо через виртуальное наследование (C++).
В функциональных языках
Полиморфизм в функциональных языках будет рассмотрен на примере языка Haskell.
Полиморфизм — одна из трех основных парадигм ООП. Если говорить кратко, полиморфизм — это способность обьекта использовать методы производного класса, который не существует на момент создания базового. Для тех, кто не особо сведущ в ООП, это, наверно, звучит сложно. Поэтому рассмотрим применение полиморфизма на примере.
Постановка задачи
Предположим, на сайте нужны три вида публикаций — новости, объявления и статьи. В чем-то они похожи — у всех них есть заголовок и текст, у новостей и объявлений есть дата. В чем-то они разные — у статей есть авторы, у новостей — источники, а у объявлений — дата, после которой оно становится не актуальным.Самые простые варианты, которые приходят в голову — написать три отдельных класса и работать с ними. Или написать один класс, в которым будут все свойства, присущие всем трем типам публикаций, а задействоваться будут только нужные. Но ведь для разных типов аналогичные по логике методы должны работать по-разному. Делать несколько однотипных методов для разных типов (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 {
// исключение или обработка ошибки
}
}
Вот и все. Легким движением руки брюки превращаются в элегантные шорты:-).
Основная выгода полиморфизма — легкость, с которой можно создавать новые классы, «ведущие себя» аналогично родственным, что, в свою очередь, позволяет достигнуть расширяемости и модифицируемости. В статье показан всего лишь примитивный пример, но даже в нем видно, насколько использование абстракций может облегчить разработку. Мы можем работать с новостями точно так, как с объявлениями или статьями, при этом нам даже не обязательно знать, с чем именно мы работаем! В реальных, намного более сложных приложениях, эта выгода еще ощутимей.
Немного теории
- Методы, которые требуют переопределения, называются абстрактными. Логично, что если класс содержит хотя бы один абстрактный метод, то он тоже является абстрактным.
- Очевидно, что обьект абстрактного класса невозможно создать, иначе он не был бы абстрактным.
- Производный класс имеет свойства и методы, принадлежащие базовому классу, и, кроме того, может иметь собственные методы и свойства.
- Метод, переопределяемый в производном классе, называется виртуальным. В базовом абстрактном классе об этом методе нет никакой информации.
- Суть абстрагирования в том, чтобы определять метод в том месте, где есть наиболее полная информация о том, как он должен работать.