Опубликован: 04.04.2012 | Уровень: для всех | Доступ: платный
Лекция 5:

Универсальность плюс наследование

< Лекция 4 || Лекция 5: 1234 || Лекция 6 >

Дальнейшее чтение

  1. Бертран Мейер: "Объектно-ориентированное конструирование программных систем" Изд. Русская Редакция, Интернет-Университет, Москва, 2005 г. Содержит детальный анализ наследования на протяжении нескольких глав.
  2. Бертран Мейер, Карин Арнаут: "Componentization: the Visitor Example", Computer (IEEEE), vol. 39, no. 7, July, 2006, pages 23-30 также доступна в интернете: se.ethz.ch/'meyer/publications/computer/visitor.pdf

Образец "Посетитель" дополняет динамическое связывание, позволяя просто добавлять операцию в множество существующих типов (в противоположность обращенной задаче). Эта статья представляет образец, предлагая реализующий его повторно используемый компонент, который является частью библиотеки образцов ETH. В литературе можно найти много описаний образца "Посетитель" (включая Википедию), большинство которых используют в качестве источника описание из книги E. GAMMA и др., Design Patterns, Addison-Wesley, 1994.

Ключевые концепции этой лекции

  • Наследование означает, что некоторый класс (наследник) получает компоненты и инвариант от другого класса (родителя). Экземпляры наследника могут быть обработаны таким же способом, что и экземпляры родителя.
  • Согласование распространяет на типы отношение "быть потомком" между классами.
  • Совместно с универсальностью наследование помогает определить изощренную систему типов, которая превращает компилятор в инструментарий, строящий доказательства и проверяющий свойства согласования элементов программной системы.
  • Полиморфизм, применяемый только к ссылкам, позволяет выражению, имеющему "статический" тип по объявлению, обозначать объекты разных типов (их динамические типы) во время выполнения программы. Система типов гарантирует, что все динамические типы являются потомками статического типа.
  • Форма полиморфизма, основанная на универсальности, позволяет определять контейнерные структуры, которые во время выполнения могут наполняться объектами разных типов.
  • Динамическое связывание гарантирует, что в присутствии полиморфизма любой вызов компонента всегда использует версию, наилучшим образом адаптированную к динамическому типу цели.
  • Полиморфизм и динамическое связывание используют преимущества скрытия информации, позволяя клиентам игнорировать точный тип объектов, которые они обрабатывают, и не знать, какая именно версия операции была применена. У них нет необходимости в знании этой информации.
  • Правила типизации ограничивают полиморфизм: тип любого объекта, к которому может быть присоединена во время выполнения переменная (динамический тип переменной), должен быть согласован с объявленным (статическим) типом. Это требует, чтобы при любом присваивании и передаче аргументов методу тип источника был согласован с типом цели.
  • Отложенные компоненты имеют спецификацию, включающую сигнатуру и возможные контракты, но не имеющие реализации. Класс отложен, если он содержит, по меньшей мере, один отложенный компонент, хотя другие компоненты могут быть эффективными (не быть отложенными). Отложенные компоненты позволяют задавать абстракции высокого уровня и, в частности, полезны при проектировании подходящей таксономии. Нельзя создавать экземпляры отложенных классов.
  • Класс может переопределить наследуемый компонент, чтобы обеспечить другую его реализацию, отличающуюся от реализации родителя. Это позволяет комбинировать повторное использование и адаптацию.
  • Универсальность и наследование являются дополняющими механизмами для расширения типа. Универсальность обеспечивает параметризацию типов, наследование обеспечивает обобщение и специализацию.
  • Ограниченная универсальность делает возможным применять специальные операции к переменным формального универсального типа, требуя, чтобы все соответствующие родовые параметры были согласованы с данным типом, заданным ограничением универсальности.
  • Множественное наследование дает классу дополнительные преимущества в результате комбинирования нескольких абстракций. Это простая и эффективная техника. Во избежание неоднозначности любой конфликт имен компонентов должен быть устранен в момент наследования.
  • Повторное наследование возникает в результате множественного наследования, когда класс является потомком другого класса более чем по одному пути наследования. Повторно наследуемые компоненты сливаются, если они наследуются под одним и тем же именем, и сохраняются раздельно в противном случае. Любая потенциальная неоднозначность при динамическом связывании разрешается благодаря введению предложения select.
  • Архитектура программной системы, обеспечивающая гладкую эволюцию, должна строиться так, чтобы минимизировать объем знаний, необходимый каждой части системы для взаимодействия с другими частями.
  • Динамическое связывание обеспечивает прекрасное решение эволюции ПО в случае подключения новых типов, имеющих собственные варианты "старых" операций.
  • В случае новых операций у "старых" типов широко используемое решение задает образец "Посетитель". Он основан на объектах-"посетителях", действующих в качестве посредников между клиентскими классами и целевыми классами. Возможно построение более общего, полностью повторно используемого решения. Оно основано на механизме агентов.

Новый словарь

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

Ancestor Предок Assignment attempt Попытка присваивания
Cast Кастинг приведение к типу Client (of a visit) Клиент (визита)
Conformance Согласование Constrai-ned genericity Ограниченная универсальность
Deferred feature, class, type Отложенный метод, класс, тип Descendant Потомок
Double dispatch Двойная диспетчеризация Downcasting Приведение вниз
Dynamic binding Динамическое связывание Dynamic cast Динамический кастинг
Dynamic dispatch Динамическая диспетчеризация Effect, effective, effecting Эффект, эффективный, эффективизация
Flat view Плоский облик Generic constraint Ограничение универсальности
Immediate feature Непосредственный компонент Inheritance Наследование
Inherited feature Наследуемый компонент Interface (Java, C#) Интерфейс (Java, C#)
Introduce (a feature) Введение (компонента) "Is-a" relation Отношение"является"
Multiple inheritance Множественное наследование Name clash Конфликт имен
Object test Тест объекта Object-Test local Переменная теста объекта
Overriding Переопределение Parametric polymorphism Параметрический полиморфизм
Polymorphic expression Полиморфное выражение Polymorphism Полиморфизм
Polymorphic data structure Полиморфная структура данных Precursor Precursor (версия родителя)
Programs with Holes pattern Программы с дырами Proper ancestor, descendant Подходящий (правильный) предок, потомок
Redeclaration Переобъявление Redefinition Переопределение
Refinement Уточнение Repeated inheritance Повторное наследование
Replication (of a feature) Репликация компонента Routine table Таблица методов
RTTI (Run-Time Type Identification) RTTI (идентификация типа во время выполнения) Single dispatch Одиночная диспетчеризация
Subclass Подкласс(субподрядчик) Subcontracting Субконтракт
Superclass Суперкласс (подрядчик) Taxonomy Таксономия
Target (of a visit) Цель (визита) Type narrowing Сужение типа
Unconstrained genericity Неограниченная универсальность Virtual table Виртуальная таблица
Visitor Посетитель (образец)

Упражнения

Словарь

Дайте точные определения терминам словаря.

Карта концепций

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

Абстрактный синтаксис

Это подготовительное упражнение для следующих двух (написать интерпретатор и компилятор). Цель — построить программы, использующие абстрактный синтаксис, избегая построения парсера. Задача не сложная, но использует приемы, которые не были рассмотрены.

Рассмотрим язык программирования L0 со следующими конструкциями, выраженными здесь неформально с конкретным синтаксисом.

  • Единственный тип данных — integer (целые).
  • Переменная (целочисленная) имеет имя, заданное произвольной непустой строкой.
  • Константами являются отрицательные и положительные целые, в том числе ноль.
  • Выражение — это переменная, константа или выражение со знаками операций.
  • Знак операции (все операции бинарные) — один из трех: +, -, *
  • Выражение со знаком состоит из двух выражений, соединенных знаком операции, с ожидаемой семантикой (сложение, вычитание, умножение).
  • Оператором является один из следующих: skip (пустой), read (чтение), compound (последовательность операторов), assignment (присваивание), conditional (условный), loop (цикл).
  • Оператор skip не имеет эффекта.
  • Оператор read x, где x — имя переменной, читает целое значение от интерактивного пользователя и присваивает его переменной x.
  • Присваивание записывается в форме x:= e, где x — переменная, e — выражение.
  • Целое можно использовать в тестах условного оператора и цикла с соглашением, что 0 означает ложь, а любое другое значение — истина.
  • Уловный оператор записывается в форме: if e then il else i2 end, где e — выражение, рассматриваемое как булевское значение, il, i2 — операторы.
  • Цикл записывается в форме: from il until e loop i2 end
  • Составной оператор является последовательностью операторов. В конкретном синтаксисе последовательность берется в скобки do... end
  • Программа является составным оператором.
  1. Используя конкретный синтаксис, напишите программу на L0, которая получает от пользователя два целых и вычисляет наибольший общий делитель — НОД, применяя алгоритм Эвклида и не используя умножение (вы можете написать другие L0-програм-мы как примеры для следующих вопросов).
  2. Спроектируйте множество классов — такFLASH_VISITOR378442их как PROGRAM, COMPOUND и так далее, позволяющих создавать представление абстрактного синтаксиса L0-программ. Используйте наследование подходящим образом. Убедитесь, что классы содержат подходящие процедуры создания, так, чтобы L0-программы были представлены как структуры объектов без конкретного синтаксиса.
  3. Напишите программу, которая создает абстрактное синтаксическое дерево (АСТ) для программы вычисления НОД (и любого другого примера, который вы подготовили, выполняя пункт 1).

Разбор АСТ

(Это продолжение предыдущего упражнения. Его задача — создать запись текста программы в конкретном синтаксисе по АСТ. Это задача, обратная к парсингу)

Напишите программу, которая печатает конкретное представление с конкретным синтаксисом, описанным в предыдущем упражнении. L0-программа дается как экземпляр класса PROGRAM и ассоциированных объектов (экземпляров COMPOUND и так далее). Каждый оператор должен начинаться на новой строке. Для составного оператора предусмотрите вложенность. Ветви условного оператора и тело цикла даются с отступами.

Проверьте результаты вычислений и убедитесь, что программа работает корректно.

Интерпретатор, работающий на абстрактном синтаксисе

(Это продолжение последних четырех упражнений)

Напишите интерпретатор — программу на Eiffel, которая может выполнять любую L0-программу, представленную в виде экземпляра класса PROGRAM и ассоциированных объектов (экземпляров COMPOUND и так далее). Семантика L0-такова:

  • выполнить задающий программу составной оператор;
  • распечатать по одной паре на каждой строке, где число пар определяется числом переменных программы, а печать пары означает печать имени переменной и ее значения, разделенных знаком равенства.

Испытайте ваш интерпретатор на построенных примерах программ.

Компилятор, работающий на абстрактном синтаксисе

(Это продолжение предыдущих упражнений)

Напишите компилятор — программу на Eiffel, которая транслирует любую L0-программу в Eiffel-систему в форме корневого класса и множества вспомогательных классов при необходимости. Проверьте, что результаты работы компилятора и интерпретатора совпадают.

Как много такси

Образцом для этого упражнения может служить функция pre_taxi_count. Рассмотрите fleet: LIST[VEHICLE].

  1. Напишите функцию, которая вычисляет число экземпляров такси в списке транспортных средств.
  2. Напишите функцию, которая вычисляет число прямых экземпляров такси в списке транспортных средств.

Универсальный "Посетитель"

Покажите, как улучшить образец "Посетитель", представляя целевой класс как родовой параметр класса VISITOR. Убедитесь, шаг за шагом, что полученное решение находится на том же уровне детализации, что и обсуждение в этой лекции. Нужно ли вам все еще множество вариантов VISITOR? Является ли решение полностью повторно используемым? Обсудите достоинства и недостатки решения в сравнении с базисным вариантом VISITOR.

< Лекция 4 || Лекция 5: 1234 || Лекция 6 >
Надежда Александрова
Надежда Александрова

Уточните пожалуйста, какие документы для этого необходимо предоставить с моей стороны. Курс "Объектно-ориентированное программирование и программная инженения". 

Юрий Симонов
Юрий Симонов
Россия, Москва, Московский Государственный Университет им. М.В. Ломоносова, 2011
Юрий Бедарев
Юрий Бедарев
Россия, Новосибирская область