Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ООП.doc
Скачиваний:
1
Добавлен:
14.04.2019
Размер:
181.25 Кб
Скачать

Полиморфизм

Может ли переменная изменять свой тип динамически, то есть во время выполнения программы? Нам известны две категорические точки зрения:

  1. Строгая типизация: переменная может иметь один и только один тип. Все преобразования типов должны быть явными и относиться только к значениям, а не к именам. Самый большой компромисс в строгой типизации – понятие подтипа как подмножества, а не подкласса;

  2. Бестиповая логика: каждое значение может пониматься как принадлежащее произвольному типу.

Что такое подкласс? Один из вариантов ответа – это наследник в смысле ООП.

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

Полиморфизм радикально изменяет взгляд на понятие типа. Если раньше переменная принадлежала типу (одному, фиксированному), то теперь она изменяет его, то есть тип становится атрибутом переменной. Полиморфное программирование – сложное программирование ещё и потому, что не во всём адекватно поддерживается реализация.

Пусть Dad – объект класса cDad, Son – выражение класса cSon, причём cDad – предок cSon в отношении наследования. Присваивание Son:=Dad недопустимо (синтаксическая ошибка). Правда допустимо явное присваивание типов переменной любого класса любому другому классу.

Son:=Dad as cSon;

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

В любом случае допустимо присваивание Dad:=Son и, соответственно, употребление выражений типа cSon вместо значений типа cDad, например, при обращении к процедуре.

Внимание! Как всегда происходит присваивание ссылок. Это означает, что ссылка на предыдущие значения, связанная со ссылкой Dad, будет потеряна. Переменная Dad изменяет свой тип с формально описанного статического типа cDad на фактический тип cSon.

Что это означает для значений? Преображает ли Dad новые поля?

Фактически – да , формально – нет. Для таких полей обращение Dad.p неверно, а верно (Dad as cSon).p. Точно так же дело обстоит с методами, которые есть у сына, но нет у отца. Но наиболее интересно другое: изменяются ли значения, то есть реализация одноимённых функций?

  1. Да, если таковые при объявлении класса cDad объявлены как виртуальные (virtual) или динамические (dynamic) (одна семантика, разная реализация), а при определении класса cSon – как переписывающие метод предка (override)

  2. Нет, если методы предка объявлены как статические (static), что является опцией по умолчанию.

Пример. unit Animals;

interface

type

Animal=class

name:string;

procedure Move;

procedure Cry;

procedure Bite(ThatAnimal: Animal);

procedure GetFood(ThatAnimal: Animal);

constructor Create(ThatName: string);

end;

Dinosaur=class(Animal)

procedure Growl;

procedure Move; override;

procedure GetFood; override;

end;

Flea=class(Animal);

procedure Move; override;

procedure Cry; override;

end;

implementation

uses Odialogs;

constructor Animal.Create(ThatName: string);{процедура создания

животного}

begin

inherited Create;

Self.Name:=ThatName;

end; {Употребление self необязательно, но всегда подразумевается. Self

является неявным параметром любого метода любого объекта, и в

реальности есть ссылка на этот объект}

procedure Animal.Bite (ThatAnimal: Animal);

begin

ShowMessage(name+’bites’+ThatAnimal.name);

end;

procedure Animal.Cry;

begin

ShowMessage(name+’cries’);

end;

procedure Animal.Move;

begin

ShowMessage(Animal+name+’moves’);

end;

procedure Animal.GetFood (ThatAnimal: Animal);

begin

ShowMessage(name+’gets food’);

move; selfCry;

bite(ThatAnimal);

ThatAnimal.Cry;

end;

end;

procedure Dinosaur.Cry;

begin

ShowMessage(‘Dinosaur’+name+’cries RRRR’);

end;

procedure Dinosaur.Move;

begin

ShowMessage(‘Dinosaur’+name+’moves’);

end;

procedure Dinosaur.Growl;

begin

Self.Cry;

end;

procedure Flea.Move;

begin

ShowMessage(‘Flea’+name+’moves’);

end;

procedure Flea.Cry;

begin

ShowMessage(‘Flea’+name+’cries RRRR’);

end;

uses Animals;

var x: Animal;

y: Dinosaur;

z: Flea;

begin

z:=Flea.Create(‘Buggy’);

y:=Dinosaur.Create(‘Dino’);

y.Cry;

{x.Animal;}

x:=Animal.Create(‘Pet’);

{x.Growl}

(x as Dinosaur).Growl; {Синтаксически допустимо, но, поскольку, метода

Growl у переменной нет, вызов её приведёт к

ошибке.}

x.Free; {Удаляем ненужный объект}

(x as Dinosaur).Growl;

x.Move;

{Для того, чтобы появилось сообщение “Dinosaur Dino moves”, поставь virtual, dinamic при определении метода move в классе Animal, а в классе Dinosaur напиши override}

z.GetFood; {‘Buggy gets food’}

{Для того, чтобы блоха двигалась, нужно дописать в объявлении этого класса override при объявлении метода move}

{Фактически, из метода предка в этом случае вызывается метод потомка}

****************

end;