- •Программирование и алгоритмические языки. Курс за третий семестр. Введение в объектно-ориентированное программирование (ооп)
- •Именование типов в Object Pascal Тип данных «класс»
- •Инкапсуляция
- •Представление данных Данные как функции доступа Свойства
- •Иерархия типов Наследование реализации Проблемы наследования
- •Полиморфизм
- •Проблема множественности иерархий Агрегирование
- •Абстрактные методы и именованные интерфейсы
- •Описание сложного поведения
- •Эволюция пользовательского интерфейса
- •Автоматные модели описания поведения Сложное поведение как изменчивое поведение
- •Программы как символьные преобразования Алфавит как тип данных
- •Программы как синхронные преобразования
- •Тривиальное поведение
- •Нетривиальное поведение
- •Интеллект как рефлексивное, самоопределяемое поведение Машины Тьюринга
- •События как предикаты
- •Событийно управляемое исполнение программ в ооп Программы как тип данных
- •События конечного пользователя и системные, внешние и внутренние
- •Обработка исключений Делегирование
- •Живые данные
- •Задача (проблема) многих тел
- •События как исключения Исключения как события Обработка исключений в Delphi
Полиморфизм
Может ли переменная изменять свой тип динамически, то есть во время выполнения программы? Нам известны две категорические точки зрения:
Строгая типизация: переменная может иметь один и только один тип. Все преобразования типов должны быть явными и относиться только к значениям, а не к именам. Самый большой компромисс в строгой типизации – понятие подтипа как подмножества, а не подкласса;
Бестиповая логика: каждое значение может пониматься как принадлежащее произвольному типу.
Что такое подкласс? Один из вариантов ответа – это наследник в смысле ООП.
Под полиморфизмом понимают возможность уточнения (изменения) не только значений, состояний объекта, но и поведения объекта, то есть связанных с ним функций изменения состояния и/или алгоритмов их реализации, то есть изменение (уточнение) фактического типа переменной в ходе выполнения программы. Описание типа переменной теперь означает лишь присвоение начального типа по умолчанию.
Полиморфизм радикально изменяет взгляд на понятие типа. Если раньше переменная принадлежала типу (одному, фиксированному), то теперь она изменяет его, то есть тип становится атрибутом переменной. Полиморфное программирование – сложное программирование ещё и потому, что не во всём адекватно поддерживается реализация.
Пусть 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. Точно так же дело обстоит с методами, которые есть у сына, но нет у отца. Но наиболее интересно другое: изменяются ли значения, то есть реализация одноимённых функций?
Да, если таковые при объявлении класса cDad объявлены как виртуальные (virtual) или динамические (dynamic) (одна семантика, разная реализация), а при определении класса cSon – как переписывающие метод предка (override)
Нет, если методы предка объявлены как статические (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;