Р.У. Себеста - Основные копцепции языков программирования (2001) (1160794), страница 118
Текст из файла (страница 118)
Эти опера- , являются настраиваемыми в том смысле, что их можно применять к объектам любо- класса, производного от одного и того же базового класса. Для иллюстрации динами-=;кого связывания рассмотрим пример с соборами. Если программа, использующая гесс =гепсЛ Сот)ззс, имеет полиморфную переменную сагбеага1 типа класса ' 1.2. Объектно-ориентированное программирование Ггепсп < ЬСЬ1с, то эта переменная может ссылаться на объекты класса ГгепсЬ СогЬ1с, а также на объекты любых производных классов.
Теперь, когда для вызова метода 6гаи (который определен в классе ГгепсЬ < осЬ1с и во всех его потомках) используется переменная сагцеага1, этот вызов динамически связывается с соответствующей версией метода 6гаи, выбранной по типу, на который в данном случае ссылается полиморфная переменная. Динамическое связывание через полиморфные переменные — мощная концепция. Предположим, что наш пример с соборами написан на языке С. Три варианта французских готических соборов следует разместить в переменных типа еькиас. Может сушествовать одна функция бган, использующая оператор еиьбаЬ для вызова правильной функции рисования, соответствующей конкретному собору. Однако при такой реализации именно программист отвечает за вызов подходящей версии функции рисования.
В рамках объектно-ориентированного программирования сделать зто намного легче. Предположим, что нам нужно дорисовать новый собор, скажем Рагдв. Это вынудило бы нас модифицировать конструкцию еидвсЬ в обшей функции рисования, а также в каждой подобным же образом организованной функции рисования соборов, При объектно-ориентированном программировании, однако, добавление рисунка нового собора не влияет на существующий код. Во многих случаях разработка иерархии наследования приводит к одному или нескольким классам, находящимся на такой высокой ступени иерархии.
что создание объектов подобных классов не имеет смысла. Предположим, что существовал класс ЬЬ1161пд в качестве родительского класса, или класса-предка, для класса ГгепсЬ бот1с. Возможно, нет смысла реализовывать метод 6гаи в классе Ьц1161пд. Однако, поскольку все классы-потомки должны иметь реализацию такого метода, протокол (но не тело) этого метода включается в класс Ьи1161п<з. Этот абстрактный метод часто называется виртуальным методом (т!гша! ше<)<о6). Кроме того. любой класс, включающий в себя хотя бы один виртуальный метод, называется виртуальным классом (т!гша! с!ан), Объекты такого класса не могут быть созданы, поскольку не все его методы имеют тела.
Любой подкласс виртуального метода, для которого следует создавать объекты, должен обеспечивать реализации всех наследуемых виртуальных методов. 11.2А. Вычисления я объектно-ориентирояанных языках Все вычисления в полностью объектно-ориентированном языке выполняются с помощью передачи сообщения объекту для вызова одного из его методов. Ответом на сообщение является объект. возвращающий результат вычислений, выполненных этим методом.
Выполнение программы на объектно-ориентированном языке можно описать как л<оделирование набора компьютеров (объектов), взаимодействующих друг с другом с помощью обмена сообщениями. Каждый объект — абстракция компьютера в том смысле, что он хранит данные и обеспечивает выполнение процессов для манипуляции этими данными. Кроме того, объекты могут передавать и получать сообщения. В сущности, зто основные свойства компьютера — хранить и обрабатывать данные, а также передавать и получать сообщения. Суть объектно-ориентированного программирования состоит в решении задач с помощью идентификации соответствующих реальных объектов и обработки, требуемой для этих объектов; и последующем моделировании этих объектов, их процессов и необходимых связей между ними. 456 Глава 11.
Поддержка объектио-ориентированного программирования 11.3. Вопросы разработки объектно- ориентированных языков При разработке свойств языков программирования. поддерживающих наследование и динамическое связывание, нужно рассмотреть большое количество вопросов. В этом разделе обсуждаются наиболее важные из них. 11.3.1. Исключительность объектов Разработчик языка, полностью полагающийся на объектную модель вычислений, обычно создает объектную систему, содержащую все другие концепции типа. В этом сценарии все, от наименьшего целого числа до полной программной системы, представляет собой объект. Преимушество такого выбора заключается в элегантности и полном единообразии языка и его использования.
Основной недостаток состоит в том, что простые операции должны выполняться с помощью передачи сообщений, которая часто делает их более медленными, чем в императивной модели, в которой такие операции реализуются простыми н быстрыми машинными командами. В этой наиболее чистой модели объектно-ориентированных вычислений все типы являются классами. Нет никаких отличий межлу встроенными классами и классами, определенными пользователем. В действительности, все классы обрабатываются одинаково и все вычисления выполняются путем передачи сообщений.
Существует два варианта. ачьтернативных исключительному использованию объектов, один из них, являющийся общим для императивных языков, дополненных поддержкой объектно-ориентированного программирования, — сохранение императивной модели типов и простое добавление объектной модели. Это порождает более крупный по размерам язык, структура типов которого приводит в замешательство всех пользователей, кроме экспертов.
Другой вариант — создать императивную структуру для элементарных скалярных типов. Зто обеспечивает скорость операций с элементарными значениями, сравнимую с ожидаемой скоростью в императивной модели. К сожалению. эта альтернатива также ведет к усложнению языка. В любом случае необъектные значения нужно смешивать с объектами. Для этого следует использовать так называемые интерфейсные классы (птаррег с!аиез) для необъектных типов, так что некоторые (обычно необходимые) операции могут быть посланы объектам со значениями необъектного типа. В разделе П.б чы рассмотрим пример этого явления в языке )ача. 11.3.2.
Являются ли подклассы подтиппми Обсуждаемый здесь вопрос довольно прост: "Сохраняется ли отношение "есть" между родительским классом и производными классами?". Отношение "есть" гарантирует, что переменная производного класса может появляться везде, где допустимо использование переменной типа родительского класса. Подтипы в языке Ада являются примерами простой формы наследования. Например, вцЪсу)рв ЯМАЕЬ 1йТ Дв 1нТЕСЕЕ капов -100 ..
1001 Переменные типа Бмй'ь 1нт обладают всеми операциями переменных типа 1МТЕСЕй, но могут содержать только подмножество значений, возможных для перемен- 457 ) ) .3. Вопросы разработки объектно-орнвнтнрованных языков ных типа 1(чте~ей. Кроме того, каждую переменную типа Я(чдьь 1((т можно испольэовать всюду, где используются переменные типа 1МТЕ6ЕЕ. Следовательно, каждая переменная ЕИАЬЬ 1ИТ является в некотором смысле переменной типа 1ЫТЕСЕЕ. Производный класс называется подтипом (зцйуре), если он состоит в отношении "есть" со своим родительским классом. Характеристики подкласса, которые подтверждают, что он является подтипом, таковы: подкласс может только добавлять переменные и методы, а также замешать наследуемые методы "совместимым" образом.
Под совместимостью здесь понимается то, что замешаюший метод может заменять замещаемый метод, не вызывая сообщений об ошибках несовместимости типов. Одинаковое количество параметров, а также идентичные типы параметров и возвращаемых значений могли бы, конечно, гарантировать согласованность.
В зависимости от правил совместимости типов в языке возможны менее строгйе ограничения. Наше определение подтипа явно запрещает иметь сущности в родительском классе, которые не наследуются подклассом. 11.3.3. Реализация и наследование интеРфейса Концепция сокрытия информации в абстрактных типах данных предоставляет клиентам интерфейс доступа к своим возможностям, но скрывает нх реализацию. Что еще следует сказать о подклассах? Достаточно ли им видеть только интерфейс своего родительского класса или они должны получить доступ к деталям реализации возможностей родительского класса".
Если для подкласса видимым является только интерфейс, то это называется наследованием интерфеЯса ((пгег(асе (п(зегйапсе). Если видимы также и детали реализации — это наследование реализации (йпр)егпеп(а(юп (п)зег)(апсе). Такое наследование обеспечивает разработчика языка готовыми компонентами, достоинства и недостатки которых следует рассмотреть.