- Любовные романы
- Фантастика и фэнтези
- Ненаучная фантастика
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Юмор
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Работа с клиентами
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Зарубежная литература о культуре и искусстве
- Пословицы, поговорки
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
Основы объектно-ориентированного программирования - Бертран Мейер
Шрифт:
Интервал:
Закладка:
Вот два основных правила, представленных в первой лекции о наследовании (лекция 14).
[x]. Правило Вызова Компонентов: запись x.f осмысленна лишь тогда, когда базовый класс x содержит и экспортирует компонент f.
[x]. Правило Совместимости Типов: при передаче a как аргумента или при присваивании его некой сущности необходимо, чтобы тип a был совместим с ожидаемым, то есть основан на классе, порожденным от класса сущности.
Правило Вызова Компонентов не является причиной каких-либо проблем - это фундаментальное условие всякой работы с объектами. Естественно, что обращаясь к компоненту объекта, нужно проверить, действительно ли данный класс предлагает и экспортирует данный компонент.
Правило Совместимости Типов требует больше внимания. Оно предполагает наличие у нас всей информации о типах объектов, с которыми мы работаем. Как правило, это так, - создав объекты, мы знаем, чем они являются, но иногда информация может частично отсутствовать. Вот два таких случая.
[x]. В полиморфной структуре данных мы располагаем лишь информацией, общей для всех объектов структуры; однако нам может понадобиться и специфическая информация, применимая только к отдельному объекту.
[x]. Если объект приходит из внешнего мира - файл или по сети - мы обычно не можем доверять тому, что он принадлежит определенному типу.
Давайте займемся исследованием примеров этих двух случаев. Рассмотрим для начала полиморфную структуру данных, такую как список геометрических фигур:
figlist: LIST [FIGURE]
В предыдущих лекциях рассматривалась иерархия наследования фигур. Пусть нам необходимо найти самую длинную диагональ среди всех прямоугольников списка (и вернуть -1, если прямоугольников нет). Сделать это непросто. Выражение item (i).diagonal, где item (i) - i-й элемент списка, идет вразрез с правилом вызова компонентов: item (i) имеет тип FIGURE, а этот класс, в отличие от его потомка RECTANGLE, не содержит в своем составе компонента diagonal. Решение, используемое до сих пор, изменяло определение класса, - в нем появлялся атрибут, задающий тип фигуры. Однако это решение не столь элегантно, как нам хотелось бы.
Теперь пример второго рассматриваемого случая. Пусть имеется механизм хранения объектов в файле или передачи их по сети, аналогичный универсальному классу STORABLE, описанному нами ранее. Для получения объекта используем:
my_last_book: BOOK
...
my_last_book := retrieved (my_book_file)
Значение, возвращаемое retrieved, имеет тип STORABLE библиотеки Kernel, хотя с тем же успехом оно может иметь тип ANY. Но мы не ожидали STORABLE или ANY, - мы надеялись получить именно BOOK. Присваивание my_last_book нарушает правило Совместимости Типов.
Даже если написать собственную функцию retrieved, учитывающую специфику приложения и объявленную с подходящим типом, вам не удастся полностью на нее положиться. В отличие от объектов вашего ПО, в котором согласованность типов гарантируется действующими правилами, данный объект к вам поступает со стороны. При его получении вы могли ошибиться в выборе имени файла и прочитать объект EMPLOYEE вместо объекта BOOK, файл мог быть подделан, а при сетевом доступе данные могли быть искажены при передаче.
Проблема
Из этих примеров ясно: нам может понадобиться механизм удостоверения типа объекта.
Решение этой проблемы, возникающей в специфических, но критически важных случаях, должно быть найдено без потери преимуществ ОО-стиля разработки. В частности, мы не хотим возвращаться к той схеме, которую сами и осудили:
if "f типа RECTANGLE" then
...
elseif "f типа CIRCLE" then
...
и т.д.
Это решение идет вразрез с принципами Единственного Выбора и Открыт-Закрыт. Избежать риска потерь нам помогут два обстоятельства.
[x]. Нет смысла создавать универсальный механизм выяснения типа объектов. В том и другом случае тип объекта предположительно известен. Все, что нам нужно, - это способ проверки гипотезы. Определение принадлежности объекта данному типу носит более частный характер, чем запрос на определение типа. Кроме того, нам не требуется вводить в наш язык никаких операций над типами, к примеру, их сравнение - ужасающая мысль.
[x]. Как уже говорилось, мы не должны влиять на правило Вызова Компонентов. Ни при каких обстоятельствах мы не должны проверять применимость вызова компонента, если класс прошел статистическую проверку. Все, что нам нужно, - это более свободная версия другого правила - правила совместимости типов, позволяющая "испытать тип" и проверить результат.
Механизм решения
И снова запись механизма решения напрямую вытекает из анализа поставленной проблемы. Введем новую форму присваивания, назвав ее попыткой присваивания (assignment attempt):
target ?= source
Знак вопроса указывает на предварительный характер операции. Пусть сущность target имеет тип T, тогда попытка присваивания дает следующий результат:
[x]. если source ссылается на объект совместимого с T типа, присоединить target к объекту так, как это делает обычное присваивание;
[x]. иначе (если source равно void или ссылается на объект несовместимого типа) приписать target значение void.
На эту инструкцию не действуют никакие ограничения типов, кроме одного: тип target (T) должен быть ссылочным.
Новое средство быстро и элегантно решает поставленные проблемы и, прежде всего, дает возможность обращаться к объектам полиморфной структуры с учетом их типа:
maxdiag (figlist: LIST [FIGURE]): REAL is
-- Максимальная длина диагонали прямоугольника в списке;
-- если прямоугольников нет, то -1.
require
list_exists: figlist /= Void
local
r: RECTANGLE
do
from
figlist.start; Result := -1.0
until
figlist.after
loop
r ?= figlist.item
if r /= Void then
Result := Result.max (r.diagonal)
end
figlist.forth
end
end
Здесь применяются обычные итерационные механизмы работы с последовательными структурами данных (лекция 5 курса "Основы объектно-ориентированного проектирования"). Компонент start служит для перехода к первому элементу (если он есть), after - для выяснения того, имеются ли еще не пройденные элементы, forth - для перехода на одну позицию, item (определенный, если not after) - для выборки текущего элемента.
В попытке присваивания используется локальная сущность r типа RECTANGLE. Успех присваивания проверяется сравнением значения r с Void. Если r не Void, то r прямоугольник и можно обратиться к r.diagonal. Эта схема проверки вполне типична.
Заметим, что мы никогда не нарушаем правило Вызова Компонентов: обращения к r.diagonal защищены дважды: статически - компилятором, проверяющим, является ли diagonal компонентом класса RECTANGLE, и динамически - нашей гарантией того, что r не Void, а имеет присоединенный объект.
Обращение к элементу списка - потомку класса RECTANGLE, например SQUARE (квадрат), связывает r с объектом, и его диагональ будет участвовать в вычислениях.
Пример с универсальной функцией чтения объектов retrieval выглядит так:
my_last_book: BOOK
... Сравните с := в первой попытке
my_last_book ?= retrieved (my_book_file)
if my_last_book /= Void then
... "Обычные операции над my_last_book" ...
else
... "Полученное не соответствует ожиданию" ...
end
Правильное использование попытки присваивания
Необходимость попытки присваивания обусловлена, как правило, тем, что на статически объявленный тип сущности положиться нельзя, а опознать тип фактически адресуемого объекта необходимо "на лету". Например, при работе с полиморфными структурами данных и получении объектов из третьих рук.
Заметьте, как тщательно был спроектирован механизм, дающий разработчикам шанс забыть об устаревшем стиле разбора вариантов (case-by-case). Если вы действительно хотите перехитрить динамическое связывание и отдельно проверять каждый вариант типа, вы можете это сделать, хотя вам и придется немало потрудиться. Так, вместо обычного f.display, использующего ОО-механизмы полиморфизма и динамического связывания, можно, - но не рекомендуется, - писать:
