- •Программирование и алгоритмические языки. Курс за третий семестр. Введение в объектно-ориентированное программирование (ооп)
- •Именование типов в Object Pascal Тип данных «класс»
- •Инкапсуляция
- •Представление данных Данные как функции доступа Свойства
- •Иерархия типов Наследование реализации Проблемы наследования
- •Полиморфизм
- •Проблема множественности иерархий Агрегирование
- •Абстрактные методы и именованные интерфейсы
- •Описание сложного поведения
- •Эволюция пользовательского интерфейса
- •Автоматные модели описания поведения Сложное поведение как изменчивое поведение
- •Программы как символьные преобразования Алфавит как тип данных
- •Программы как синхронные преобразования
- •Тривиальное поведение
- •Нетривиальное поведение
- •Интеллект как рефлексивное, самоопределяемое поведение Машины Тьюринга
- •События как предикаты
- •Событийно управляемое исполнение программ в ооп Программы как тип данных
- •События конечного пользователя и системные, внешние и внутренние
- •Обработка исключений Делегирование
- •Живые данные
- •Задача (проблема) многих тел
- •События как исключения Исключения как события Обработка исключений в Delphi
Проблема множественности иерархий Агрегирование
Мышление в терминах иерархий, уточнение строения и поведения, естественно для человеческого мышления. Но в несистематизированных областях построение иерархии – очень трудное дело, даже в хорошо систематизированных областях. Проектирование классов занимает до 80% всего времени разработки. Даже в систематизированных областях таких иерархий может быть очень много. Не деле для полной идентификации объектов необходимо, чтобы любые два объекта попадали в различные классы при какой-то классификации (в какой-то иерархии).
В принципе, реализуемо так называемое множественное наследование от нескольких предков, что с точностью до семантики совпадает с отношением агрегирования, то есть возможностью использовать в качестве полей классов ссылки на другие классы.
Children=class
Mum: cMum;
Dad: cDad;
end;
Помимо общей для ООП проблемы обязательности наследования всех полей наследования метода, множественное наследование и агрегирование порождают более точную семантическую проблему отсутствия приоритетов наследования. Эта проблема решается разделением существительных и прилагательных.
Абстрактные методы и именованные интерфейсы
Вспомним пример с животными. Нужно ли было реализовывать в классе Animal метод Move, который по смыслу, в любом случае, должен был быть переписан в классах-потомках? Можно было просто оставить эту реализацию пустой, но это означало бы лишь, что этот метод может, но не должен обязательно, быть реализован в классах-потомках. Отношение необходимости такой реализации оформляется с помощью опции abstract (абстрактный метод).
Нетрудно представить пример абстрактного класса, в котором нет полей, а все методы абстрактны. Такой класс по семантике (не синтаксису и не происхождению, отличному от чистого ООП) сопоставили именованному интерфейсу.
Основная идея – возможность ссылки на интерфейс. На деле несколько интерфейсов.
type
(имя интерфейса)= interface (ссылка на интерфейс-предок)
[опции]
(список методов и свойств (не полей!))
end;
Заголовки процедур в этом случае не имеют никаких привычных опций, все они по умолчанию public. Спецификации static, virtual, dynamic и так далее отсутствуют. Интерфейсы не могут быть инициализированы (то есть не существует переменных такого типа), поэтому интерфейсы не имеют конструкторов и деструкторов. Класс реализует интерфейс, если в нём реализуются все его методы. Один класс может реализовывать несколько интерфейсов. В случае множественных иерархий нужно выделять одну, как основную (существительные), а много других – как вторичные (уточняющие).
Пример. Иерархия животных основная по строению, по передвижению – вторичная.
type Animals=class … end;
Mammals=class(Animals) … end;
Fishes=class(Animals) … end;
Birds=class(Animals) … end;
FlyAble=interface
procedure Fly(P);
end;
RunAble=interface
procedure Run(P);
end;
SwimAble=interface
procedure Swim(P);
end;
Elephant=class(Mammals, RunAble)
***
end;
{Класс Elephant не обязан определять методы класса Mammals, но обязан реализовать интерфейс RunAble (реализация процедуры Run)}
Dolphin=class(Mammal, SwimAble)
***
end;
Duck=class (Birds, SwimAble, RunAble, FlyAble)
***
end;
Могут ли разные интерфейсы обязывать реализовать методы с одинаковыми именами? Возникающая коллизия имён разрешается обычным образом с помощью точечной нотации, явной ссылки на нужный интерфейс.
Что является значением метода?
С точки зрения логики – реализация (определение). С точки зрения реализации – ссылка на определение (в терминах Паскаля – значение процедурного типа). Например, мы можем реализовать метод присваиванием ему имени, уже определённого к этому времени.
NewMethod=OldMethod – такое присваивание называется отображением (mapping).
Наряду с агрегированием в качестве множественного наследования этот способ может трактоваться как определение, собственно, наследования по умолчанию.
Что делает понятие интерфейса независимой и полной схемой объектного программирования?
К сожалению, понятие класса и его интерфейса не очень чётко разведены в Delphi. Ранее мы трактовали функцию as как явное приведение типов: (объект) as (класс). Ту же функцию можно употреблять как (объект as имя интерфейса).Method, как если бы этот объект имел только этот интерфейс.