Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Выч. тех. - Лаба-3 - с изображением формы.docx
Скачиваний:
1
Добавлен:
12.11.2018
Размер:
704.61 Кб
Скачать

Три механизма, обеспечивающие полиморфизм

Под полиморфизмом в ООП понимают способность одного и того же программного текста

x1.M(arg1, arg2, … argN);

выполняться по-разному, в зависимости от того, с каким объектом связана сущность x. Полиморфизм гарантирует, что вызываемый метод M будет принадлежать классу объекта, связанному с сущностью x. В основе полиморфизма, характерного для семейства классов, лежат три механизма.

Одностороннее присваивание объектов внутри семейства классов. Сущность, базовым классом которой является класс предка, можно связать с объектом любого из потомков. Другими словами, для введенной нами последовательности объектов xk присваивание xi = xj допустимо для всех j >=i.

Переопределение потомком метода, наследованного от родителя. Благодаря переопределению в семействе классов существует совокупность полиморфных методов с одинаковым именем и одинаковой сигнатурой, но разной реализацией.

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

В совокупности это и называется полиморфизмом семейства классов. Целевую сущность часто называют полиморфной сущностью, вызываемый метод - полиморфным методом, сам вызов - полиморфным вызовом.

Полиморфизм семейства классов Found и Derived

Вернемся к нашему примеру с классами Found и Derived. В классе Found определены два виртуальных метода. Один из них - виртуальный метод VirtMethod - определен в самом классе, другой - виртуальный метод ToString - наследован от родительского класса object и переопределен в классе Found. Потомок класса Found - класс Derived переопределяет оба метода, соблюдая контракт, заключаемый в этом случае между родителем и потомком. При переопределении виртуального метода сохраняется имя метода и его сигнатура, изменяется лишь реализация:

public override string ToString()

{

string s = "Поля: name = {0}, Basecredit = {1}" +

"credit = {2}, debit = {3}";

returnString.Format(s, name, base.credit,

credit, debit);

}

public override string VirtMethod()

{

return "Derived: " + this.ToString();

}

В классе Found определены два не виртуальных метода NonVirtMethod и Job, наследуемые потомком Derived без всяких переопределений. Вы ошибаетесь, если думаете, что работа этих методов полностью определяется базовым классом Found. Полиморфизм делает их работу куда более интересной. Давайте рассмотрим в деталях работу метода Job:

public string Job()

{

string res = "";

res += "VirtMethod: " +

VirtMethod() + NL;

res += "NonVirtMethod: " +

NonVirtMethod() + NL;

res += "Parse: " +

Parse() + NL;

returnres;

}

При компиляции метода Job будет обнаружено, что вызываемый метод VirtMethod является виртуальным, поэтому для него будет применяться динамическое связывание. Это означает, что вопрос о вызове метода откладывается до момента, когда метод Job будет вызван объектом, связанным с x. Объект может принадлежать как классу Found, так и классам Derived и ChildDerived, и в зависимости от класса объекта и будет вызван метод этого класса.

Для вызываемых методов NonVirtMethod и Parse, не являющихся виртуальными, будет применено статическое связывание, так что метод Job всегда будет вызывать методы, принадлежащие классу Found. Однако и здесь не все просто. Метод NonVirtMethod

publicstringNonVirtMethod()

{

return "Found: " + this.ToString();

}

в процессе своей работы вызывает виртуальный метод ToString. Опять-таки, для метода ToString будет применяться динамическое связывание, и в момент выполнения будет вызываться метод объекта, а не метод ссылки.

Что же касается метода Parse, определенного в каждом классе, то всегда в процессе работы Job будет вызываться только родительский метод разбора из-за стратегии статического связывания.

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

Класс Found, создающий метод Job, говорит примерно следующее: "Я предоставляю этот метод своим потомкам. Потомок, вызвавший этот метод, должен иметь VirtMethod, выполняющий специфическую для потомка часть работы; конечно, потомок может воспользоваться и моей реализацией, но допустима и его собственная реализация. Затем часть работы выполняю я сам, но выдача информации об объекте определяется самим объектом. Заключительную часть работы, связанную с анализом, я потомкам не доверяю и делаю ее сам".