Тимоти Бадд Объектно-ориентированное программирование в действии Перевел с английского А. Бердников Главный редактор В. Усманов Заведующий редакцией А. Быстрое Литературный редактор Ф. Андреев Художественный редактор И. Полеводов Художник А. Суворов Корректоры В. Листова, М. Одинакова Верстка Ю. Сергиенко ББК 32.973 УДК. 681.3.06 Бадд Т. Б 15 Объектно-ориентированное программирование в действии/Перев. с англ. — СПб.: Питер, 1997. —464с.: ил. ISBN 5-88782-270-8 Второе американское издание книги известного специалиста по объектно- ориентированному программированию выпускается на русском языке по лицензии издательства Addison Wesley Longman. В ней рассматриваются теоретические и практические аспекты ООП (как на уровне разработки программ, так и на уровне работы компиляторов), позволяющие с наименьшими затратами получать современные программы со сложной логической структурой. Автор обобщает опыт объектно-ориентированного программи- рования на примерах таких языков, как Java, C++, Object Pascal и др. Соответствующий круг вопросов весьма актуален, поскольку в практике современного программирования приходится иметь дело со все более сложными логическими и программными объектами. Рассмотрение теоретических вопросов на страницах книги удачно сочетается с многочис- ленными наглядными примерами. Для понимания материала достаточно владения каким- либо традиционным языком программирования типа С или Pascal, хотя в отдельных случаях (особенно в последней четверти книги) могут оказаться желательны (но не необходимы) и более глубокие знания. Книга будет полезна преподавателям, студентам, разработчикам прикладных программ и всем, кто хочет освоить современные подходы к программированию. Original English language Edition Copyright © Addison Wesley Longman, Inc., 1997 © Перевод на русский язык, А. Бердников, 1997 © Серия, оформление, издательство «Питер Паблишинг», 1997 Published by arrangement with the original publisher, Addison-Wesley Longman, U.S.A. Подготовлено к печати издательством «Питер Пресс» по лицензионному договору с Addison-Wesley Longman, США. Все права защищены. Никакая часть данной книги не может быть воспроизведена в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. ISBN 5-88782-270-8 ISBN 0-201-82419-1 (англ.) Все упомянутые в данном издании товарные знаки и зарегистрированные товарные знаки принадлежат своим законным владельцам. Информация, содержащаяся в данной книге, получена из источников, рассматриваемых издательством как надежные. Тем не менее, имея в виду возможные человеческие или технические ошибки, издательство не может гарантировать абсолютную точность и полноту приводимых сведений и не несет ответственности за возможные ошибки, связанные с использованием книги. Издательство <[1итер Паблишинг». 196105, С.-Петербург, ул. Благодатная, 67. Лицензия ЛР № 064868 от 9.12.96. Подписано и печать 30.08.97. формат 70Х100'/'^. Усл. п. л. 37,7. Тираж 6000 экз. Заказ № 848. Отпечатано с диапозитивов в ГПП «Печатный Двор» Государственного комитета РФ по печати. 197110, С.-Петербург, Чкаловский пр., 15. Краткое содержание Введение........................................................................................... 14 1. Объектно-ориентированное мышление ....................................20 2. Объектно-ориентированное проектирование ..........................48 3. Классы и методы ........................................................................69 4. Сообщения, экземпляры и инициализация ..............................92 5. Учебный пример: задача о восьми ферзях........................... 113 6. Учебный пример: игра «Бильярд............................................ 131 7. Наследование............................................................................ 143 8. Учебный пример: пасьянс ....................................................... 163 9. Повторное использование кода ............................................ 184 10. Подклассы и подтипы............................................................ 195 11. Замещение и уточнение ........................................................212 12. Следствия наследования ........................................................229 13. Множественное наследование ..............................................250 14. Полиморфизм .........................................................................267 15. Учебный пример: контейнерные классы.............................. 287 16. Пример: STL................ ........................................................ 302 17. Видимость и зависимость ......................................................315 18. Среды и схемы разработки...................................................336 19. Учебный пример: среда разработки ....................................348 20. Новый взгляд на классы........................................................358 21. Реализация объектно-ориентированных языков................ 37 7 Приложение А. Исходный код программ для задачи «Восемь ферзей» .......................................................................391 Приложение Б. Исходный код игры «Бильярд» ........................402 Приложение В. Исходный код программ для карточного пасьянса .........................................................417 Глоссарий .......................................................................................425 Библиография................................................................................ 442 Алфавитный указатель ..................................................................451 Содержание ВВЕДЕНИЕ............................................................................. 14 Как получить исходные тексты ............................................................... 16 Что требуется знать для чтения книги.................................................... 16 Предисловие к первому изданию............................................................ 17 Благодарности ......................................................................................... 18 1. ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ МЫШЛЕНИЕ ........ 20 1.1. Почему ООП так популярно?........................................................ 20 1.2. Язык и мышление........................................................................... 21 Эскимосы и снег • Пример из области программирования • Принцип Чёрча и гипотеза Ворфа 1.3. Новая парадигма............................................................................ 26 1.4. Способ видения мира .................................................................... 27 Агенты, обязанности, сообщения и методы • Обязанности и ответственности • Классы и экземпляры • Иерархии классов и наследование • Связывание и переопределение методов • Краткое изложение принципов 1.5. Вычисление и моделирование ....................................................... 34 Сила метафор • Как избежать бесконечной регрессии 1.6. Барьер сложности .......................................................................... 36 Нелинейное увеличение сложности • Механизмы абстрагирования 1.7. Многократно используемое программное обеспечение ................. 42 1.8. Резюме ........................................................................................... 43 Что читать дальше................................................................................... 44 Упражнения ............................................................................................. 47 2. ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОЕКТИРОВАНИЕ......................................................... 48 2.1. Ответственность подразумевает невмешательство ......................... 48 2.2. Программирование «в малом» и «в большом» ............................... 50 2.3. Почему надо начинать с функционирования? .............................. 50 2.4. Учебный пример: проектирование на основе обязанностей......... 51 Интерактивный разумный кухонный помощник • Работа по сценарию • Идентификация компонент СОДЕРЖАНИЕ________________________________________7 2.5. CRC-карточка — способ записи обязанностей ............................. 53 Дайте компонентам физический образ • Цикл «что/кто» • Документирование 2.6. Компоненты и поведение .............................................................. 55 Отложенные решения • Готовность к изменениям • Продолжение работы со сценарием • Диаграммы взаимодействия 2.7. Компоненты программы ................................................................. 60 Поведение и состояние • Экземпляры и классы • Зацепление и связность • Интерфейс и реализация модуля — принципы Парнаса 2.8. Формализация интерфейса ............................................................ 63 Выбор имен 2.9. Выбор представления данных........................................................ 65 2.10. Реализация компонент ................................................................... 66 2.11. Интеграция компонент................................................................... 67 2.12. Сопровождение и развитие ........................................................... 67 Упражнения ............................................................................................. 68 3. КЛАССЫ И МЕТОДЫ...................................................... 69 3.1. Инкапсуляция ................................................................................. 69 3.2. Разновидности классов................................................................... 70 3.3. Пример: игра в карты ................................................................... 72 3.4. Интерфейс и реализация............................................................... 73 3.5. Классы и методы в ООП ............................................................... 74 Классы и методы в языке Object Pascal • Классы и методы в языке Smalltalk • Классы и методы в языке Objectioe-C • Классы и методы в языке C++ • Классы и методы в языке Java Упражнения ............................................................................................. 90 4. СООБЩЕНИЯ, ЭКЗЕМПЛЯРЫ И ИНИЦИАЛИЗАЦИЯ..................................................... 92 4.1. Синтаксис пересылки сообщений .................................................. 92 Синтаксис пересылки сообщений в Object Pascal • Синтаксис пересылки сообщений в C++ • Синтаксис пересылки сообщений в Java • Синтаксис пересылки сообщений в Smalltalk • Синтаксис пересылки сообщений в Objective-С 4.2. Способы создания и инициализации ............................................. 98 Стек против •«кучи^> * Восстановление памяти • Указатели • Создание неизменяемого экземпляра объекта 4.3. Механизмы создания и инициализации........................................ 101 Создание и инициализация в C++ • Создание и инициализация в Java • Создание и инициализация в Objective-С • Создание и инициализация в Object Pascal • Создание и инициализация в Smalltalk Упражнения ........................................................................................... 112 8 __________________________СОДЕРЖАНИЕ 5. УЧЕБНЫЙ ПРИМЕР: ЗАДАЧА О ВОСЬМИ ФЕРЗЯХ..................................................... 113 5.1. Задача о восьми ферзях.............................................................. 113 Создание объектов, решающих «самих себя» 5.2. Использование генераторов......................................................... 116 Инициализация • Нахождение решения • Продвижение на следующую позицию 5.3. Задача о восьми ферзях в различных языках программирования .......................................................................118 Задача о восьми ферзях: Object Pascal • Задача о восьми ферзях: C++ • Задача о восьми ферзях: Java • Задача о восьми ферзях: Objective-C • Задача о восьми ферзях: Smalltalk Упражнения ........................................................................................... 130 6. УЧЕБНЫЙ ПРИМЕР: ИГРА «БИЛЬЯРД» ..................... 131 6.1. Элементы бильярда ...................................................................... 131 6.2. Графические объекты .................................................................. 132 Графический объект Wall (стенка) • Графический объект Hole (луза) • Графический объект Ball (шар) 6.3. Основная программа.................................................................... 138 6.4. Использование наследования....................................................... 139 Упражнения ........................................................................................... 142 7. НАСЛЕДОВАНИЕ........................................................... 143 7.1. Интуитивное описание наследования .......................................... 143 7.2. Подкласс, подтип и принцип подстановки.................................. 144 Подтипы и строгий контроль типов данных 7.3. Формы наследования ................................................................... 146 Порождение подклассов для специализации (порождение подтипов) • Порождение подкласса для спецификации • Порождение подкласса с целью конструирования • Порождение подкласса для обобщения • Порождение подкласса для расширения • Порождение подкласса для ограничения • Порождение подкласса для варьирования • Порождение подкласса для комбинирования • Краткое перечисление форм наследования 7.4. Наследование в различных языках программирования............... 151 Наследование в языке Object Pascal • Наследование в языке Smalltalk • Наследование в языке Objective-C • Наследование в языке C++ • Наследование в языке Java 7.5. Преимущества наследования........................................................ 157 Повторное использование программ • Использование общего кода • Согласование интерфейса • Программные компоненты • Быстрое макетирование • Полиморфизм и структура • Маскировка информации СОДЕРЖАНИЕ________________________________________9 7.6. Издержки наследования............................................................... 159 Скорость выполнения • Размер программ • Накладные расходы, на посылку сообщений • Сложность программ Упражнения ........................................................................................... 161 8. УЧЕБНЫЙ ПРИМЕР: ПАСЬЯНС.................................. 163 8.1. Класс игральных карт Card ......................................................... 163 8.2. Связные списки............................................................................ 166 8.3. Правила пасьянса ........................................................................ 169 8.4. Стопки карт — наследование в действии .................................. 170 Основание SuitPile • Колода DeckPile • Промежуточная стопка DiscardPile • Стопка расклада TablePile 8.5. Полиморфная игра ...................................................................... 177 8.6. Создание более сложной игры .................................................... 181 Упражнения ........................................................................................... 182 9. ПОВТОРНОЕ ИСПОЛЬЗОВАНИЕ КОДА .................. 184 9.1. Наследование и принцип подстановки........................................ 184 «Быть экземпляром^ и «включать как частью 9.2. Композиция и наследование: описание ....................................... 186 Использование композиции • Применение наследования • Закрытое наследование в языке C++ 9.3. Противопоставление композиции и наследования ...................... 190 9.4. Повторное использование кола: реальность?.............................. 192 Упражнения ........................................................................................... 194 10. ПОДКЛАССЫ И ПОДТИПЫ ...................................... 195 10.1. Связывание методов и сообщения............................................... 196 Связывание методов • Проблема обращения полиморфизма 10.2. Связывание в языках программирования .................................... 198 Связывание в языке Object Pascal • Связывание в языке Smalltalk • Связывание в языке Objective-C • Связывание в языке C++ • Связывание в языке Java 10.3. Как связывать: статически или динамически? ............................. 209 Упражнения ........................................................................................... 210 11. ЗАМЕЩЕНИЕ И УТОЧНЕНИЕ.................................... 212 11.1. Добавление, замещение и уточнение .......................................... 212 Американская и скандинавская семантики 11.2. Замещение методов...................................................................... 213 Замещение методов и принцип подстановки • Уведомление о замещении 11.3. Замещение в разных языках........................................................ 215 Замещение в C++ • Замещение методов в Object Pascal • Замещение в Smalltalk и Objective-C • Замещение в Java 10_______________________________________СОДЕРЖАНИЕ 11.4. Уточнение методов....................................................................... 220 Уточнение в языках Simula и Beta • Методы-оболочки в языке CLOS 11.5. Уточнение в разных языках......................................................... 225 Уточнение в Object Pascal • Уточнение в C++ • Уточнение в Smalltalk, Java и Objective-C Упражнения ........................................................................................... 227 12. СЛЕДСТВИЯ НАСЛЕДОВАНИЯ................................. 229 12.1. Выделение памяти........................................................................ 229 Размещение минимальной статической памяти • Размещение максимальной статической памяти • Динамическое выделение памяти 12.2. Присваивание............................................................................... 235 Присваивание в C++ • Присваивание в Object Pascal и Java • Присваивание в Smalltalk • Присваивание в Objective-C 12.3. Проверка на равенство ............................................................... 240 Ковариация и контрвариация • Равенство в Objective-C, Java и Object Pascal * Равенство в Smalltalk * Равенство в С+ + 12.4. Преобразование типов................................................................. 246 Упражнения ........................................................................................... 249 13. МНОЖЕСТВЕННОЕ НАСЛЕДОВАНИЕ .....................250 13.1. Комплексные числа ...................................................................... 250 13.2. Всплывающие меню ..................................................................... 253 13.3. Двусмысленность имен ................................................................. 255 Наследование через общих предков 13.4. Множественное наследование в C++.......................................... 257 13.5. Множественное наследование в Java .......................................... 265 Литература для дальнейшего чтения..................................................... 266 Упражнения ........................................................................................... 266 14. ПОЛИМОРФИЗМ ......................................................... 267 14.1. Полиморфизм в языках программирования ................................ 267 Полиморфные функции в динамических языках • Абстракции низкого и высокого уровней 14.2. Разновидности полиморфизма ..................................................... 269 14.3. Полиморфные переменные .......................................................... 270 14.4. Перегрузка ................................................................................... 271 Перегрузка в реальной жизни • Перегрузка и приведение типа • Перегрузка не подразумевает сходство • Параметрическая перегрузка 14.5. Переопределение......................................................................... 274 Переопределение в классе Magnitude СОДЕРЖАНИЕ^^ _________________________________11 14.6. Отложенные методы..................................................................... 275 14.7. Чистый полиморфизм................................................................... 276 14.8. Обобщенные функции и шаблоны............................................... 278 14.9. Полиморфизм в различных языках.............................................. 279 Полиморфизм в C++ • Полиморфизм в Java • Полиморфизм в Object Pascal • Полиморфизм в Objective-C • Полиморфизм в Smalltalk 14.10. Эффективность и полиморфизм ............................................... 285 Упражнения ........................................................................................... 285 15. УЧЕБНЫЙ ПРИМЕР: КОНТЕЙНЕРНЫЕ КЛАССЫ..287 15.1. Использование традиционных подходов...................................... 287 15.2. Контейнеры в динамических языках............................................ 290 15.3. Контейнеры в языках со строгим контролем типа данных......... 293 15.4. Скрытое приведение типа данных при наследовании ................ 295 15.5. Параметризованные классы ......................................................... 297 Циклы и итерации в C++ Упражнения ........................................................................................... 301 16. ПРИМЕР: STL............................................................... 302 16.1. Итераторы.................................................................................... 304 16.2. Объекты-функции......................................................................... 305 16.3. Пример программы: инвентаризация........................................... 306 16.4. Пример программы: графы ......................................................... 308 16.5. Пример программы: алфавитный указатель ................................ 310 16.6. Будущее ООП .............................................................................. 313 Упражнения ........................................................................................... 314 17. ВИДИМОСТЬ И ЗАВИСИМОСТЬ.............................. 315 17.1. Зацепление и связность ............................................................... 316 Разновидности зацепления • Разновидности связности • Зацепление и связность в ООП • Закон Деметера • Видимость: на уровне классов и на уровне объектов • Активные значения 17.2. Клиенты-подклассы и клиенты-пользователи ............................... 322 17.3. Управление доступом и видимостью ........................................... 324 Видимость в Smalltalk • Видимость в Object Pascal • Видимость в C++ • Видимость в Java • Видимость в Objective-C 17 А. Преднамеренное зацепление ....................................................... 333 Упражнения ........................................................................................... 334 18. СРЕДЫ И СХЕМЫ РАЗРАБОТКИ .............................. 336 18.1. Среда разработки ........................................................................ 336 Java API • Среда моделирования 12 СОДЕРЖАНИЕ 18.2. Схемы разработки........................................................................ 340 Схемы с посредником • Схемы обхода • Схема двойной диспетчеризации • Классификация схем разработок Упражнения ........................................................................................... 347 19. УЧЕБНЫЙ ПРИМЕР: СРЕДА РАЗРАБОТКИ ............ 348 19.1. Компоненты GUI.......................................................................... 348 19.2. Выполнение, управляемое событиями ......................................... 349 19.3. Настройка через наследование ................................................... 350 19.4. Классы в среде LAF .................................................................... 351 19.5. Класс application .......................................................................... 351 Класс button • Классы menu и menultem 19.6. Резюме ......................................................................................... 356 Упражнения ........................................................................................... 357 20. НОВЫЙ ВЗГЛЯД НА КЛАССЫ.................................. 358 20.1. Классы как типы .......................................................................... 358 Как наследование усложняет понятие типа • Наследование и память 20.2. Классы как объекты ..................................................................... 363 Фабрики по созданию объектов • Класс Class • Метаклассы и класс-методы • Инициализация объектов • Подстановки в Objectiue-C 20.3. Данные класса ............................................................................. 368 Переменные класса в Smalltalk • Переменные класса в C++ • Переменные класса в Java • Переменные класса в Objective-C 20.4. Нужны ли классы? ....................................................................... 372 Что такое знание? • Делегирование полномочий Упражнения ........................................................................................... 376 21. РЕАЛИЗАЦИЯ ОБЪЕКТНО-ОРИЕНТИРОВАННЫХ ЯЗЫКОВ .......................................................................... 377 21.1. Компиляторы и интерпретаторы .................................................. 377 21.2. Компиляторы................................................................................ 378 Проблема «срезки» • Соответствие между методами и сообщениями • Таблицы виртуальных методов • Кодирование имен • Таблицы диспетчеризации 21.3. Интерпретаторы ........................................................................... 386 Литература для дальнейшего чтения .....................................................389 Упражнения ........................................................................................... 389 СОДЕРЖАНИЕ_______________________________________13^ ПРИЛОЖЕНИЕ А. ИСХОДНЫЙ КОД ПРОГРАММ ДЛЯ ЗАДАЧИ «ВОСЕМЬ ФЕРЗЕЙ............................... 391 A.I. «Задача о восьми ферзях» на языке Apple Object Pascal............ 391 А.2. «Задача о восьми ферзях» на языке C++ ................................... 393 А.З. «Задача о восьми ферзях» на языке Java.................................... 395 HTML-файл для anwiemajava А.4. «Задача о восьми ферзях» на языке Objective-C ......................... 398 А.5. «Задача о восьми ферзях» на языке Smalltalk............................. 400 ПРИЛОЖЕНИЕ Б. ИСХОДНЫЙ КОД ИГРЫ «БИЛЬЯРД»........................................................... 402 Б.1. Версия без использования наследования .................................... 402 Б.2. Версия с использованием наследования...................................... 410 ПРИЛОЖЕНИЕ В. ИСХОДНЫЙ КОД ПРОГРАММ ДЛЯ КАРТОЧНОГО ПАСЬЯНСА .................................. 417 B.I. HTML-файл для апплета.............................................................. 417 В.2. Файл Solitare.java ......................................................................... 417 ГЛОССАРИЙ........................................................................ 425 БИБЛИОГРАФИЯ................................................................ 442 АЛФАВИТНЫЙ УКАЗАТЕЛЬ ............................................. 451 ВВЕДЕНИЕ Я начал писать эту книгу в 1988 году. В конце 1990 года увидело свет ее первое издание. За восемь лет, прошедших с начала работы над книгой, мы стали свидетелями изменений в объектно-ориентированном программировании, потре- бовавших значительных исправлений и добавлений в тексте. К ним можно отнести следующее: • Более глубокое понимание различия между подклассами и подтипами и признание того факта, что зачастую это далеко не одно и то же. • Быстрый рост, эволюция и стандартизация языка программирования C++, включая введение шаблонов, исключительных ситуаций, логических пере- менных, пространства имен, строк, идентификации типов данных во время выполнения (RTTI — run-time type identification system) и стандартной библиотеки. • Появление языка программирования Java — восхитительного нового средства для разработки приложений World Wide Web. • Медленное исчезновение языка Object Pascal после того, как Apple пере- стала использовать его в качестве основного средства создания приложе- ний для компьютеров Macintosh. Однако на PC этот язык возвращается к жизни в образе Delphi. • Закат и падение Objective-C. Для тех, кто профессионально занимается языками программирования, это — болезненная утрата, так как динами- чески типизированный Objective-C оставался практически единственной альтернативой строгой типизации в духе C++. Поэтому в данном издании я продолжил обсуждение Objective-C. • Развитие новых интересных объектно-ориентированных языков (таких, как Beta, CLOS и Java), которые соединяют современные и классические идеи. • Становление представлений о совокупностях классов, в частности появле- ние таких понятий, как шаблоны конструирования классов и среда разра- ботки приложений. По этим и многим другим причинам почти каждая глава книги была пересмотре- на. Тем не менее я попытался сохранить общую структуру книги, которая может быть представлена в виде следующего списка тем: I. Введение и общий замысел. Глава 1 дает неформальное определение базо- вых концепций объектно-ориентированного программирования. Глава 2 вводит принцип разработки на основе обязанностей. Эти две главы являются фунда- ментальными, и их следует изучить подробно. В частности, я настоятельно ВВЕДЕНИЕ_________________________________________15 рекомендую выполнить по крайней мере одно упражнение с CRC-карточками из главы 2. Техника CRC-карточек, по моему мнению, является одной из лучших для определения функциональности, ответственности и инкапсуляции при базо- вой разработке проекта. II. Классы, методы и сообщения. Главы 3 и 4 определяют синтаксис, исполь- зуемый в языках Smalltalk, C++, Java, Objective-C и Object Pascal для задания классов, методов и посылки сообщений. Глава 3 заостряет внимание на стати- ческих свойствах (классах и методах), в то время как глава 4 описывает динамические аспекты (создание объектов и пересылку сообщений). Главы 5 и 6 развивают эти идеи. Здесь же начинаются обучающие примеры — образцы программ, разработанных в объектно-ориентированной манере и иллюстрирую- щих различные черты объектной техники. III. Наследование и повторное использование кода. Главы 7, 8 и 9 вводят концепцию наследования и объясняют ее применение для обеспечения повтор- ного использования кода. Пример из главы 8, написанный на языке Java, иллюстрирует также применение стандартного прикладного программного ин- терфейса (API — application program interface). В главе 9 противопоставляются наследование и композиция в качестве альтернативных техник обеспечения повторного использования кода. IV. Более подробно о наследовании. В главах с 10 по 13 концепция наследо- вания анализируется более детально. Введение наследования оказывает влияние на почти все аспекты языка программирования, которое зачастую не сразу очевидно для начинающего. В главе 10 обсуждается поиск методов и их свя- зывание с сообщениями. Там же иллюстрируется тот факт, что подклассы и подтипы — это не одно и то же. В главе 11 обсуждается семантика переопреде- ления методов и отмечаются две совершенно различные интерпретации этого понятия. В главе 12 продолжается тема переопределения и исследуются некото- рые следствия наследования применительно к механизмам управления памятью, присваивания и сравнения. Наконец, в главе 13 изучается множественное насле- дование. V. Полиморфизм. В значительной степени мощь объектно-ориентированного программирования проистекает из применения различных форм полиморфизма. В главе 14 читатель знакомится с основными механизмами полиморфизма в объектно-ориентированных языках и двумя показательными обучающими при- мерами. Первый пример в главе 15 рассматривает создание библиотек общего назначения. Конкретная библиотека, а именно недавно разработанная стандарт- ная библиотека шаблонов (STL — Standard Template Library) для языка C++, обсуждается в главе 16. VI. Разработка программного обеспечения. В главе 17 обсуждается ряд стан- дартных тем компьютерной инженерии в контексте объектно-ориентированного программирования. Глава 18 знакомит с несколькими относительно новыми концепциями — средой разработки приложений и шаблонами разработки. Оба подхода основаны на использовании наборов классов. Наконец, в главе 19 приводится конкретный пример среды разработки. 16. ВВЕДЕНИЕ VII. Продвинутое изучение. Концепция классов при внимательном рассмотре- нии не столь проста, как нас пытались убедить в главе 3. В главе 20 рассмотре- ны более глубокие аспекты объектно-ориентированного программирования. Там же обсуждаются делегирование (являющееся примером объектно-ориентирован- ного программирования без классов) и понятие метакласса (на уровне собственно языка программирования). В главе 21 в общих чертах описаны разнообразные техники реализации, применяющиеся при создании объектно- ориентированных языков. В десятинедельном курсе, который я читаю в университете штата Орегон, приблизительно одну неделю я посвящаю каждому из основных направлений, описанных выше. В то же самое время студенты работают над не слишком большим проектом. Конкретный объектно-ориентированный язык разработки они выбирают сами. Семестр заканчивается представлением дизайна проекта и его реализацией. Первое издание книги я закончил главой «Дополнительная информация». К сожалению, объектно-ориентированное программирование развивается так быс- тро, что любая дополнительная информация почти сразу устаревает. Поэтому я не включил во второе издание главу с таким названием. Вместо этого я попытаюсь поддерживать страничку Web с последними сведениями. Как получить исходные тексты Исходные тексты обучающих примеров, представленных в книге, можно полу- чить анонимно, обратившись через ftp по адресу ftp.cs.orst.edu, каталог /pub/ budd/oopintro. В том же каталоге можно будет найти дополнительную информа- цию, например список ошибок, обнаруженных в книге, упражнения, копии «прозрачек», которые я использую в своем курсе. Все это можно также увидеть через World Wide Web на моих личных домашних страницах по адресу http:// www.cs.orst.edu/~budd/oopintro. Вопросы вы можете посылать электронной почтой по адресу budd@cs.orst.edu или обычной почтой: Professor Timothy A. Budd, Department of Computer Science, Oregon State University, Corvallis, Oregon, 97331. Что требуется знать для чтения книги Я предполагаю, что читатель знаком хотя бы с одним традиционным языком программирования, например Pascal или С. Мои курсы были вполне успешно восприняты студентами последнего года undegraduate level и первого graduate level. В некоторых случаях (особенно в последней четверти книги) более глубокие знания окажутся полезны, но они не являются обязательными. На- пример, студент, который специализируется на разработке программного обеспечения, легче воспримет материал главы 17, а обучающийся построению компиляторов сочтет главу 21 вполне понятной. Тематику обеих глав можно упростить при необходимости. ВВЕДЕНИЕ___________________________________________17 Предисловие к первому изданию Когда-то я начал вести курс лекций по языку Smalltalk и скоро обнаружил, что учебной литературы по данной теме не существует. Пришлось написать книгу по Smalltalk [Budd 1987], на основе которой я несколько лет вел семинар по языку Smalltalk и объектно-ориентированному программированию. Несомнен- но, вы уже догадались, что книгу, которую вы держите в руках, породила та же потребность. Начав преподавание в конце 80-х, я получал все возрастающее число запросов на курс, построенный на основе C++. В то же самое время популярность компьютеров Macintosh сделала известным язык Object Pascal. Наконец, появление NeXT вызвало интерес к обучению Objective-C. Поскольку я не собирался давать четыре различных курса, я решил вести один курс, в котором излагал принципы объектно-ориентированного программирова- ния, иллюстрируя их примерами на всех четырех языках. Слушатели узнали кое-что о каждом языке и смогли выполнить проект на том языке, который им понравился. Вскоре я отправился на поиски учебника по курсу такого типа. К моему удивлению, довольно быстро обнаружилось, что все имеющиеся книги, совер- шенно восхитительные во многих отношениях, ориентированы на один отдельно взятый язык. Я изучил следующие труды: Кокс [Сох 1986], Голдберг и Робинсон [Goldberg 1983], Кэхлер и Паттерсон [Kaechler 1986], Кин [Keen 1989], Мейер [Меуег 1988а], Пинсон и Винер [Pinson 1988], а также, разумеется, Винер и Пинсон [Wiener 1988], Страуструп [Stroustrup 1986], Поль [Pohl 1989]. Хотя в результате я отобрал некоторые из них как вспомогательные, все эти книги были отвергнуты в качестве основного учебника по той простой причине, что все они сводятся к утверждению: «объектно-ориентированное программирование есть объектно-ориентированное программирование на языке X», где Х — это люби- мый язык программирования того или иного автора. Итак, мне пришлось писать свои собственные лекции. На следующий год я пересмотрел и дополнил свои записи. В результате родилась эта книга. Некоторые слушатели моего курса (который оказался намного популярнее и, следовательно, значительно многочисленнее, чем я предполагал) дополнительно к выполнению проектов на одном из четырех языков, упомянутых выше, успешно завершили работы на языках Actor [Actor 1987], Turbo Pascal [Turbo 1988] и CLOS [Keen 1989]. Так как моя задача состояла в том, чтобы передать принципы объектно-ориентированного программирования вне зави- симости от конкретного языка, я спросил этих уникумов о том, пригодились ли им в написании программ мои лекции. На основании их положительных ответов я убедился, что по крайней мере частично достиг определенного уровня языко- вой независимости материала. Эту книгу нельзя считать ни учебником языка программирования, ни справоч- ником по любому из рассматриваемых четырех языков. В каждом из них есть многочисленные нюансы, специфичные для языка программирования в целом или его конкретной реализации, которые я не считаю возможным обсуждать в 18 ВВЕДЕНИЕ данной книге, но которые, несомненно, имеют важное практическое значение для программиста. Благодарности Я, безусловно, благодарен всем 65 студентам моей группы CS589 универ- ситета штата Орегон, которые в течение 1989 года вытерпели на себе процесс становления чернового варианта этой книги. Они получали на руки по одной главе, зачастую всего за день или за два до того, как им предстояло услышать соответствующий материал на лекции. Их терпению отдается должное. Конк- ретные примечания, исправления, замечания и критика моих уважаемых студентов были чрезвычайно полезны. В частности, я хочу поблагодарить за подробные комментарии Томаса Амофа (Thomas Amoth), Кима Дронгезена (Kim Drongesen), Франка Грисволда (Frank Griswold), Раджива Пандея (Rajeev Pandey) и Фила Радера (Phil Ruder). Пасьянс из главы 8 был вдохновлен проектом, выполненным Кимом Дронгезеном, а игра «Бильярд» (глава 6) основана на проекте Гунтара Мамье (Guenter Mamier) и Дитриха Веттшерека (Dietrich Wettschereck). Однако в обоих случаях собственно текст программы был мной полностью переработан. Фактически мои варианты программ с целью лучшего изложения были значительно сокращены и никоим образом не сопоставимы с намного превос- ходящими их проектами, выполненными этими студентами. Я также признателен тем людям, которые внесли комментарии, исправления, замечания. К ним относятся: Майкл Адар (Michael Adar), Джери Андреас (Jerrie Andreas), Линн Кокран (Lynn Cochran), Брэд Кокс (Brad Сох), Грахам Дамплетон (Graham Dumpleton), Питер Грогоно (Peter Grogono), Нола Хаг (Nola Hague), Марсиа Хортон (Marcia Horton), Ральф Джонсон (Ralph John- son), Дуг Ли (Doug Lea), Тэд Льюис (Ted Lewis), Стэнли Липман (Stanley Lippman), Дарси МакКаллум (Darcy McCallum), Линдсей Маршал (Lindsey Marshall), Макку Саккинен (Makku Sakkinen), Майкл Шаре (Michael Share), Дэйв Тензер (Dave Taenzer), Набиль Замель (Nabil Zamel). Хочу побла- годарить рецензентов Еда Герингера (Ed Gehringer), Джеймса Хелиотиса (James Heliotis), Карла Либергерра (Karl Lieberherr), Джеффа Паркера (Jeff Parker), Джастима Смита (Justim Smith) и Даниеля Штермса (Daniel Sterms). Листинги программ были напечатаны с помощью макросов LaTeX, основанных на макросах форматирования программ С, написанных Эамонном МакМанусом (Eamonn McManus) из колледжа Святой Троицы, Дублин. Для любого автора всегда полезно узнать точку зрения других людей на его книгу. Поэтому я благодарю Арину Бринц (Arina Brintz), Луиса Линена (Louise Leenen), Томми Мейера (Tommie Meyer), Елену Розенблатт (Helene Rosenblatt) и Анель Вильоен (Anel Viljoen) с факультета компьютерных наук и информационных систем южноафриканского университета в Претории. ВВЕДЕНИЕ_________________________________________19 Огромное количество людей оказало помощь в устранении ошибок и недочетов первого издания и внесении улучшений. Я признателен всем и сожалею, что не могу перечислить здесь всех. Рецензентами второго издания были Томас Бонник (Thomas Bonnick), Северо-Восточный университет, М. А. Шридхар (М. A. Shridhar), университет штата Южная Каролина, и Уолтер С. Дохерити (Walter С. Daugherity), A&M университет, Техас. Линн Доран Коте (Lynne Doran Cote) и Дебора Лафферти (Debora Laffertey) из Addison Wesley были компетентными и терпеливыми редакторами второго издания. Помощь в издании книги была оказана Анн Найт (Ann Knight) из Superscript. ГЛАВА ОБЪЕКТНО- ОРИЕНТИРОВАННОЕ МЫШЛЕНИЕ Объектно-ориентированное программирование (ООП) стало чрезвычайно по- пулярно в последние несколько лет. Производители программного обеспечения бросаются создавать объектно-ориентированные версии своих продуктов. По- явилось несчетное количество книг и специальных выпусков академических (и не только) журналов, посвященных этому предмету. Студенты стремятся к записи «компетентен в объектно-ориентированном программировании» в своих характеристиках. Чтобы оценить эту безумную активность, отметим, что объектно-ориентированное программирование приветствуется с ббльшим эн- тузиазмом, чем тот, который мы видели ранее при провозглашении таких революционных идей, как «структурное программирование» или «экспертные системы». Моя цель в первой главе состоит в том, чтобы исследовать и объяснить основные принципы объектно-ориентированного программирования, а также проиллюстрировать следующие утверждения. • ООП — это революционная идея, совершенно непохожая на что-либо выдвигавшееся в программировании. • ООП — это эволюционный шаг, естественным образом вытекающий из предшествующей истории. 1.1. Почему ООП так популярно? Я перечислю некоторые (на мой взгляд — самые главные) причины огромной популярности объектно-ориентированного программирования в последнее десятилетие: • надежда, что ООП может просто и быстро привести к росту продуктив- ности и улучшению надежности программ, помогая тем самым разрешить кризис в программном обеспечении; • желание перейти от существующих языков программирования к новой технологии; • вдохновляющее сходство с идеями, родившимися в других областях. 1.2. ЯЗЫК И МЫШЛЕНИЕ_________________________________21 Объектно-ориентированное программирование является лишь последним зве- ном в длинной цепи решений, которые были предложены для разрешения «кризиса программного обеспечения». Положа руку на сердце: кризис про- граммного обеспечения просто означает, что наше воображение и те задачи, которые мы хотим решить с помощью компьютеров, почти всегда опережают наши возможности. Несмотря на то что объектно-ориентированное программирование действи- тельно помогает при создании сложных программных систем, важно помнить, что ООП не является «серебряной пулей» (термин, ставший популярным благодаря Фреду Бруксу [Brooks 1987]), которая запросто справляется с чудо- вищем. Программирование по-прежнему является одной из наиболее трудных задач, взваливаемых на себя человеком. Чтобы стать профессионалом в про- граммировании, необходимы талант, способность к творчеству, интеллект, знания, логика, умение строить и использовать абстракции и, самое главное, опыт — даже в том случае, когда используются лучшие средства разработки. Я подозреваю, что есть и другая причина особой популярности таких языков программирования, как C++ и Object Pascal (по контрасту со Smalltalk и Beta). Она состоит в том, что и администрация и разработчики надеются, что про- граммист на языках С или Pascal может перейти на C++ или Object Pascal с той же легкостью, с которой происходит добавление нескольких букв на ти- тульный лист сертификата о специальности. К сожалению, так происходит не всегда. Объектно-ориентированное программирование является новым понима- нием того, что собственно называется вычислениями, а также того, как мы можем структурировать информацию внутри компьютера. Чтобы стать профес- сионалом в технике ООП, требуется полная переоценка привычных методов разработки программ. 1.2. Язык и мышление Человеческие существа не общаются непосредственно с объективным миром и с обществом в том смысле, как это обычно понимается. Они в значительной мере зависят от того конкретного языка, который стал их средой общения. Это совершенная иллюзия — полагать, что кто-то может согласовать себя с сущнос- тью реальности без использования языка и что язык — всего лишь случайное средство решения конкретных задач общения или мышления. Суть вопроса в том, что «реальный мир» в значительной степени неосознанно строится на языковых привычках группы людей... Мы видим, слышим и испытываем остальные ощущения так, как мы это делаем, в значительной степени потому, что языковые обычаи нашего общества предрасполагают к определенному выбору способа интерпретации. Эдвард Сапир (цитировано по [Whorf 1956J). Цитата подчеркивает тот факт, что язык, на котором мы говорим, непосред- ственно влияет на способ восприятия мира. Это справедливо не только для естественных языков, подобных тем, что изучались в начале двадцатого века 22_________________1- ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ МЫШЛЕНИЕ американскими лингвистами Эдвардом Сапиром и Ли Ворфом, но также и для искусственных языков, наподобие тех, что мы используем в програм- мировании. 1.2.1. Эскимосы и снег Примером, почти повсеместно цитируемым (хотя зачастую ошибочно — см. [Pillum 1991]) в качестве иллюстрации того, как язык влияет на мышление, является тот «факт», что в эскимосских (или юитских) языках имеется множе- ство слов для описания различных типов снежного покрова — мокрого, плот- ного, подмерзшего и т. д. Это-то как раз не является удивительным. Любое сообщество с общими интересами естественным образом разрабатывает специа- лизированный словарь необходимых понятий. Что действительно важно — не слишком абсолютизировать вывод, который мы можем сделать из этого простого наблюдения. Главное не в том, что глаз эскимосов в каком-то существенном аспекте отличается от моего собственного или что эскимосы могут видеть вещи, которые я не способен различать. С течением времени, с помощью тренировки, я бы стал ничуть не хуже различать разнообразные типы снежного покрова. Однако язык, на котором я говорю (а именно английский), не вынуждает меня заниматься этим, и тем самым указанные способности не являются для меня естественными. Таким образом, различные языки (например, эвенкийский) могут привести (но не обязательно требуют этого) к тому, чтобы смотреть на мир с разных сторон. Чтобы эффективно использовать ООП, требуется глядеть на мир иным спо- собом. Само по себе применение объектно-ориентированного языка программи- рования (такого, как C++) не вынуждает стать объектно-ориентированным программистом. Использование объектно-ориентированного языка упрощает разработку объектно-ориентированных приложений, но, как было остроумно замечено, «программа фортрановского типа может быть написана на любом языке». 1.2.2. Пример из области программирования Связь между языком и мышлением для естественных языков, о которой мы говорили, является еще более заметной для искусственных компьютерных языков. Язык программирования, в терминах которого разработчик думает о проблеме, вносит особые оттенки и, вообще говоря, изменяет даже сам алгоритм. Приведем пример, иллюстрирующий связь между компьютерным языком и способом решения задачи. Некоторое время назад один студент, работающий в области генетических исследований, столкнулся с необходимостью анализа последовательностей ДНК. Проблема могла быть сведена к относительно про- стой задаче. Молекула ДНК представляется в виде вектора из N целочислен- ных значений, где N очень велико (порядка десятков тысяч). Нужно было 1.2. ЯЗЫК И МЫШЛЕНИЕ_________________________________23 проверить, не является ли какой-либо участок длины М (М — фиксированная константа порядка 5-10) повторяющимся в последовательности ДНК. ACTCGGATCTTGCATTTCGGCAATTGGACCCTGACTTGGCCA... Программист, не долго думая, написал простую и прямолинейную программу на Fortran — нечто вроде DO 10 I =1, N-M DO 10 J = 1, N-M FOUND=.TRUE. DO 20 К = 1, M 20 IF (X(I+K-1).NE.X(J+K-1)) FOUND=.FALSE. IF (FOUND) ... 10 CONTINUE Он был неприятно разочарован, когда пробные запуски программы показали, что она потребует многих часов для завершения работы. Студент обсудил эту проблему со студенткой, которая оказалась профессионалом в программирова- нии на языке APL. Она сказала, что могла бы попробовать написать программу для решения этой задачи. Студент был в сомнении: Fortran известен как один из наиболее «эффективных» компилируемых языков, а APL реализовывался с помощью интерпретатора. Таким образом, тот факт, что APL-программист способен составить алгоритм, который требует для работы минуты, а не часы, был воспринят с определенной дозой недоверия. APL-программистка переформулировала задачу. Вместо того чтобы работать с вектором из N элементов, она представила данные в виде матрицы, имеющей приблизительно N строк и М столбцов: А С Т С G G позиции 1 — М С Т С G G А позиции 2 - М+1 Т С G G А Т позиции 3 - М+2 С G G А Т Т позиции 4 — М+3 G G А Т Т С позиции 5 - М+4 G А Т Т С Т позиции 6 — М+5 Т G G А С С G G А С С С Затем студентка отсортировала матрицу по строкам. Если какой-то фрагмент оказывается повторяющимся, то в отсортированной матрице две соседние стро- ки должны оказаться идентичными. Т G G А С С Т G G А С С Проверка этого условия оказывается тривиальной задачей. Причина, по кото- рой APL-программа оказалась быстрее, не имела ничего общего со скоростью работы APL по сравнению с Fortran. Главным было то, что программа на Fortran использовала алгоритм со сложностью 0(М х N2), в то время как алгоритм сортировки APL-программы требовал примерно 0(М х N log N) операций. Ключевой момент этой истории не в том, что APL является лучшим языком программирования, чем Fortran, но в том, что APL-программист естественным образом пришел к более удачному решению. В частности, из-за того, что на языке APL очень неудобно организовывать циклы, а сортировка является тривиальной операцией — ей соответствует встроенный оператор языка. Таким образом, раз уж сортировку можно столь легко использовать, хороший APL- программист всегда старается найти для нее новое применение. В этом смысле язык программирования, на котором записывается решение задачи, напрямую влияет на ход мыслей программиста, заставляя его рассматривать задачу под определенным углом. 1.2.3. Принцип Чёрча и гипотеза Ворфа Легко поверить в утверждение, что язык, на котором высказывается идея, направляет мышление. Однако есть более сильное утверждение, известное среди лингвистов как гипотеза Сапира-Ворфа. Она идет еще дальше, хотя и является спорной [Pullum 1991]. Гипотеза Сапира-Ворфа утверждает, что индивидуум, использующий некото- рый язык, в состоянии вообразить или придумать нечто, не могущее быть переведенным или даже понятым индивидуумами из другой языковой среды. Такое происходит, если в языке второго индивидуума нет эквивалентных слов и отсутствуют концепции или категории для идей, вовлеченных в рассматрива- емую мысль. Интересно сравнить данную идею с прямо противоположной концепцией в информатике — а именно принципом Чёрча. В 30-х годах у математиков пробудился большой интерес к различным фор- мализмам, которые могут быть использованы при вычислениях. Эти идеи получили развитие в 40-50-х годах, когда они привлекли внимание молодого сообщества специалистов по информатике. Примерами таких систем яв- ляются модели, предложенные Чёрчем [Church 1936], Постом [Post 1936], Марковым [Markov 1951], Тьюрингом [Turing 1936], Клини [Kleene 1936] и другими. В одно время приводилось множество аргументов, доказывающих, что каждая из этих систем может быть использована для моделирования остальных. Часто такие доводы были двухсторонними, показывая, что обе 1.2. ЯЗЫК И МЫШЛЕНИЕ__________________________________25 модели эквивалентны с некой общей точки зрения. Все это привело логику Алонзо Чёрча к гипотезе, которая теперь связана с его именем. Принцип Чёрча: Любое вычисление, для которого существует эффективная процедура, может быть реализовано на машине Тьюринга. По самой своей природе это утверждение недоказуемо, поскольку мы не имеем строгого определения термина «эффективная процедура». Тем не менее до сих пор не было найдено контрпримера, и убедительность очевид- ности, по-видимому, благоприятствует принятию этого утверждения*. Признание принципа Чёрча имеет важное и глубокое следствие для языков программирования. Машины Тьюринга являются изумительно простыми ме- ханизмами. От языка программирования требуется немного, чтобы смодели- ровать такое устройство. В 1960-х годах, к примеру, было показано, что машина Тьюринга может быть смоделирована на любом языке программиро- вания, в котором содержатся условные операторы и операторы цикла [Bohm 1966]. Этот не совсем правильно понимаемый результат был одним из основных доводов в защиту утверждения о том, что знаменитый оператор goto является ненужным. Если мы признаем принцип Чёрча, то любой язык, на котором можно смоделировать машину Тьюринга, является достаточно мощным, чтобы осуществить любой реализуемый алгоритм. Для решения проблемы надо построить машину Тьюринга, которая выдаст желаемый результат, — сог- ласно принципу Чёрча такая машина должна существовать для каждого алгоритма. Затем остается только смоделировать машину Тьюринга на вашем любимом языке программирования. Тем самым споры об относительной «мощности» языков программирования — если под мощностью мы понимаем «способность решать задачи», — оказываются бессмысленными. Позднее Алан Перлис ввел удачный термин для подобных аргументов, назвав их «тьюринговская пропасть», поскольку из них так сложно выбраться, в то время как сами они столь фундаментально бесполезны. ' Создание математического формализма вычислимости было связано с необходимостью определить понятие алгоритма. Пока исследования в этой области шли успешно, каждая новая формализованная последовательность вычислений получала имя «алгоритм» просто по определению. Когда же математики столкнулись с задачами, для которых пришлось доказывать отсутствие алгоритма, потребовалось формальное определение. В настоящий момент принято считать, что алгоритмом является последовательность действий, которая может быть сведена к программе, выполняемой с помощью машины Тьюринга. Или, в эквивалентной форме: последовательность действий, которая может быть сведена к программе для машины Поста, или конечного автомата Маркова, или же к последовательности рекурсивных функций Клини и Чёрча, является алгоритмом. Доказано, что все эти формальные системы вычислимости являются эквивалентными. Тем самым принцип Чёрча является аксиомой, не требующей доказательства, которая формализует понятие алгоритма («эффективной процедуры») и в силу статуса аксиомы опровергающего контрпримера иметь не может. — Примеч. перев. 26 1. ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ МЫШЛЕНИЕ Заметим, что принцип Чёрча является в определенном смысле точной проти- воположностью гипотезы Сапира-Ворфа. Принцип Чёрча утверждает, что по своей сути все языки программирования идентичны. Любая идея, которая выражается на одном языке, может (согласно теории) быть реализована на другом. Гипотеза же Сапира-Ворфа, как вы помните, утверждает, что сущест- вуют идеи, не согласующиеся с этим принципом. Многие лингвисты отвергают гипотезу Сапира-Ворфа и вместо этого прини- мают «тьюринговский эквивалент» для естественных языков: любая идея в принципе может быть выражена на любом языке. Например, несмотря на то что язык людей, живущих в жарком климате, не содержит готовых понятий для типов снежного покрова, в принципе южане тоже могут стать специалис- тами в области гляциологии. Аналогично объектно-ориентированная техника не снабжает (б теории) вас новой вычислительной мощностью, которая позволила бы решить проблемы, недоступные для других средств. Но объект- но-ориентированный подход делает задачу проще и приводит ее к более естественной форме. Это позволяет обращаться с проблемой таким образом, который благоприятствует управлению большими программными системами. Итак, как для компьютерных, так и для естественных языков справедливо: язык направляет мысли, но не предписывает их. 1.3. Новая парадигма Объектно-ориентированное программирование часто называют новой пара- дигмой программирования. Другие парадигмы программирования: дирек- тивное (языки типа Pascal или С), логическое (языки типа Prolog) и функциональное (языки типа Lisp, FP или Haskell) программирование. Интересно исследовать слово «парадигма». Следующий фрагмент взят из толкового словаря American Heritage Dictionary of the English Language: par-a-digm (сущ.) 1. Список всех вариантов окончаний слова, рассматривае- мый как иллюстративный пример того, к какому спряжению или склонению оно относится. 2. Любой пример или модель (от латинского paradigma и греческого paradeigma — модель, от paredeiknunai — сравнивать, выставлять). На первый взгляд, склонение и спряжение слов (например, латинских) имеет мало общего с компьютерными языками. Чтобы понять связь, мы должны заметить, что слово «парадигма» пришло в программирование из оказавшей большое влияние книги «Структура научных революций^, написанной истори- ком науки Томасом Куном [Kuhn 1970]. Кун использовал этот термин во втором значении, чтобы описывать набор теорий, стандартов и методов, кото- рые совместно представляют собой способ организации научного знания — иными словами, способ видения мира. Основное положение Куна состоит в том, что революции в науке происходят, когда старая парадигма пересматрива- ется, отвергается и заменяется новой. 1.4. СПОСОБ ВИДЕНИЯ МИРА_____________________________27 Именно в этом смысле — как модель или пример, а также как организующий подход — это слово использовал Роберт Флойд, лауреат премии Тьюринга 1979 года, в лекции «Парадигмы программирования» [Floyd 1979]. Парадигмы в программировании — это способ концептуализации, который определяет, как проводить вычисления и как работа, выполняемая компьютером, должна быть структурирована и организована. Хотя сердцевина объектно-ориентированного программирования — техника организации вычислений и данных является новой, ее зарождение можно отнести по крайней мере к временам Линнея (1707-1778), если не Платона. Парадоксально, но стиль решения задач, воплощенный в объектно-ориенти- рованной технике, нередко используется в повседневной жизни. Тем самым новички в информатике часто способны воспринять основные идеи объект- но-ориентированного программирования сравнительно легко, в то время как люди, более осведомленные в информатике, зачастую становятся в тупик из- за своих представлений. К примеру, Алан Кей обнаружил, что легче обучать языку Smalltalk детей, чем профессиональных программистов [Кау 1977]. При попытках понять, что же в точности имеется в виду под термином объектно-ориентированное программирование, полезно посмотреть на ООП с разных точек зрения. В нескольких следующих разделах кратко очерчиваются три разных аспекта объектно-ориентированного программирования. Каждый из них по-своему объясняет, чем замечательна эта идея. 1.4. Способ видения мира Чтобы проиллюстрировать некоторые основные идеи объектно-ориентиро- ванного программирования, рассмотрим ситуацию из обыденной жизни, а затем подумаем, как можно заставить компьютер наиболее близко смоде- лировать найденное решение. Предположим, я хочу послать цветы своей бабушке (которую зовут Элси) в ее день рождения. Она живет в городе, расположенном за много миль от меня, так что вариант, когда я сам срываю цветы и кладу их к ее порогу, не подлежит обсуждению. Тем не менее послать ей цветы — это достаточно простая задача: я иду в ближайший цветочный магазин, хозяйку которого (какое совпадение) зовут Фло (florist — цветочница), называю ей тип и количество цветов, которые бы я хотел послать моей бабушке, и (за прием- лемую цену) я могу быть уверен, что цветы будут доставлены в срок, по нужному адресу. 1.4.1. Агенты, обязанности, сообщения и методы Рискуя быть обвиненным в тавтологии, все-таки хочу подчеркнуть, что механизм, который я использовал для решения этой проблемы, состоял в поиске подходящего агента (а именно, Фло) и передаче ей сообщения, содержащего мой запрос. Обязанностью Фло является удовлетворение моего запроса. Имеется некоторый метод — то есть алгоритм, или последова- 28 ______________1- ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ МЫШЛЕНИЕ тельность операций, который используется Фло для выполнения запроса. Мне не надо знать, какой конкретный метод она использует для выполнения моего запроса, и в действительности зачастую я и не хочу это знать. Все дальнейшее обычно скрыто от моего взгляда. Однако если бы я исследовал этот вопрос, я, возможно, обнаружил бы, что Фло пошлет свое сообщение хозяину цветочного магазина в городе, где живет моя бабушка. Хозяин цветочного магазина в свою очередь примет необходимые меры и подготовит распоряжение (сообщение) для человека, ответственного за доставку цветов, и т. д. Тем самым мой запрос в конечном счете будет удовлетворен через последовательность запросов, пересылаемых от одного агента к другому. Итак, первым принципом объектно-ориентированного подхода к решению за- дач является способ задания действий. Действие в объектно-ориентированном программировании инициируется посред- ством передачи сообщений агенту (объекту), ответственному за действие. Сооб- щение содержит запрос на осуществление действия и сопровождается допол- нительной информацией (аргументами), необходимой для его выполнения. Получатель (receiver) — это агент, которому посылается сообщение. Если он принимает сообщение, то на него автоматически возлагается ответственность за выполнение указанного действия. В качестве реакции на сообщение получатель запустит некоторый метод, чтобы удовлетворить принятый запрос. Мы заметили, что существует важный принцип маскировки информации в отношении пересылки сообщений. А именно: клиенту, посылающему запрос, не требуется знать о фактических средствах, с помощью которых его запрос будет удовлетворен. Существует и другой принцип, также вполне человеческий, кото- рый мы видели в неявной форме при пересылке сообщений. Если имеется работа, которую нужно выполнить, то первая мысль клиента — найти кого- либо еще, кому можно было бы ее поручить. Такая вполне нормальная реакция почти полностью атрофировалась у программиста, имеющего боль- шой опыт в традиционном программировании. Ему трудно представить, что он (или она) не должен все полностью программировать сам, а может обратиться к услугам других. Важная часть объектно-ориентированного про- граммирования — разработка повторно используемых компонент, и первым шагом в этом направлении является желание попробовать этот путь. Скрытие информации является важным принципом и в традиционных язы- ках программирования. Тогда в чем пересылка сообщений отличается от обычного вызова пгюйед^цы,? В обоих случаях имеется последовательность точно определенных действий, которые будут инициированы в ответ на запрос. Однако имеются два существенных отличия. Первое из них состоит в том, что у сообщения имеется вполне конкретный получатель — агент, которому послано сообщение. При вызове процедуры нет столь явно выделенного получателя. (Хотя, конечно, мы можем принять согла- шение, согласно которому получателем сообщения является первый аргумент в вызове процедуры — примерно так и реализуются получатели сообщений). 1.4. СПОСОБ ВИДЕНИЯ МИРА ^•' " ' ___ 29 Второе отличие состоит в том, что интерпретация сообщения (а именно метод, вызываемый после приема сообщения) зависит от получателя и явля- ется различной для различных получателей. Я могу передать мое сообщение, к примеру, моей жене Бет, и она его поймет, и как результат действие будет выполнено (а именно цветы будут доставлены бабушке). Однако метод, который использует Бет для выполнения запроса (весьма вероятно, просто переадресовав его хозяйке цветочного магазина Фло), будет иным, чем тот, который применит Фло в ответ на тот же самый запрос. Если я попрошу о том же Кена, моего зубного врача, у него может не оказаться подходящего метода для решения поставленной задачи. Если предположить, что Кен вообще воспримет этот запрос, то он с большой вероятностью выдаст надле- жащее диагностическое сообщение об ошибке. Вернемся в нашем обсуждении на уровень компьютеров и программ. Раз- личие между вызовом процедуры и пересылкой сообщения состоит в том, что в последнем случае существует определенный получатель и интерпретация (то есть выбор подходящего метода, который запускается в ответ на сообще- ние) может быть различной для разных получателей. Обычно конкретный получатель неизвестен вплоть до выполнения программы, так что определить, какой метод будет вызван, заранее невозможно. В таком случае говорят, что имеет место позднее связывание между сообщением (именем процедуры или функции) и фрагментом кода (методом), используемым в ответ на сообщение. Эта ситуация противопоставляется раннему связыванию (на этапе компи- лирования или компоновки программы) имени с фрагментом кода, что проис- ходит при традиционных вызовах процедур. 1.4.2. Обязанности и ответственности Фундаментальной концепцией в объектно-ориентированном программировании является понятие обязанности или ответственности за выполнение действия. Мой запрос выражает только стремление получить желаемый результат (а именно доставить цветы бабушке). Хозяйка цветочного магазина свободна в выборе способа, который приведет к желаемому результату, и не испытывает препятствий с моей стороны в этом аспекте. Обсуждая проблему в терминах обязанностей, мы увс. i ичиваем уровень абстра- гирования. Это позволяет иметь большую независимость между агентами — критический фактод.пои решении сложных_задач. В главе 2 мы будем подроб- 'но исследовать, как можно использовать обязанности в разработке програм- много обеспечения. Полный набор обязанностей, связанных с определенным объектом, часто определяется с помощью термина протокол. Различие между взглядом на программное обеспечение со стороны традицион- ного, структурного подхода и объектно-ориентированной точкой зрения на него может быть выражено в форме пародии на хорошо известную цитату: Задавайтесь вопросом не о том, что вы можете сделать для своих структур \, данных, а о том, что структуры данных могут сделать для вас. 30_________________1- ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ МЫШЛЕНИЕ 1.4.3. Классы и экземпляры Хотя я имел дело с Фло лишь несколько раз, у меня имеется примерное представление о ее реакции на мой запрос. Я могу сделать определенные предположения, поскольку имею общую информацию о людях, занимающихся разведением цветов, и ожидаю, что Фло, будучи представителем этой катего- рии, в общих чертах будет соответствовать шаблону. Мы можем использовать термин Florist для описания категории (или класса) всех людей, занимающихся цветоводством, собрав в нее (категорию) все то общее, что им свойственно. Эта операция является вторым принципом объектно-ориентированного программи- рования: Все объекты являются представителями, или экземплярами, классов. Метод, акти- визируемый объектом в ответ на сообщение, определяется классом, к которому принадлежит получатель сообщения. Все объекты одного класса используют одни и те же методы в ответ на одинаковые сообщения. Проблема сообщества объектно-ориентированных программистов заключает- ся в распространенности различных терминов для обозначения сходных идей. Так, в языке Object Pascal класс называется «объектом» (тип данных object), а подклассы (которые вкратце будут описаны ниже) известны как роди- тельский класс, класс-предок и т. д. Словарь-глоссарий в конце этой книги поможет вам разобраться с нестандартными терминами. Мы будем использо- вать соглашение, общее для объектно-ориентированных языков программи- рования: всегда обозначать классы идентификаторами, начинающимися с заглавной буквы. Несмотря на свою распространенность, данное соглашение не является обязательным для большинства языков программирования. 1.4.4. Иерархии классов и наследование О Фло у меня имеется больше информации, чем содержится в категории Florist. Я знаю, что она разбирается в цветах и является владелицей магазина (shopkeeper). Я догадываюсь, что, вероятно, меня спросят о деньгах в процес- се обработки моего запроса и что после оплаты мне будет выдана квитанция. Все вышеперечисленное справедливо также для зеленщиков, киоскеров, про- давцов магазинов и т. д. Поскольку категория Florist является более узкой, чем Shopkeeper, то любое знание, которым я обладаю о категории Shopkeeper, справедливо также и для Florist, и, в частности, для Фло. Один из способов представить организацию моего знания о Фло — это иерархия категорий (рис. 1.1). Фло принадлежит к категории Florist; Florist является подкатегорией категории Shopkeeper. Далее, представитель Shopkee- per заведомо является человеком, то есть принадлежит к категории Human — тем самым я знаю, что Фло с большой вероятностью является двуногим существом. Далее, категория Human включена в категорию млекопитающих (Mammal), которые кормят своих детенышей молоком, а млекопитающие являются подкатегорией животных (Animal) и, следовательно, дышат кис- лородом. В свою очередь животные являются материальными объектами 1.4. СПОСОБ ВИДЕНИЯ МИРА 31 (Material Object) и в силу этого обладают массой. В результате многое из того, что я знаю о Фло, не является связанным непосредственно ни с ней, ни даже с категорией Florist. Принцип, в соответствии с которым знание о более общей категории разре- шается использовать для более узкой категории, называется наследованием. Мы будем говорить, что класс Florist наследует атрибуты класса (или кате- гории) Shopkeeper. Имеется альтернативный способ графического представления, иллюстрирую- щий подобное родство. Он особенно хорош, когда имеется много индиви- Material object (Материальные объекты) /^ Animal (Животные) /"—————————— Mammal (Млекопитающие) У———————— Human (Люди) /shopkeeper (Владельцы магазинов) 'Horist (Цветочницы)' Рис. 1.1. Категории, охватывающие объект «Фпо» 32 1. ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ МЫШЛЕНИЕ дуумов с различными линиями наследования. Классы представляются в виде иерархической древовидной структуры, в которой более абстрактные классы (такие, как Material Object или Animal) располагаются в корне дерева, а более специализированные классы и в конечном итоге индивидуумы располагаются на его конце, в ветвях. Рисунок 1.2 показывает такую иерархию классов для Фло. Эта же самая иерархия включает в себя мою жену Бет, собаку Флеш, Фила — утконоса, живущего в зоопарке, а также цветы, которые я послал своей бабушке. Material object (Материальные объекты) Anir (Живо ifial тные) /о1313 (Раст ения) /». мапг (Млекопи irnal тающие) Flo (Цвс wer эты) D (Соб одаки) Hum (Лю an PlatypuДИ) (УТКОНОС s;ы) Shopkeeper(Владельцы магазинов) А (Ху 0) return datastack [datatop — 1]; :'У, return 0; :,Д|:: } '" int pop() { .'; if (datatop > 0) : return datastack [--datatop] ; ,:,, return 0; } Область видимости для блоков Механизм видимости для блоков, использованный в языке Алгол и его преемниках (таких, как Pascal), предлагает чуть больший контроль над ви- димостью имен, чем просто различие между локальными и глобальными именами. Кажется, мы могли бы надеяться, что это решит проблему скрытия информации. К сожалению, проблема остается. В любой области, в которой разрешен доступ к именам четырех процедур, видны также и их общие данные. Чтобы решить эту дилемму, требуется разработать иной механизм структурирования. 40 1- ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ МЫШЛЕНИЕ begin var datastack : array [1..100] of integer; datatop : integer; procedure init; . . . procedure push(val : integer); . . function pop : integer- end; Модули В некотором смысле модули можно рассматривать просто как улучшенный метод создания и управления совокупностями имен и связанными с ними значениями. Наш пример со стеком является типичным в том аспекте, что имеется определенная информация (интерфейсные процедуры), которую мы хотим сделать широко и открыто используемой, в то время как доступ к некоторым данным (собственно данные стека) должен быть ограничен. Если рассматривать модуль как абстрактную концепцию, сведенную к своей простей- шей форме, то ее сутьсостоит в разбиении пространства имен на _две_части. Открытая (риЬИс}_часть_является доступнои^извн1ё^одуля^"^^ (private) часть доступна'только внутри модуля. Типы, данные (переменные) и процеду- ^ ____„- „-^--'••"^——"^.^.^.-И'»"^™"™^'"^"'^».-.^..-».^^,.*^ -..«•"•"•«^'-**1^«,.. ' л ' . ^ •/ ры могут быть отнесены к любой из двух частей. Дэвид Парнас [Pamas 1972] популяризовал понятие модулей. Он сформулиро- вал следующие два принципа их правильного использования: 1. Пользователя, который намеревается использовать модуль, следует снаб- дить всей информацией, необходимой, чтобы делать это корректно, и не более того. 2. Разработчика следует снабдить всей информацией, необходимой для со- здания модуля, и не более того. Эта философия в значительной мере напоминает военную доктрину «необ- ходимого знания»: если вам не нужно знать определенную информацию, вы и не должны иметь к ней доступа. Это явное, намеренное и целенаправ- ленное утаивание информации называется маскировкой информации (infor- mation hiding). Модули решают некоторые, но не все проблемы разработки программного обеспечения. Например, они позволяют нашему программисту скрыть детали реализации стека, но что делать, если другие пользователи захотят иметь два (или более) стека? В качестве более сложного примера предположим, что программист заявляет, что им разработан новый тип числовых объектов, названный Complex. Он определил арифметические операции для комплексных величин — сложение, вычитание, умножение и т. д. и ввел подпрограммы для преобразования 1.6. БАРЬЕР СЛОЖНОСТИ_________________________________41 обычных чисел в комплексные и обратно. Имеется лишь одна маленькая проблема: можно манипулировать только с одним комплексным числом. Комплексные числа вряд ли будут полезны при таком ограничении, но это именно та ситуация, в которой мы оказываемся в случае простых модулей. Последние, взятые сами по себе, обеспечивают эффективный механизм маски- ровки информации, но они не позволяют осуществлять размножение экземпля- ров, под которым MU'noHiiMaeM'BO^MO^HOCTb сделать"много"копйй областей данных. Чтобы справиться с проблемой размножения, специалистам по инфор- матике потребовалось разработать новую концепцию. Абстрактные типы данных Абстрактный тип данных задается программистом. С данными абстрактного типа можно манипулировать так же, как и с данными типов, встроенных в систему. Как и последним, абстрактному типу данных соответствует набор (возможно, бесконечный) допустимых значений и ряд элементарных операций, которые могут быть выполнены над данными. Пользователю разрешается создавать переменные, которые принимают значения из допустимого мно- жества, и манипулировать ими, используя имеющиеся операции. К примеру, наш бесстрашный программист может определить свой стек как абстрактный тип данных и стековые операции как единственные действия, которые до- пускается производить над отдельными экземплярами стеков. Модули часто используются при реализации абстрактных типов данных. Непосредственной логической взаимосвязи между понятиями модуля и абст- рактного типа данных нет. Эти две идеи близки, но не идентичны. Чтобы построить абстрактный тип данных, мы должны уметь: 1. Экспортировать определение типа данных. 2. Делать доступным набор операций, использующихся для манипулирова- ния экземплярами типа данных. 3. Защищать данные, связанные с типом данных, чтобы с ними можно было работать только через указанные подпрограммы. 4. Создавать несколько экземпляров абстрактного типа данных. В нашем определении модули служат только как механизм маскировки инфор- мации и тем самым непосредственно связаны только со свойствами 2 и 3 из нашего списка. Остальные свойства в принципе могут быть реализованы с использованием соответствующей техники программирования. Пакеты, кото- рые встречаются в таких языках программирования, как CLU или Ada, тесно связаны с перечисленными выше требуемыми свойствами абстрактных типов данных. В определенном смысле объект — это просто абстрактный тип данных. Говори- ли, к примеру, что программисты на языке Smalltalk пишут наиболее «структу- рированные» программы, потому что они не имеют возможности написать что- либо кроме определений абстрактных типов данных. Истинная правда, что 42 ________J. ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ МЫШЛЕНИЕ объект является абстрактным типом данных, но понятия объектно-ориенти- рованного программирования, хотя и строятся на идеях абстрактных типов данных, добавляют к ним важные новшества по части разделения__^ совместного^спользованйя программного кода. Объекты: сообщения, наследование и полиморфизм Объектно-ориентированное программирование добавляет несколько новых важ- ных идей к концепции абстрактных типов данных. Главная из них — пересылка сообщений. Действие инициируется по запросу, обращенному к конкретному объекту, а нечерез„вь1зрв_.фхнк11ии. В значительной степени это просто смеще- ние ударения: традиционная точка зрения делает основной упор на операции, в то время как ООП на первое место ставит собственно значение. (Вызываете ли вы подпрограмму push со стеком и значением в качестве аргументов, или же вы просите объект stack поместить нужное значение к нему внутрь?) Если бы это было все, что имеется в объектно-ориентированном программировании, эта техника не рассматривалась бы как принципиальное нововведение. Но к пере- сылке сообщений добавляются мощные механизмы переопределения имен и совместного/многократного использования программного кода. Неявной в идее пересылки сообщений является мысль о том, что интерпре- тация сообщения' может меняться для различных объектов. А именно пове- дение и реакция, инициируемые сообщением, зависят от объекта, который получает сообщение. Тем самым push может означать одно действие для стека и совсем другое для блока управления механической рукой. Поскольку_имена операций не обязаны быть уникальными, могут использоваться простые и явные формы команд. Это приводит к более читаемому и понятному коду. Наконец, объектно-ориентированное программирование добавляет механиз- мы наследования и полиморфизма. Наследование позволяет различным типам данных совместно использовать один и тот же код, приводя к уменьшению его размера и повышению функциональности. Полиморфизм перекраивает этот общий код так, чтобы удовлетворить конкретным особенностям отдель- ных _типов данных. Упор на независимость индивидуальных компонент позволяет использовать процесс пошаговой сборки, при которой отдельные блоки программного обеспечения разрабатываются, программируются и отла- живаются до того, как они объединяются в большую систему. Все эти идеи будут описаны более подробно в последующих главах. 1.7. Многократно используемое программное обеспечение Десятилетиями люди спрашивали себя, почему создание программного обеспе- чения не может копировать процесс конструирования материальных объектов К примеру, когда мы строим здание, автомобиль или электронное устройство мы обычно соединяем вместе несколько готовых компонент вместо того 1.8. РЕЗЮМЕ ______________________________ 43 чтобы изготовлять каждый новый элемент с нуля. Можно ли конструировать программное обеспечение таким же образом? Многократное использование программного обеспечения — цель, к которой постоянно стремятся и редко достигают. Основная причина этого — значитель- ная взаимозависимость большей части программного обеспечения, созданного традиционными способами. Как мы видели в предыдущих разделах, трудно извлечь из проекта фрагменты программного обеспечения, которые бы легко использовались в не имеющем к нему отношения новом программном продукте (каждая часть кода обычно связана с остальными фрагментами). Эти взаимоза- висимости могут быть результатом определения структуры данных или след- ствием особенностей функционирования. Например, организация записей в виде таблицы и осуществление операции ее индексированного просмотра являются обычным и в программировании. Тем не менее до сих пор подпрограммы поиска в таблицах зачастую пишутся «с нуля» для каждого нового приложения. Почему? Потому что в привычных языках программирования формат записи для элементов таблицы жестко свя- зан с более общим кодом для вставки и просмотра. Трудно написать код, который бы работал для произвольной структуры данных и любого типа записей. Объектно-ориентированное программирование обеспечивает механизм для от- деления существенной информации (занесение и получение записей) от специ- ализированной (конкретный формат записей). Тем самым при использовании объектно-ориентированной техники мы можем создавать большие программные компоненты, пригодные для повторного использования. Многие коммерческие пакеты программных компонентов, пригодных для многократного использова- ния, уже имеются, и разработка повторно используемых программных компо- нентов становится быстро развивающейся отраслью индустрии программного обеспечения. 1.8. Резюме Объектно-ориентированное программирование — это не просто несколько но- вых свойств, добавленных в уже существующие языки. Скорее — это новый шаг в осмыслении процессов декомпозиции задач и разработки программного обеспечения. ООП рассматривает программы как совокупность свободно (гибко) связанных между собой агентов, называемых объектами. Каждый из них отвечает за конкретные задачи. Вычисление осуществляется посредством взаимодействия объектов. Следовательно, в определенном смысле программирование — это ни много ни мало, как моделирование мира. Объект получается в результате инкапсуляции состояния (данных) и поведения (операций). Тем самым объект во многих отношениях аналогичен модулю или абстрактному типу данных. 44_________________1. ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ МЫШЛЕНИЕ Поведение объекта диктуется его классом. Каждый объект является экземп- ляром некоторого класса. Все экземпляры одного класса будут вести себя одинаковым образом (то есть вызывать те же методы) в ответ на одинаковые запросы. Объект проявляет свое поведение путем вызова метода в ответ на сообщение. Интерпретация сообщения (то есть конкретный используемый метод) зависит от объекта и может быть различной для различных классов объектов. Объекты и классы расширяют понятие абстрактного типа данных путем вве- дения наследования. Классы могут быть организованы в виде иерархического дерева наследования. Данные и поведение, связанные с классами, которые расположены выше в иерархическом дереве, доступны для нижележащих классов. Происходит наследование поведения от родительских классов. С помощью уменьшения взаимозависимости между компонентами програм- много обеспечения ООП позволяет разрабатывать системы, пригодные для многократного использования. Такие компоненты могут быть созданы и отла- жены как независимые программные единицы, в изоляции от других частей прикладной программы. Многократно используемые программные компоненты позволяют разработчику иметь дело с проблемами на более высокой ступени абстрагирования. Мы можем определять и манипулировать объектами просто в терминах сообщений, которые они распознают, и работы, которую они выполняют, игнорируя детали реализации. Что читать дальше Я отметил ранее, что Алан Кей считается отцом объектно-ориентированного программирования. Подобно многим простым высказываниям, данное утверж- дение выдерживает критику лишь отчасти. Сам Кей [Кау 1993] считает, что его вклад состоит преимущественно в разработке языка Smalltalk на основе более раннего_языка программийования..51п1и1а, созданного в Скандинавии в 60:д,^аздах_Ша/г/ 1966, Kirkerud 1989]. История свидетельствует, что боль- шинство_придцш1ов,йъектно-ориентйрован^ программирования было пол- ностью гтзработано создателями языка Simula, но этот факт в значительной 'степени игнорировался профессионалами"до'тёх пор, пока они (принципы) не были вновь открыты Кеем при разработке языка программирования Smalltalk. Пользующийся широкой популярностью журнал Byte в 1981 году сделал мно- гое для популяризации концепций, разработанных Кеем и его командой из группы Xerox PARC. Термин «кризис программного обеспечения», по-видимому, был изобретен Дутом Мак-Илроем во время конференции НАТО 1968 года по программ- ным технологиям. Забавно, что мы находимся в этом кризисе и сейчас, по прошествии половины срока существования информатики как независимой дисциплины. Несмотря на окончание холодной войны, выход из кризиса программного обеспечения не ближе к нам, чем это было в 1968 году — ЧТО ЧИТАТЬ ДАЛЬШЕ ______________________________45 см., к примеру, статью Гиббса «Хронический кризис программного обеспе- чения» в сентябрьском выпуске Scientific American за 1994 год [Gibbs 1994]. До некоторой степени кризис программного обеспечения — в значительной мере иллюзия. Например, задачи, рассматривавшиеся как чрезвычайно слож- ные пять лет назад, редко считаются таковыми сегодня. Проблемы, которые мы желаем решить сейчас, ранее считались непреодолимыми — по-видимому, это показывает, что разработка программного обеспечения год от года про- грессирует. Цитата американского лингвиста Эдварда Сапира (стр. 21) взята из статьи «Связь поведения и мышления с языком», перепечатанной в сборнике «Мыш- ление и реальность» [Whorf 1956]. В нем содержится несколько интересных работ по связям между языком и процессом мышления. Я настоятельно рекомендую каждому серьезному студенту, занимающемуся компьютерными языками, прочитать эти статьи. Некоторые из них имеют удивительно близкое отношение к искусственным языкам. Другая интересная книга — это «Эффект алфавита» Роберта Логана [Logan 1986], которая объясняет в лингвистических терминах, почему логика и наука были разработаны на Западе, в то время как в течение веков Китай имел опережающую технологию. В более современном исследовании о влиянии есте- ственного языка на информатику Дж. Маршалл Унгер [Unger 1987] описывает влияние японского языка на известный проект Пятого поколения компьюте- ров. Всеми признанное наблюдение, что язык эскимосов имеет много слов для обозначения типов снега, было развенчано Джоффри Паллумом в его сборнике статей по лингвистике [Pullum 1991]. В статье в Atlantic Monthly «Похвала снегу» (январь 1995) Каллен Мерфи указывал, что набор слов, используемый для обсуждения «снежной» тематики людьми, говорящими по-английски, по крайней мере столь же разнообразен, как и термины эскимосов. При этом, естественно, имеются в виду люди, для которых различия в типах снега существенны (преимущественно это ученые, которые проводят исследования в данной области). В любом случае данное обстоятельство не имеет значения для нашего обсужде- ния. Определенно истинно, что группы индивидуумов с общими интересами стремятся разработать свой собственный специализированный словарь и, будучи однажды созданным, он имеет тенденцию направлять мысли своих творцов по пути, который не является естественным для людей за пределами группы. Именно такова ситуация с ООП. Хотя объектно-ориентированные идеи могут, при надлежащей дисциплине, быть использованы и без объектно- ориентированных языков, использование их терминов помогает направить ум программиста по пути, который не очевиден без терминологии ООП. Мой рассказ является слегка неточным в отношении принципа Чёрча и машин Тьюринга. Чёрч фактически делал свое утверждение относительно рекурсивных функций [Church 1936], которые впоследствии оказались экви- валентными вычислениям, проводимым с помощью машин Тьюринга 46_________________1- ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ МЫШЛЕНИЕ {Turing 1936]. В той форме, в которой мы его формулируем здесь, этот принцип был описан Клини, и им же было дано то название, под которым принцип теперь известен. Роджерс приводит хорошую сводку аргументов в защиту эквивалентности различных моделей вычислений [Rogers 1967]. Если вы помните, именно шведский ботаник Карл Линней разработал идеи родов, видов и т. д. Это является прототипом схемы иерархической органи- зации, иллюстрирующей наследование, поскольку абстрактная классификация описывает характеристики, свойственные всем классификациям. Большинство иерархий наследования следуют модели Линнея. Критика процедур как методики абстрагирования (поскольку они не способны обеспечить надлежащий механизм маскировки данных) была впервые проведе- на Вилльямом Вульфом и Мери Шоу [Wulf 1973] при анализе многочисленных, проблем, CB^3aHHbix^CJicnoAb30BaHHeMJaQ6.aAbHbrx_nepeMeH^^ ция была впоследствии расширена Дэвидом Хансоном [Hanson 1981]. Подобно многим словам, которые нашли себе место в общепринятом жаргоне, термин «объектно-ориентированный^ используется гораздо шире своего фак- тического значения. Тем самым на вопрос: «Что такое объектно-ориен- тированное программирование?» очень непросто ответить. Бьорн Страуструп [Stroustrup 1988] не без юмора заметил, что большинство аргументов сводится к следующему силлогизму: • Х — это хорошо. • Объектная ориентированность — это хорошо. • Следовательно, Х является объектно-ориентированным. Роджер Кинг аргументированно настаивал, что его кот является объектно- ориентированным. Кроме прочих своих достоинств, кот демонстрирует ха- рактерное поведение, реагирует на сообщения, наделен унаследованными реакциями и управляет своим вполне независимым внутренним состоянием. Многие авторы пытались дать строгое определение тех свойств языка про- граммирования, которыми он должен обладать, чтобы называться объектно- ориентированным, — см., к примеру, анализ, проведенный Джозефиной Микалеф [Micallef 1988] или Питером Вегнером [Wegner 1986]. Вегнер, к примеру, различает языки, основанные на объектах, которые поддер- живают только абстрагирование (такие, как Ada), и объектно-ориентированные языки, которые поддерживают наследование. Другие авторы — среди них наиболее заметен Брэд Кокс [Сох 1990] — опреде- ляют термин ООП значительно шире. Согласно Коксу объектно-ориентирован- ное программирование представляет собой метод или цель (objective) програм- мирования путем сборки приложений из уже имеющихся компонент, а не конкретную технологию, которую мы можем использовать, чтобы достичь этой цели. Вместо выпячивания различий между подходами мы должны объединить воедино любые средства, которые оказываются многообещающими на пути к новой Индустриальной Революции в программировании. Книга Кокса по УПРАЖНЕНИЯ_________________________________________47 ООП [Сод- 1986], хотя и написана на заре развития объектно-ориентиро- ванного программирования, и в силу этого отчасти устаревшая в отношении деталей, тем не менее является одним из наиболее читаемых манифестов объектно-ориентированного движения. Упражнения 1. В объектно-ориентированной иерархии наследования каждый сле- дующий уровень является более специализированной формой пре- дыдущего. Приведите пример иерархии из повседневной жизни с этим свойством. Некоторые из иерархий, обнаруживаемые в реаль- ной жизни, не являются иерархиями наследования. Укажите при- мер иерархии без свойства наследования. 2. Посмотрите значение слова парадигма по крайней мере в трех словарях. Соотнесите эти определения с языками программирова- ния. 3. Возьмите задачу из реального мира (аналогичную пересылке цве- тов, рассмотренной ранее) и опишите ее решение в терминах агентов (объектов) и обязанностей. 4. Если вы знакомы с двумя (или более) различными языками про- граммирования, приведите пример, когда один язык направляет мысль программиста к определенному решению, а другой — сти- мулирует альтернативное решение. 5. Если вы знакомы с двумя (или более) естественными языками, опишите ситуацию, когда один язык направляет говорящего в одном направлении, в то время как другой язык приводит к иному ходу мысли. 6. Приведите аргументы за и против той точки зрения, что работа с компьютером в своей основе является моделированием. Возможно, вам захочется прочесть статью Алана Кея в Scientific American [Кау 1977]. ПРИЛОЖЕНИЕ исходный код ПРОГРАММ ДЛЯ ЗАДАЧИ «ВОСЕМЬ ФЕРЗЕЙ» В этом приложении приведены готовые программы для задачи о восьми ферзях, описанной в главе 5. Л. 1. ^Задача о восьми ферзях» на языке Apple Object Pascal с ^ Задача «Восемь ферзей», язык Object Pascal ill sal Автор: Тимоти Бадд, университет штата Орегон, 1996 ||g ") Program EightQueen; type Queen = object f* поля данных *) row : integer; column : integer; neighbor : Queen; (* инициализация *) procedure initialize (col : integer; ngh : Queen) ; ('операции *) function canAttack (testRow, testColumn : integer) : boolean; function findSolution : boolean; function advance : boolean; procedure print; end; var neighbor, lastQueen : Queen; i : integer; А.2. «ЗАДАЧА О ВОСЬМИ ФЕРЗЯХ» НА ЯЗЫКЕ C++______________393 else begin (* дальше нельзя *} (* передвинуть соседа, чтобы получить следующее решение *) if neighbor <> nil then if not neighbor.advance then advance := false else begin (* начать снова с горизонтали 1 *) row := 1; advance := self.findSolution; end; end; end; procedure Queen.print; begin if neighbor <> nil then neighbor.print; writeln('row ', row, ' column ', columns- end; begin neighbor := nil; for i := 1 to 8 do begin (* создать и инициализировать нового ферзя *} new (lastqueen); lastQueen.initialize (i, neighbor); if not lastQueen. findSolution then writeln('no solution'); (* самый новый ферзь является следующим соседом *} neighbor := lastQueen; end; lastQueen.print; for i := 1 to 8 do begin neighbor := lastQueen.neighbor; dispose (lastQueen); lastQueen := neighbor; end; end. Л.2. tdefine bool int #define true 1 A.3. «ЗАДАЧА О ВОСЬМИ ФЕРЗЯХ» НА ЯЗЫКЕ JAVA______________395 { if (row < 8) ( row++; return findSolution(); } if (neighbor && ! neighbor->advance()) return false; . . row =1; return findSolution (); } void queen::print() { if (neighbor) neighbor->print() ; cout « "column " « column « " row " « row « '\n'; } void main() ( queen * lastQueen =0; for (int i = 1; i <= 8; i++) { lastQueen = new queen(i, lastQueen); if (! lastQueen->findSolution()) ' cout « "no solution\n"; } lastQueen->print() ; } A3. ^Задача о восьми ферзях^ на языке Java /* Задача «Восемь ферзей», язык Java Автор: Тимоти Бадд, университет штата Орегон, январь 1996 */ import java.awt.*; import java.applet.*; class Queen { // поля данных private int row; private int column; private Queen neighbor; // конструктор Queen (int c. Queen n) { / / инициализировать поля данных A3. «ЗАДАЧА О ВОСЬМИ ФЕРЗЯХ» НА ЯЗЫКЕ JAVA______________397 g.drawLine(x+5, у+45, х+45, у+45); g.drawLine(х+5, у+45, х+5, у+5); g.drawLine(х+45, у+45, х+45, у+5); g.drawLine(х+5, у+35, х+45, у+35); g.drawLine(х+5, у+5, х+15, у+20); g.drawLine(x+15, у+20, х+25, у+5); g.drawLine(х+25, у+5, х+35, у+20); g.drawLine(х+35, у+20, х+45, у+5); g.drawOval(х+20, у+20, 10, 10); } ) public class QueenSolver extends Applet ( private Queen lastQueen; public void init() { int i; lastQueen = null; for (i = 1; i <= 8; i++) ( lastQueen = new Queen(i, lastQueen); lastQueen.findSolution() ; } } public void paint(Graphics g) ( / / нарисовать доску for (int i = 0; i <= 8; i++) { g.drawLine(50 * i, 0, 50*i, 400); g.drawLine(0, 50 * i, 400, 50*i); } / / нарисовать ферзей lastQueen.paint(g) ; } public boolean mouseDown(Java.awt.Event evt, int x, int y) { lastQueen.advance(); repaint () ; return true; } ) A.3.1. HTML-файл для апплета Java Eight-Queen Puzzle Головоломка «8 Ферзей» на языке Java А 4. «ЗАДАЧА О ВОСЬМИ ФЕРЗЯХ» НА ЯЗЫКЕ OBJECTIVE-C_________399 / * нельзя атаковать * 1 return 0; } Send ©interface Queen : Object { /* поля данных */ int row; int column; id neighbor; } / * методы * I - (void) initialize: (int) с neighbor: ngh; - (int) advance; - (void) print; - (int) canAttack: (int) testRow column: (int) testColumn; - (int) findSolution; Send gimplementation Queen : Object - (void) initialize: (int) с neighbor: ngh; ( /* задать постоянные значения */ column = с; neighbor = ngh; row =1; } - (int) advance ( / * сначала попробовать следующую горизонталь * / if (row < 8) ( row = row +1; return [ self findSolution ] ; } / * дальше двигаться нельзя, подвинем соседа * I if ( ! [ neighbor advance ] ) return 0; / * начать снова с первой горизонтали * 1 row = 1; return [ self findSolution ] ; } - (void) print { if (neighbor) [ neighbor print ] ; print("column %d row %d\n", column, row); } А.5. «ЗАДАЧА О ВОСЬМИ ФЕРЗЯХ» НА ЯЗЫКЕ SMALLTALK__________401 cqnAttack: row column: column t false result T List new Класс Queen имеет три переменные экземпляра: row, column, neighbor. Опре- делены следующие методы класса: setColumn: aNumber neighbor: aQueen " инициализировать поля данных " column := aNumber. neighbor := aQueen. " найти первое решение " row := 1. canAttack: testRow column: testColumn | columnDifference I columnDifference := testColumn — column. (((row = testRow) or: [ row + columnDifference = testRow]) or: [ row — columnDifference = testRow]) ifTrue: [ t true ] . T neighbor canAttack: testRow column: testColumn advance " сначала испытать следующую строку " (row < 8) ifTrue: [ row := row + 1. t self findSolution ]. " нельзя двигаться дальше, подвинем соседа " (neighbor advance ) if False: [ t false ]. row := 1. T self findSolution findSolution [ neighbor canAttack: row column: column ] whileTrue: [ self advance if False: [ t false ] ] . T true result tneighbor result; addLast: row Чтобы найти решение, выполняется следующий метод: run I lastQueen | lastQueen <- SentinelQueen new. 1 to: 8 do: [:i S lastQueen <- (Queen new) setColumn: i neighbor: lastQueen. lastQueen findSolution ] . 'результат получен" print. lastQueen result do: [:x | x print. ' ' print ]. Char newline print. Б. 1. ВЕРСИЯ БЕЗ ИСПОЛЬЗОВАНИЯ НАСЛЕДОВАНИЯ_____________403 (* вернуть координаты хиу центра шара *) function x : integer; function у : integer; end; Wall = object (* поля данных *) link : Wall; region : Rect; (* нечто вроде коэффициента отражения шаров при ударе *) convertFactor : real; (* процедура инициализации *} procedure initialize (left, top, right, bottom : integer; cf : real); (* рисуем стенку *) procedure draw; (* известить стенку, что в нее попал шар *) procedure hitBy(aBall : Ball); end; Hole = object (* поля денных *) link ; Hole; region : Rect; (* инициализировать положение лузы *) procedure initialize(x,у : integer); (* нарисовать лузу *) procedure draw; (* известить лузу, что в нее попал шар *) procedure hitBy(aBall : Ball); end; var cueBall : Ball; saveRack : integer; ballMoved : boolean; listOfHoles : Hole; listOfWalls : Wall; listOfBalls : Ball; theWindow : windowPtr; procedure Wall.initialize (left, top, right, bottom : integer; cf : real); begin (* инициализировать коэффициент отражения *) convertFactor := cf; (* задать прямоугольник для стенки *) SetRect(region, left, top, right, bottom); end; Б. 1. ВЕРСИЯ БЕЗ ИСПОЛЬЗОВАНИЯ НАСЛЕДОВАНИЯ_____________405 procedure Ball.initialize(x, у : integer); begin setCenter(x,у) ; setDirection(0,0) ; energy := 0.0; end; procedure Ball.setDirection(newDirection : real); begin direction := newDirection; end; procedure Ball.erase; begin EraseRect(region); end; procedure Ball.draw; begin if self = cueBall then (* нарисовать окружность *) FrameOval(region) else (* нарисовать закрашенный круг *} PaintOval(region); end; procedure Ball.update; var hptr : Hole; wptr : Wall; bptr : Ball; dx, dy : integer; thelntersection : Rect; i : integer; begin if energy > 0.5 then begin ballMoved := true; (* удалить шар с экрана *) erase; (* уменьшить энергию *} energy := energy — 0.05; (* передвинуть шар *) dx := trunc(5.0*cos(direction)); dy := trunc(5.0*sin(direction) ); offsetRect(region, dx, dy); (* перерисовать шар *) for i := 1 to 25 do draw; (* проверить, не попали ли мы в лузу *) hptr := listOfHoles; while (hptr <> nil) do Б.1. ВЕРСИЯ БЕЗ ИСПОЛЬЗОВАНИЯ НАСЛЕДОВАНИЯ____________407 begin if (abs(dx) < 0.05) then па := PI/2 else па := arctan (abs)dy/dx)); if (dx < 0) then na := PI — na; if (dy < 0) then na := -na; hitAngle := na; end; procedure Ball.hitBy(aBall : Ball); var da : real; begin (* уменьшить энергию ударяющегося шара вдвое *} aBall.energy := aBall.energy / 2.0; (* и прибавить ев к собственной *) energy := energy + aBali.energy; (* задать новое направление *) direction := hitAngle(self.x — aBall.x, self.у — aBall.у); (* направить ударившийся шар *) da := aBall.direction — direction; aBall.setDirection(aBall.direction + da); (* продолжить обновление состояния *} update; end; procedure mouseButtonDown(x, у : integer); var bptr : Ball; begin (* придать белому шару некоторую начальную энергию *) cueBall.energy := 20.0; (* и направление *) cueBall.setDirection( hitAngle(cueBali.x — x, cueBall.у — у)); (* цикл до тех пор, пока есть что-то движущееся *) ballMoved := true; while ballMoved do begin ballMoved := false; bptr := listOfBalls; while bptr <> nil do begin bptr.update; bptr := bptr.link; end; end; end; Б. 1. ВЕРСИЯ БЕЗ ИСПОЛЬЗОВАНИЯ НАСЛЕДОВАНИЯ_____________409 listOfBalls := cueBall; for i := 1 to 5 do begin for j := 1 to i do begin new (newBall); newBall.initialize(190 + i*8, 100 + 16*j - 8*i) ; newBail.link := listOfBalls/ listOfBalis := newBall; end; end; end; procedure drawBoard; var aWall : Wall; aBall : Ball; aHole : Hole; begin SetPort(theWindow) ; aWall := listOfWalls; while (aWall <> nil) do begin aWall.draw; aWall := aWall.link; end; aHole := listOfHoles; while (aHole о nil) do begin aHole.draw; aHole •.= aHole.link; end; aBall := listOfBalls; while (aBall <> nil) do begin aBall.draw; aBall := aBall. link; end; cueBall.draw; end; procedure createWindow; var name : STR255; winType : integer; windowRect : Rect; begin name := 'billiard game'; SetRect(windowRect, 50, 70, 500, 400); Б. 2. ВЕРСИЯ С ИСПОЛЬЗОВАНИЕМ НАСЛЕДОВАНИЯ 411 Автор: Тимоти Бадд, университет штата Орегон, сентябрь 1995 *) Program billiards; USES Windows; type GraphicalObject = object (* поля данных *) link : GraphicalObject; region : rect; (* процедура инициализации *) procedure setRegion(left, top, right, bottom : integer); (* операции с графическими объектами *} procedure draw; procedure erase; procedure update; function intersect(anObj : GraphicalObject) : boolean; procedure hitBy(aBall : GraphicalObject); end; Ball = object (GraphicalObject) (* данные, которые содержатся в объектах-шарах *) direction : real; energy : real; (* процедура инициализации *) procedure initialize(x, у : integer); (* методы общего характера *) procedure draw; override; procedure erase; override; procedure update; override; procedure hitBy(aBall : GraphicalObject); override; procedure setCenter(newx, newy : integers- procedure setDirection(newDirection : real); (* вернуть координаты х и у центра шара *) function x : integer; function у : integer; end; CueBall = object (Ball) (* изменяется только подпрограмма рисования *) procedure draw; overrider- end; Wall = object (GraphicalObject) (* коэффициент отражения ударяющихся шаров *) convertFactor : real; (* процедура инициализации *) procedure initialize (left, top, right, bottom : integer; cf : real); Б.2. ВЕРСИЯ С ИСПОЛЬЗОВАНИЕМ НАСЛЕДОВАНИЯ_____________413 procedure Wall .hit.By (anObj : GraphicalObject); var aBall : Ball; begin if Member(anObj, Ball) then begin aBall := Ball(anObj); (* отразить шар от стенки *) aBall.setDirection(convertFactor — aBall.direction) ; end; end; procedure Hole.hitBy(anObj : GraphicalObject); var aBall : Ball; begin if Member(anObj, Ball) then begin aBall := Ball(anObj); (* забрать энергию шара *) aBall.energy := 0.0; aBall.erase; (* передвинуть шар *) if aBall = cueBall then aEall.setCenter(50, 100) else begin saveRack := saveRack + 1,- asBall.setCenter(10 + saveRack*15, 250); end; (* перерисовать шар заново *) aBall.draw; end; end; procedure Ball.update; var gptr : GraphicalObject; dx, dy : integer; i : integer; begin if energy > 0.5 then begin ballMoved := true; (* удалить шар с экрана *} erase; (* уменьшить энергию *) energy := energy — 0.05; (* передвинуть шар *) dx := trunc(5.0*cos(direction)); Б. 2. ВЕРСИЯ С ИСПОЛЬЗОВАНИЕМ НАСЛЕДОВАНИЯ___________415 (* нарисовать закрашенный круг *) PaintOval(region) ; end; procedure mouseButtonDown(x, у : integer); var gptr : GraphicalObject; begin (* придать белому шару некоторую начальную энергию *) cueBall.energy := 20.0; (* и направление *) cueBall.setDirection( hitAngle(cueBall.x — x, cueBall.у — ; (* цикл до тех пор, пока что-либо движется *) ballMoved := true; while ballMoved do begin ballMoved := false; gptr := listOfObjects; while bptr <> nil do begin gptr.update; gptr := gptr.link; end; end; end; procedure CreateGlobals; var i, j : integer; newBall : Bail; newWall : Wall; newHole : Hole; begin saveRack := 0; listOfObjects := nil; (* создать стенки *) new (newWall); newWall.initialize(10, 10, 300, 15, 0.0); newWall.link := listOfObjects; listOfObjects := newWall; new (newWali) ; newWall.initialize(10, 200, 300, 205, 0.0); newWall.link := listOfObjects; listOfObjects := newWall; new (newWall) ; newWall.initialize(10, 10, 15, 200, 3.14159265359); newWall.link := listOfObjects; listOfObjects := newWall; new (newWall); newWall.initialize (300, 10, 305, 205, 3.14159265359); ПРИПОЖЕНИ ИСХОДНЫЙ КОД ПРОГРАММ ДЛЯ КАРТОЧНОГО ПАСЬЯНСА Программа карточного пасьянса из главы 8 написана на языке Java со стандартной библиотекой API. В. 1. HTML-файл для апплета И Solitare Game Д ЦЦ |l|| Щ B.2. Файл Solitarejava /* • Карточный пасьянс на языке Java Автор: Тимоти Бадд, университет штата Орегон, 1996 */ import java.awt.*; import java.applet.*; class Card ( // конструктор Card (int sv, int rv) ( s = sv; r •- rv; faceup = false; } // доступ к атрибутам карты public int rank () { return r; } public int suit() { return s; 3.2. ФАЙЛ SOUTARE JAVA_______________________________419 g.drawLine(x+25, y+20, x+40, y+40); g.drawLine(x+40, y+40, x+25, y+60); g.drawLine(x+25, y+60, x+10, y+40); g.drawLine(x+10, y+40, x+25, y+20) ; } else if (suit() == club) { g.drawOval(x+20, y+25, 10, 10); g.drawOvy!(x+25, y+35, 10, 10); g.drawOval(x+15, y+35, 10, 10); g.drawLine(x+23, y+45, x+20, y+55); g.drawLine(x+20, y+55, x+30, y+55); g.drawLine(x+30, y+55, x+27, y+45); } } else / / картинка вниз { g.setColor(Color.yellow) ; g.drawLine(x+15, y+5, x+15, y+65); g.drawLine(x+35, y+5, x+35, y+65); g.drawLine(x+5, y+20, x+45, y+20); g.drawLine(x+5, y+35, x+45, y+35); g.drawLine(x+5, y+50, x+45, y+50); } } / / поля данных для цветов и мастей final static int width = 50; final static int height = 70; final static int red = 0; final static int black = 1; final static int heart = 0; final static int spade = 1; final static int diamond == 2; final static int club = 3; / / поля данных private boolean faceup; private int r; private int s; public Card link; } class CardPile { CardPile (int xl, int yl) { x = xl; У = yl; firstCard = null; ) / / функции доступа к картам не переопределяются public Card top() ( B.2. ФАЙЛ SOUTARE.JAVA 421 DeckPile (int x, int y) ( / / вначале инициализировать как родителя super(x, у) ; / / затем создать новую колоду карт 1 I сначала положим карты в локальную стопку CardPile pileOne = new CardPile(0, 0) ; CardPile pileTwo = new CardPile(0, 0) ; int count = 0; for (int i = 0; i < 4; i++) for (int j = 0; j <= 12; j++) ( pileOne.addCard(new Card(i, j)); count++; } / / затем вытаскиваем карты произвольным образом for <; count > 0; count--) { int limit = ( (int) (Math. random () * 1000)) % count; / / передвинуть карту вниз на случайное место for (int i = 0; i < limit; i++) pileTwo.addCard(pileOne.pop()) ; / / затем добавить карту, найденную здесь addCard(pileOne.pop()) ; // теперь положить обратно while (! pileTwo.empty() ) pileOne.addCard(pileTwo.pop()) ; } } public void select (int tx, int ty) { if (emptyO) return; Solitare.discardPile.addCard(pop()); } } class DiscardPile extends CardPile ( DiscardPile (int x, int y) ( super (x, y) ; ) public void addCard (Card aCard) { if (! aCard.faceUpO ) aCard.flip(); super.addCard(aCard) ; } public void select (int tx, int ty) ( if (empty()) return; Card topCard = pop(); В.2. ФАЙ/J SOUTARE.JAVA 423 return (aCard.color() != topCard.color О) && (aCard.rank() == topCard.rank() —1); } public boolean includes (int tx, int ty) ( / / не проверять нижнюю карту return x <= tx && tx <= x + Card.width && У <= ty; } public void select (int tx, int ty) { if (empty() ) return; / / если карта закрыта, перевернем Card topCard = top(); if (! topCard.faceUp()) ( topCard.flip() ; return; } / / иначе посмотреть, можно ли переместить // в основание topCard = pop() ; for (int i = 0; i < 4; i++) if (Solitare.suitPile[i].canTake(topCard)) { Solitare.suitPile[i].addCard(topCard) ; return; } / / иначе посмотреть, можно ли переместить // в другую стопку расклада fox (int i = 0; i < 1; i++) if (Solitare.tableau[i].canTake(topCard)) { Solitare.tableau[i].addCard(topCard) ; return; } / / иначе положить обратно addCard(topCard) ; } private int stackDisplay(Graphics g. Card aCard) { int localy; if (aCard == null) return y; localy = stackDisplay(9, aCard.link); aCard.draw(g, x, localy); return localy + 35; } ГЛОССАРИИ Объектно-ориентированное программирование вводит множество новых идей и терминов, которые, вероятно, незнакомы новичку, даже если он достаточно опытен в традиционных языках программирования. Проблема также состоит в том, что в различных объектно-ориентированных языках для одного и того же понятия часто используются различные термины. Они в нашем глоссарии пере- числены как синонимы. Указаны ситуации, когда значение термина в одном языке противоречит его смыслу в других языках. ad hoc полиморфизм (ad hoc polymorphism). Идентификатор процедуры (мето- да) обозначает более одной процедуры (метода). Синоним: перегрузка. Class [Smalltalk, Java]. Класс, который отвечает за поведение экземпляров клас- са и создание подклассов. См. метакласс. CRC-карточка (CRC card). Карточка для заметок, на которой документируется имя класса, обязанности класса и сотрудничающие с ним классы. Используется в процессе создания спецификации, разработки и анализа системы. ЕСООР (European Conference on Object-Oriented Programming) — Европейская конференция по объектно-ориентированному программированию. Основ- ная конференция в Европе, в рамках которой обсуждаются объектно-ори- ентированные подходы, техники и инструментальные средства. extends [Java]. Ключевое слово, которое используется для того, чтобы сформи- ровать подкласс существующего класса или чтобы объявить новый ин- терфейс, расширяющий определенный ранее интерфейс. inherited [Object Pascal]. Ключевое слово. Используется для вызова переопре- деленной процедуры. initialize [Objective-C, Smalltalk]. Специальное сообщение, посылаемое объекту- классу до всех остальных сообщений. Оно может быть переопределено как «фабричный» метод, чтобы в процессе выполнения программы уста- новить нужную среду до использования экземпляров класса. isa — указатель связи (isa link) [Objective-C]. Неявный указатель, содер- жащийся в каждом объекте, который ссылается на таблицу диспетчериза- ции объекта. Поскольку объекты характеризуются исключительно своим поведением, этот указатель, по существу, кодирует класс объекта. Member [Object Pascal]. Встроенная системная функция. Возвращает значение типа boolean, которое используется для определения принадлежности ве- личины к указанному объектному типу. object [Object Pascal]. Определяет объектный тип данных. ГЛОССАРИЙ__________________________427 пированные сущности рассматриваются как элементы единой категории. Несущественная информация игнорируется. Абстрактный класс (abstract class). Класс, который не используется для соз- дания экземпляров. Он служит исключительно для порождения других классов. В языке C++ этот термин относится к классам, которые содер- жат хотя бы один чисто виртуальный метод. В языке Java абстрактным считается класс, явно объявленный с ключевым словом abstract. См. также отложенный класс. Абстрактный метод (abstract method) [Java]. Метод, который явно объявлен с ключевым словом abstract. Такие методы должны быть переопределены до их вызова. Автоматическая переменная (automatic variable). Переменная, для которой память выделяется автоматически при входе в процедуру. Противопос- тавляется динамической переменной, для которой память выделяется пользователем. Автоматическое управление памятью (automatic storage management). Алго- ритм распределения памяти, при котором исполнительная система нижнего уровня отвечает за нахождение и повторное использование недоступных (а следовательно, ненужных) блоков памяти. Из рассматри- ваемых в этой книге языков только Smalltalk и Java обеспечивают автоматическое управление памятью. См. также: сборка мусора. Агент (agent). Нетехнический термин, используемый для указания на то, что объект не зависит от других объектов, а также, что он обеспечивает об- служивание других объектов. Синонимы: объект, экземпляр. Базовый класс (base class) [C++]. Класс, из которого порождается другой класс. Синонимы: класс-предок, подкласс, родительский класс. Блок (block) [Smalltalk]. Последовательность операторов. Аналогичен безымян- ной функции. Блоки являются значениями и могут передаваться в качестве аргументов или (что используется реже) присваиваться пере- менным. Блок выполняет содержащиеся в нем команды в ответ на сооб- щение value. Броузер (browser). Программное средство, используемое для просмотра иерар- хии классов и методов, связанных с различными классами. Исходно разработан как часть программной среды Smalltalk. Теперь броузеры встречаются во многих интегрированных средах. Броузеры иного типа используются для доступа к информации в World Wide Web. Современ- ные Web-броузеры включают в себя интерпретаторы Java, позволяя эффективно выполнять Java-приложения во время обращения к Web- страницам. Быстрое макетирование (rapid prototyping). Стиль разработки программного обеспечения, при котором меньше внимания уделяется полному фор- мальному описанию, а вместо этого быстро создается прототип системы. ГЛОССАРИЙ__________________________429 сравнению с программами, которые должны дожидаться освобождения сервера (как правило, весьма загруженного). Гибридный язык (hybrid language). Язык, который содержит многие стили программирования. Например, C++ и Object Pascal являются гибридными, поскольку они поддерживают одновременно императивный (традиционный) и объектно-ориентированный подходы. Smalltalk является чистым объектно-ориентированным языком. Глобальная переменная (global variable). Переменная, к которой потенциально разрешен доступ из любого места программы. Граф наследования (inheritance graph). Абстрактная структура, которая ил- люстрирует отношение наследования в совокупности классов. Делегирование (delegation). Альтернативный подход к организации классов. Используя делегирование, объект перепоручает реализацию своего пове- дения другим объектам, называемым делегатами. Эта техника позволяет добиться общности поведения без классов и наследования. Деструктор (destructor) [C++]. Метод, который вызывается непосредственно перед тем, как освобождается память, занимаемая объектом. Деструктор может выполнять любые необходимые действия. Имя деструктора конст- руируется из символа «тильда» (~), за которым следует имя класса1. Диаграмма взаимодействия (interaction diagram). Документирует поток сооб- щений между объектами в сценарии. Динамическая переменная (dynamic variable). Переменная, для которой память выделяется явной командой пользователя. Противопоставляется автома- тической переменной, память для которой отводится автоматически при входе в процедуру. Динамический класс (dynamic class). См. статический класс. Динамический тип данных (dynamic type). Тип данных, связанный со значени- ем, которое содержится в переменной в текущий момент. Он не обяза- тельно совпадает со статическим типом данных, присвоенном переменной при ее объявлении. В объектно-ориентированных языках программиро- вания динамический тип, как правило, является потомком статического типа. Динамическое связывание (dynamic binding). Связывание имени и атрибута, которое производится во время выполнения программы, а не во время компиляции. См. время связывания. Дочерний класс (child class) [C++]. Класс, определяемый как расширение другого класса, называемого родительским. Синонимы: подкласс, произ- водный класс. 1 В языках Delphi Pascal и Borland Pascal with Objects также имеются методы-деструк- торы. В этих языках деструктор имеет произвольное назначаемое пользователем имя и идентифицируется с помощью ключевого слова destructor. — Примеч. перев. ГЛОССАРИЙ_______________________________________431 Иерархия классов (class hierarchy). Иерархия, образуемая классами в соответ- ствии с их взаимосвязью «класс-подкласс». См. также иерархия. Иерархия объектов (object hierarchy). В языке Object Pascal — последователь- ность объектных типов, связанных через наследование. Синоним: иерар- хия классов. Инкапсуляция (encapsulation). Техника, при которой информация прячется внутри структуры подобно тому, как данные, связанные с экземпляром класса, прячутся внутри класса. Интернет (Internet). Всемирная совокупность компьютеров, обменивающихся информацией по единому стандартизированному протоколу. Итератор (iterator). Класс, который используется в основном для того, чтобы обеспечить доступ к данным другого класса (как правило, контейнерного класса). Итератор предоставляет однородную среду доступа к данным без необходимости инкапсулировать контейнер. Каскад сообщений (cascaded message) [Smalltalk]. Сокращенный способ пере- слать несколько сообщений одному получателю. Класс (class). Абстрактное описание данных и поведения для совокупности похожих объектов, представители которой называются экземплярами класса. Синоним: объектный тип данных. Класс-метод (class method) [C++]. Метод, объявленный с помощью ключевого слова static. Класс-методам не разрешен доступ к переменным экземпля- ра — они могут обращаться только к переменным класса. Класс-методы могут вызываться без привязки к конкретному получателю путем явного указания имени класса. В терминологии языка C++ называются «стати- ческими функциями — членами класса». Класс-переменная (class variable). См. переменная класса. Класс-предок (ancestor class) [Object Pascal]. Тип данных, из которого произ- водится наследование. Класс, указанный при определении объектного типа, называется непосредственным предком. Синонимы: базовый класс, подкласс. Класс-спецификатор (specification class). Абстрактный надкласс, который ис- пользуется только для того, чтобы определить интерфейс. Действитель- ное воплощение интерфейса оставляется на долю подклассов. Клиент-подкласс (subclass client). Класс, который использует возможности над- класса для того, чтобы обеспечить свое собственное функционирование, а следовательно, имеет более свободный доступ к внутреннему устройству надкласса по сравнению с тем, что доступен клиентам-пользователям. Клиент-пользователь (user client). Класс, который использует средства, обес- печиваемые другим независимым классом, для чего требуется только та информация, которая представлена в интерфейсной, открытой части опи- сания класса. См. клиент-подкласс. ГЛОССАРИЙ__________________________433 Метакласс (metaclass) [Smalltalk]. Класс объекта-класса. Для каждого класса имеется ассоциированный с ним метакласс. Объект-класс является, как правило, единственным экземпляром метакласса. Метаклассы позволяют специфировать поведение классов. Без них все классы (но не экземпляры классов!) стали бы вести себя идентично. Метапрограммирование (metaprogramming). Стиль программирования, кото- рый интенсивно использует метаклассы. При этом семантика языка и смысл различных конструкций видоизменяются средствами самого же языка программирования. Smalltalk является одним из языков, использу- ющих метапрограммирование. Метод (method). Процедура или функция, связанная с классом (или объектным типом), вызываемая в стиле пересылки сообщений. «Метод-фабрика» (factory method). [Objective-C]. Метод, который распознает- ся только класс-объектом (объектом-фабрикой) данного класса. Противо- поставляется «экземплярным» методам, которые распознаются экземпля- рами данного класса. См. также: класс-метод, метакласс. Метод экземпляра (instance method) [Objective-C]. Метод, распознаваемый эк- земплярами класса. Синоним: метод. См. также: «методы-фабрики^. Множественное наследование (multiple inheritance). Свойство языка, которое позволяет подклассу наследовать сразу от нескольких надклассов. Не все объектно-ориентированные языки поддерживают множественное насле- дование. Мутатор (imitator). Метод, который используется для изменения значения переменной экземпляра. Если все такие изменения осуществляются через посредничество специальной функции, класс может более полно конт- ролировать свое внутреннее состояние. Надкласс (superclass). Синонимы: класс-предок, базовый класс. В языке Small- talk этот термин обозначает также класс, из которого производится на- следование атрибутов. Наследование (inheritance). Свойство объектов, посредством которого экземп- ляры класса получают доступ к данным и методам классов-предков без их повторного определения. См. также: класс-предок. Неизменяемое значение (immutable value). Значение, которое устанавливается только один раз, после чего изменять его запрещается. Переменные, кото- рые содержат такие значения, иногда называют «однократно присваивае- мыми». В языке C++ они идентифицируются ключевым словом const. Непосредственный надкласс (immediate superclass). Ближайший родительский класс, из которого порожден данный класс. Отношение «быть надклас- сом» является транзитивным замыканием отношения «быть непосред- ственным надклассом». ГЛОССАРИЙ_________________________435 Открытый метод (public method). Метод, который может без ограничения вы- зываться вне объекта. Отложенный класс (deferred class). См. абстрактный класс. Отложенный метод (deferred method). Метод, для которого определен интер- фейс, но не реализация. Последняя обеспечивается подклассами, которые переопределят отложенный метод, сохраняя его интерфейс. См. также чисто виртуальный метод. Отношение «быть экземпляром» (is-a relation). Условие, которое утверждает, что экземпляры подкласса должны быть просто специализированными формами надкласса. Тем самым экземпляры подкласса могут быть ис- пользованы везде, где требуются величины типа надкласса. См. также: отношение «включать как часть». Отношение «включать как часть» (has-a relation). Условие, которое утвержда- ет, что экземпляры класса содержат поля данных определенного типа. См. также отношение «быть экземпляром^. Парадигма (paradigm). Базовая модель конкретного способа организации ин- формации. Объектно-ориентированная парадигма делает упор на поведе- нии и обязанностях. Параметризованные классы (parametrized classes). Классы, в определении кото- рых некоторые типы данных оставлены неопределенными. До создания экземпляров класса происходит доопределение неизвестных типов. Параметрическая перегрузка (parametric overloading). Перегрузка имени функ- ции, при которой в данном контексте имеются две или более функции с тем же именем. Неоднозначность разрешается путем сравнения числа аргументов в заголовке функции и их типов. Параметрическая перегруз- ка существует для процедур, функций, методов и операторов. Перегрузка (overload). Этот термин используется для идентификаторов, кото- рые обозначают более одного объекта. Перегружаются функции, процеду- ры, методы и операторы. Виртуальный метод (метод, переопределяемый программистом) также может называться перегруженным. См. парамет- рическая перегрузка. Переименование (renaming). Меняется имя наследуемого кода, но не его пове- дение. Противопоставляется переопределению. Переменная класса (class variable). Поле данных, являющееся общим для всех экземпляров данного класса. В C++ это член класса, объявленный с ключевым словом static. В Smalltalk это переменная, объявленная как пе- ременная класса (class variable) в сообщении, указывающем на создание класса. Переменная с однократным присваиванием (single-assignment variable). Пере- менная, которой значение присваивается лишь однажды. Затем оно не может быть изменено. В языке C++ переменные с однократным при- сваиванием либо описаны с модификатором const, либо существуют в ГЛОССАРИЙ_________________________437 Полиморфный (polymorphic). Буквально — «многоформенный». Полиморфная переменная способна принимать значения различных типов. Понятие ис- пользуется также для функций, имеющих хотя бы один полиморфный аргумент, и для имен функций, которым соответствует несколько разных функций. См. чистый полиморфизм, ad hoc полиморфизм. Полное имя (qualified name) [C++]. Имя метода или переменной экземпляра, в котором в явном виде указано, к какому классу относится метод или переменная. В C++ имя класса отделяется от метода двумя двоето- чиями (класс::метод). В языках Java и Object Pascal используется точка. Поскольку в этом случае класс метода указан явно, вызов с исполь- зованием полного имени может осуществляться как вызов процедуры вместо обычной пересылки сообщений. Получатель (receiver). Объект, которому посылается сообщение. В языках Smalltalk и Objective-С получатель идентифицируется как объект, стоящий слева от спецификатора сообщения. Внутри метода, соответст- вующего сообщению, сам получатель этого сообщения может иденти- фицироваться различными способами: в языке C++ для этой цели служит псевдопеременная-указатель this, а в языках Objective-C, Object Pascal и Smalltalk объект-получатель доступен через псевдопеременную- объект self. Потерянный объект (persistent object). Объект, который продолжает сущест- вовать даже тогда, когда фрагменты кода программы, имеющие к нему доступ, уже закончили свою работу. Приведение типа (cast). Одноместное (унарное) выражение, которое преоб- разует значение из одного типа в другой. Примитивная операция (primitive) [Smalltalk]. Операция, которая не может быть выполнена языком программирования, и требует использования средств исполнительной системы более низкого уровня. Принцип подстановки (substitutability, principle of). Принцип, который утверж- дает, что можно безнаказанно подставлять экземпляры дочернего класса туда, где используются экземпляры родительского класса. Он справед- лив, если дочерний класс является подтипом родителя, но никак не в общем случае. Принципы Парнаса (Parnas's principles). Принципы, которые регламентируют правильное использование модулей и модульного подхода при раз- работке программ. Первоначально сформулированы специалистом по информатике Дэвидом Парнасом. Проблема «вверх-вниз» (уо-уо problem). Проблема, связанная с объектно- ориентированными языками: при трассировке вызова конкретного метода может потребоваться многократное перемещение вверх и вниз по иерархии классов. Программа просмотра (browser). См. броузер. ГЛОССАРИЙ__________________________439 Селектор сообщения (message selector). Текстовая строка, которая иден- тифицирует сообщение при пересылке сообщений. Она используется для того, чтобы найти соответствующий метод (что является частью процесса поиска требуемого метода). Синонимы: селектор, селектор метода, идентификатор метода. Сигнатура аргументов (argument signature) [C++]. Внутреннее представление списка типов данных аргументов функции. Используется для ликвидации двусмысленности при вызове перегруженных функций. Из числа претендентов выбирается та функция, которая наиболее близко соответ- ствует сигнатуре вызываемой функции. См. параметрическая перегрузка. Сигнатура типа (type signature). См. сигнатура аргументов. Символ (symbol) [Smalltalk]. Величина, которая однозначно характеризуется своим значением. Аналогичен значениям перечисиляемых типов в С или Pascal с тем отличием, что во время выполнения символ может напеча- тать себя в текстовом виде. Скрываемое имя (shadowed name). Имя, которое совпадает с другим именем в пределах данного контекста. Новый объект с тем же именем, по существу делает предыдущий объект недоступным в пределах текущего контекста, то есть происходит скрытие. Примером служит локальная переменная с именем, совпадающим с именем глобальной переменной. Внутри проце- дуры всем ссылкам на соответствующий идентификатор соответствует локальная переменная — тем самым затрудняется доступ к переменной более высокого уровня. В языках C++ и Java доступ к таким переменным возможен с использованием полного составного имени. Скрытие данных (data hiding). См. маскировка данных. Скрытие информации (information hiding). См. маскировка информации. Совокупности классов (collection classes). См. контейнерные классы. Сообщение (message). Текстовая строка, которая определяет требуемое действие при пересылке сообщений. Эта строка используется для того, чтобы найти соответствующий ей метод (что является частью процесса поиска требуемого метода). Синонимы: селектор, селектор сообщения, селектор метода, идентификатор метода. Сотрудник (collaborator). Два класса, которые зависят друг от друга при выпол- нении своих функций, называются сотрудниками. Спецификатор доступа (access specifier) [C++, Delphi Pascal]. Одно из ключе- вых слов: private, protected, public. Управляет доступом к полям данных и методам в определяемых пользователем классах. «Срезка» (slicing) [C++]. Процесс, при котором значение производного типа передается в качестве аргумента, объявленного с базовым типом. В ре- зультате поля и методы производного класса отрезаются от базовых полей. ГЛОССАРИЙ 441 Функция доступа (accessor function). Функция, которая используется для доступа к значениям полей данных экземпляра класса. Если доступ к данным осуществляется только через эту функцию, то программист мо- жет гарантировать, например, что поле данных будет читаться, но не модифицироваться. См. также: мутатор. Функция-член класса (function member) [C++]. См. метод. Чисто виртуальный метод (pure virtual method) [C++]. Виртуальный метод, не содержащий тела. Создается путем присваивания полю функции значения 0 в определении класса. Чисто виртуальные методы обязаны специфицироваться в подклассах. См. также: отложенный метод. Чистый полиморфизм (pure polymorphism). Свойство функции, которое поз- воляет вызывать ее с аргументами различных типов. См. ad hoc поли- морфизм. Член данных (data member) [C++]. См. переменная экземпляра. Член класса (member) [C++]. Термин, обозначающий атрибуты экземпляров класса. Переменные экземпляра называются в C++ членами данных, а методы — функциями-членами класса. Экземпляр (instance). В языке C++ — переменная типа class. В языке Object Pascal — объектная переменная. В языке Smalltalk — конкретный пример структуры общего вида, определяемой классом. Синоним: объект. Экспортируемое имя (exported name). Идентификатор (имя переменной, типа данных, функции или метода), доступный вне контекста, в котором он определен. В языке Objective-C — переменная, тип данных, функция или метод, которые определены глобально или описаны в файле интерфейса (*.h). В языке Object Pascal — переменная, тип данных, функция или метод, которые описаны в разделе interface модуля. В языке Java — класс, который объявлен как public в том пакете, где он определен. Язык с динамическими типами данных (dynamically typed language). Язык про- граммирования, в котором типы данных связываются с текущими зна- чениями, а не с переменными. Переменные могут принимать значения любого типа. Язык программирования Smalltalk является примером ди- намически типизированного языка. Язык с контролем типов данных (strongly typed language). Язык программиро- вания, в котором тип любого выражения может быть определен на этапе компиляции. Язык со статическими типами данных (statically typed language). Язык про- граммирования, в котором переменные могут принимать значения только объявленного типа. Такие языки, как правило, подразумевают строгий контроль типов данных. Объектно-ориентированные языки зачастую ос- лабляют правила работы со статическими типами данных за счет того, что разрешают переменным иметь значения, относящиеся к подтипу объявленного типа данных. БИБЛИОГРАФИЯ 443 [Budd 1994] - Timothy A. Budd, «Classic Data Structures in C++», Addison- Wesley, Reading, MA, 1994. [Cardelli 1985] - Luca Cardelli and Peter Wegner, «On Understanding Types, Data Abstraction and Polymorphism», Computing Surveys, 17(4): 471-523, 1985. [Carrol 1995] - Martin D. Carrol and Margaret A. Ellis, «Designing and Coding Reusable C++», Addison-Wesley, Reading, MA, 1987. [Chirlian} - Paul M. Chirlian, «Programming in C++», Merrill, Columbus, OH, 1990. [Church 1936] - Alonzo Church, «An Unsolvable Problem of Elementary Number Theory», American Journal of Mathematics, 58:345-363, 1936. [Cohen 1981] - Jacques Cohen, «Garbage Collection of Linked Data Structures», ACM Computing Surveys, 13(3): 341-367, 1981. [Coplien 1995] - Pattern Languages of Program Design, edited by James A. Coplien and Douglas C. Schmidt, Addison-Wesley, Reading, MA, 1995. [Cox 1986] - Brad J. Cox, Object Oriented Programming: An Evolutionary Ap- proach, Addison-Wesley, Reading, MA, 1986. [Cox 1990] - Brad J. Cox, «Planning the Software Industrial Revolution», IEEE Software, 7(6): 25-35, November 1990. [Dahl 1966] - Ole-Johan Dahl and Kristen Nygaard, «Simula, An Algol-Based Simulation Language», Communication of the ACM, 9(9): 671-678, Septem- ber 1966. [Danforth 1988] - Scott Danforth and Chris Tomlinson, «Type Theories and Object- Oriented Programming», ACM Computing Surveys, 20(1): 29-72, 1988. [Deutsch 1989] - L. Peter Deutsch «Design Reuse and Frameworks in the Smalltalk- 80 System» In Ted J. Biggerstaff and Alan J. Perils (Eds.), Software Reusability, Volume II: Applications and Experience, pages 57-71, Addison- Wesley, Reading, MA, 1989. [Dijkstra 1976] - Edsger W. Dijkstra, A Discipline of Programming, Prentice-Hall, Englewood Cliffs, NJ, 1976. [Имеется русский перевод] [Eckel 1989] - Bruce Eckel, Using C++, McGraw-Hill, New York, 1989. [Ellis 1990] - Margaret A. Ellis and Bjarne Stroustrup, The Annotated C++ \/ Reference Manual, Addison-Wesley, Reading, MA, 1990. [Fairley 1985] - Richard Fairiey, Software Engineering Concepts, McGraw-Hill, New York, 1985. [Fischer 1988] - Charles N. Fischer and Richard J. LeBlanc, Jr., Grafting A Compiler, \/ Benjamin Cummings, Menio Park, CA, 1988. [Floyd 1979] - Robert W. Floyd, «The Paradigms of Programming», Communi- cations of the ACM, 22(8): 455-460, August 1979. [Имеется русский перевод] БИБЛИОГРАФИЯ___________________________________445 [Ingalls 1986] - Daniel H. H. Ingalls, «A Simple Technique for Handling Multiple Polymorphism», Proceedings of the 1986 OOPSLA — «Conference on Ob- ject-Oriented Programming Systems, Languages and Applications»; Reprinted in Sigplan Notices, 21(11): 347-349, 1986. [Kaehler 1986] - Ted Kaehler and Dave Patterson, A Taste of Smalltalk, W. W. Norton & Company, New York, 1986. [Kamin 1990] - Samuel N. Kamin, Programming Language; An Interpreter Based Approach, Addison-Wesley, Reading, MA, 1990. [Kay 1977] - Alan Kay, «Microelectronics and the Personal Computer», Scientific American, 237(3): 230-244, 1977. [Kay 1993] - Alan С. Kay, «The Early History of Smalltalk», The Second ACM SIGPLAN History of Programming Languages Conference (HOLP-II), ACM SIGPLAN Notices 28(3): 69-75, March 1993. [Keene 1989] - Sonya E. Keene, Object-Oriented Programming in Common Lisp, Addison-Wesley, Reading, MA, 1989. [Keller 1990] - Daniel Keller, «A Guide to Natural Naming», Sigplan Noti- ces, 25(5): 95-102, May 1990. {Kic.za.les 1991] - Gregor Kiczales, Jim des Rivieres and Daniel G. Bobrow, The Art of the Metaobject Protocol, MIT Press, Cambridge, MA, 1991. [Kim 1989] - Won Kim and Frederick H. Lochovsky (Eds.), Object-oriented Concepts, Databases, and Applications, Addison-Wesley, Reading, MA, 1989. [Kirkemd 1989] - Bjorn Kirkerud, Object-oriented Programming with Simula, Addison-Wesley, Reading, MA, 1989. [Kleene 1936] - Stephen C. Kleene, «^-Definability and Recursiveness», Duke Mathematical Journal, 2: 340-353, 1936. [Knolle 1989] - Nancy Т. Knolle, «Why Object-oriented User Interface Toolkits Are Better», Journal of Object-oriented Programming, 2(4): 63-67, 1989. [Koenig 1989a] - Andrew Koenig, «References in C++», Journal of Object-oriented Programming, 1(6), 1989. [Koenig 1989b] - Andrew Koenig, «Objects, Values, and Assignment», Journal of Object-oriented Programming, 2(2), 37-38, 1989. [Koenig 1989с] - Andrew Koenig, «What Are Friends For?», Journal of Object- oriented Programming, 2(4): 53-54, 1989. [Korienek 1993] - Andrew Korienek, A Quick Trip to ObjectLand, Prentice-Hall, Englewood Cliffs, New Jersey, 1993. [Krasner 1983] - Glenn Krasner, Smalltalk-80: Bits of History, Words of Advice, Addison-Wesley, Reading, MA, 1983. [Krogdahl 1985] - Stein Krogdahl, «Multiple Inheritance in Simula-Like Lan- guages», BIT, 25: 318-326, 1985. БИБЛИОГРАФИЯ___________________________________447 [Meyer 1994] - Bertrand Meyer, Reusable Software, Prentice-Hall, Englewood Cliffs, NJ, 1994. [Micallef 1988] - Josephine Micallef, «Encapsulation, Resuability and Extensibility in Object-Oreinted Programming Languages», Journal of Object-Oriented Programming, 1(1): 12-35, 1988. [Milner 1990] - Robin Milner, Mads Tofte, and Robert Harper, The Definition of Standard ML, MIT Press, Cambridge, MA, 1990. [Morehead 1949] - Albert H. Morehead and Geoffrey Mott-Smith, The Complete Book of Solitaire and Patience Games, Grosset & Dunlap, New York, 1949. [Musser 1996] - David R. Musser and Atui Saini, STL Tutorial and Reference Guide, McGraw-Hill, New York, 1996. [O'Brian 1989] - Stephen K. O'Brian, Turbo-Pascal 5.5: The Complete Reference, McGraw-Hill, New-York, 1989. [Pamas 1972] - David L. Parnas, «On the Criteria to Be Used in Decomposing Systems into Modules», Communications of the ACM, 15(12): 1059- 1062, 1972. [Perry 1990] - Dewayne E. Perry and Gail E. Kaiser, «Adequate Testing and Object- oriented Programming», Journal of Object-oriented Programming, 2(5): 13- 19, 1990. [Pinson 1988] - Lewis J. Pinson and Richard S. Wiener, An Introduction to Object- oriented Programming and Smalltalk, Addison-Wesley, Reading, MA, 1988. [Pohl 1989] - Ira Pohl, C++ for С Programmers, Addison-Wesley, Reading, MA, 1989. [Post 1936] - Emil L. Post, «Finite Combinatory Processes Formulation, I», The Journal of Symbolic Logic, 1: 103-105, 1936. [Pree 1995] - Wolfgang Pree, Design Patterns for Object-oriented Software De- velopment, Addison-Wesley, Reading, MA, 1995. [Pullum 1991] - Geoffrey K. Pullum, The Great Eskimo Vocabulary Hoax, The University of Chicago Press, Chicago, 1991. [Rist 1995] - Robert Rist and Robert Terwilliger, Object-oriented Programming in Eiffel, Prentice-Hall, Englewood Cliffs, NJ, 1995. [Rogers 1967] - Hartley Rogers , Jr., Theory of Recursive Functions and Effective Computability, McGraw-Hill, New York, 1967. [Rosenberg 1971] - Jay F. Rosenberg and Charles Travis (Eds.), Readings in Philosophy of Language, Prentice-Hall, Englewood Cliffs, NJ, 1971. [Sakkinen 1988a] - Markku Sakkinen, «On the darker side of C++», ECOOP'88 Proceedings: European Conference on Object-oriented Programming, S. Gjessing and K. Nygaard, Eds., Springer-Verlag, 1988. БИБЛИОГРАФИЯ____________________________________449 [Turing 1936] - Alan Turing, «On computable numbers, with an application to the Entscheidungsproblem», Proceedings of the London Mathematical Society, Series 2, 42: 230-265; and 43: 544-546. [Ungar 1987] - David Ungar and Randall B. Smith, «Self: The Power of Simplicity», Proceedings of the 1987 OOPSLA — Conference on Object-Oriented Pro- gramming Systems, Languages and Applications; Reprinted in Sigplan No- tices, 22(12): 227-242, 1987. [Unger 1987] -J. Marshall Unger, The Fifth Generation Fallacy, Oxford University Press, New York, 1987. [Usenix 1987] - Proceedings of the C++ Workshop, USENIX Association, Berkley, CA, 1987. [Webster 1989] - Bruce F. Webster, The NeXT Book, Addison-Wesley, Reading, MA, 1989. [Wegner 1986] - Peter Wegner, «Classification in Object-oriented Systems», Sig- plan Notices, 21(10): 173-182, October 1986. [Weinand 1988] - Andre Weinand, Erich Gamma, and Rudolf Marty. «ET++, An Object-oriented Application Framework in C++», in Proceedings of the 1988 OOPSLA — Conference on Object-oriented Programming Systems, Langua- ges and Applications; Reprinted in Sigplan Notices, 23(10): 46-57, Octo- ber 1988. [Weiskamp 1990] - Keith Weiskamp and Bryan Flamig, The Complete C++ Primer, Academic Press, New York, 1990. [Wiener 1988] - Richard S. Wiener and Lewis J. Pinson, An Introduction to Object- oriented Programming and C++, Addison-Wesley, Reading, MA, 1988. [Wiener 1989] - Richard S. Wiener and Lewis J. Pinson, «A Practical Example of Multiple Inheritance in C++», Sigplan Notices, 24 (9): 112-115, 1989. [Wiener 1990] - Richard S. Wiener and Lewis J. Pinson, The C++ Workbook, Addison-Wesley, Reading, MA, 1990. [Wikstrom 1987] - Ake Wikstrom, Functional Programming Using Standard ML, Prentice-Hall International, London, 1987. [Wilson 1990] - David A. Wilson, Larry S. Rosenstein, and Dan Shafer, Program- ming With MacApp, Addison-Wesley, Reading, MA, 1990. [Wirfs-Brock 1989a] - Alien Wirfs-Brock and Brian Wilkerson, «Variables Limit Reusability», Journal of Object-oriented Programming, 2(1): 34-40, May 1990. [Wirfs-Brock 1989a] - Alien Wirfs-Brock and Brian Wilkerson, «Object-oriented Design: A Responsibility-Driven Approach», Proceedings of the 1989 OOPSLA — «Conference on Object-oriented Programming Systems, Lan- АЛФАВИТНЫЙ УКАЗАТЕЛЬ А ad hoc полиморфизм, 270 Ada, язык программирования, 302 APL, 23 Applet, в языке Java, 125, 177, 338 В Beta, язык программирования, 213, 221 inner, ключевое слово, 221 block, конструкция языка Smalltalk, 291 Borland International, 74 С C++ before, функция, 204 class, ключевое слово, 84 const, ключевое слово, 104 const_cast, функция, 206 delete, ключевое слово, 104 dynamic_cast, функция, 206, 248, 295 inline, ключевое слово, 86 new, создание объекта, 102 private, ключевое слово, 84, 323 protected, ключевое слово, 323 public, ключевое слово, 84, 323 reinterpret_cast, функция, 206 RTTI, 204 static, ключевое слово, 98 static_cast, функция, 206 this, псевдопеременная, 95 typeinfo, структура, 204 union, структура данных, 233 virtual, ключевое слово, 155, 202 видимость, 325 виртуальное наследование, 262 деструктор, 104 дружественная функция, 328 замещение метода, 215 идентификация типа во время выполнения, 204, 248, 295 C++ (продолжение) инициализаторы, 103 интерфейсный файл, 84 класс, 84 конструктор, 101, 155, 226 по умолчанию, 226 контрвариантное переопределение, 244 копирующий конструктор, 238 методы, 84 наследование, 154 множественное, 155, 257 обобщенные функции, 282 объявление переменной, 101 оператор инициализации, 236 присваивания, 236 отличия от Java, 88 отложенные методы, 282 параметрическая перегрузка, 261, 281 перегруженные функции, 102 перегрузка и переопределение методов, 331 переменные класса, 370 переопределение метода, 280 полиморфные переменные, 279 полное имя метода, 225 порядок инициализации, 263 приватное наследование, 327 приведение динамического типа, 295 приведение типа, 247 пространство имен, 329 равенство объектов, 246 родственные экземпляры, 326 связывание методов и сообщений, 202 система RTTI, 248, 295 составное имя, 225 «срезка», 231 уточнение метода, 225 функция-член класса, 94 циклы и итерации, 299 АЛФАВИТНЫЙ УКАЗАТЕЛЬ 453 Java (продолжение) синтаксис пересылки сообщений, 96 тип данных boolean, 88 уточнение метода, 227 L LAF, мини-среда разработки, 337, 348 Lisp, язык программирования, 267 Little Smalltalk, язык программирования, 387 м map, структура данных, 308 Model-View-Controller, среда языка Smalltalk, 336 N NewsWeek, еженедельник, 36 О Object Pascal, 199 dispose, ключевое слово, 108 inherited, ключевое слово, 225 Member, функция, 199 new, ключевое слово, 108 object, ключевое слово, 152 override, ключевое слово, 152 Result, псевдопеременная, 94 self, псевдопеременная, 93 библиотека процедур unit, 74 видимость, 324 замещение методов, 218 запись с вариантами, 233 идентификация типа во время выполнения, 248 история, 74 класс, 76 ключевое слово class, 76 методы, 76 наследование, 151 оператор присваивания, 238 отложенный метод, 283 поиск метода, 93 получатель сообщения, 93 равенство объектов, 244 связывание методов и сообщений, 199 Object Pascal (продолжение) сообщение, 93 уточнение метода, 225 Objective-С free, ключевое слово, 108 import, ключевое слово, 82 new, ключевое слово, 107 self, ключевое слово, 107 super, псевдопеременная, 227 union, структура данных, 233 видимость, 333 вызов сообщений, 97 замещение метода, 220 классы, 82 контейнерный класс, 290 методы, 82 методы-фабрики, 107 наследование, 154 оператор присваивания, 240 отложенный метод, 284 переменные класса, 371 подстановка класса, 368 полиморфизм, 284 приведение типа, 247 равенство объектов, 244 связывание методов и сообщений, 201 уточнение метода, 227 OWL, библиотека, 336 Р private, ключевое слово, 84 property, ключевое слово, 109 protected, ключевое слово, 155, 323 public, ключевое слово, 84 R RTTI, идентификация типа во время выполнения, 204, 248 s Sather, язык программирования, 244 Scheme, язык программирования, 267, 302 self, псевдопеременная, 93 Self, язык программирования, 376 Simula, язык программирования, 213, 221, 389 видимость (продолжение) в языке Object Pascal, 324 в языке Objective-C, 333 в языке Smalltalk, 324 глобальная, 316 для блоков, 39 на уровне класса, 321 на уровне объектов, 321 виртуальное наследование, 262 «включать как часть», отношение, 185 влияние языка на мышление, 22 встроенные операции, в ООП- языках, 36 выделение памяти, 229 через «кучу», 98, 229 через стек, 98, 229 вызов процедуры, 28 выражение типа сообщения, 97 вычисления в форме моделирования, 35 Г гибкость и эффективность, 209 гипотеза Сапира-Ворфа, 24 глобальные переменные область видимости в пределах программы, 316 область видимости в пределах файла, 316 готовность к изменениям, 57 граф, 308 д двойная диспетчеризация, 253, 344 деструктор, 104 дети, обучение языку Smalltalk, 27 диаграмма взаимодействия, 60 динамические методы, 153 динамические переменные, 98 динамические типы данных, 96, 151, 195 добавление методов, 212 документация, разработка, 55 дочерний класс, 33, 143, 188 дружественная функция, 328 3 зависимость, 315, 322 в языке Objective-C, 322 в языке Smalltalk, 322 задача о восьми ферзях, 113 караульное значение, 126, 128 закон Деметера, 320 сильная форма, 320 слабая форма, 320 закрытое наследование, 148, 190, 327 закрытые поля данных, 163 замещение методов, 175, 212 в языке C++, 215 в языке Delphi Pascal, 218 в языке Java, 220 в языке Object Pascal, 218 в языке Objective-C, 220 в языке Smalltalk, 220 принцип подстановки, 214 запись активации процедуры, 379 вариантная, 233, 289 запредельный элемент, 304 зацепление, 61 по внутренним данным, 316 по глобальным данным, 316 преднамеренное, 334 разновидности, 316 через параметры, 316 через подклассы, 316 защищенные поля, 155 значение активное, 321 в естественном языке, 240 И игра в бильярд, 131, 184, 247, 379 идентичность в естественном языке, 240 в сравнении с равенством, 240 иерархия классов, 33 наследования с единым предком, 151 объектов, 33 подклассов, 282 подтипов, 282 инициализаторы, 103 инкапсуляция, 70, 92, 302 интерпретатор, 377 и байт-код, 387 интерпретация сообщения, 29 интерфейс, в сравнении с реализацией, 81 в языке Java, 208 надкласс, 33, 143 накладные расходы на пересылку сообщений, 161 наследование, 33, 69, 143, 184 безвариантное, 244 в языке C++, 154 в языке Java, 155 в языке Object Pascal, 151 в языке Objective-С, 154 в языке Smalltalk, 153 виртуальное, 262 для конструирования, 147 для специализации, 146 для спецификации, 147 закрытое, 148, 190 и варьирование, 150 и комбинирование, 150 и обобщение, 148 и ограничение, 149 и присваивание, 235 и расширение, 149 иерархия, 151 иерархия с единым предком, 151 издержки, 159 как комбинирование, 250 как расширение, 143 как сужение, 143 ковариантное, 243 контрвариантное, 243 множественное, 150, 250, 253 от общих предков, 256 преимущества, 157 формы и разновидности, 146 независимость агентов, в ООП , 29 неизменяемые переменные, 100 О область видимости, 315 обобщение и порождение подклассов, 148 обобщенные алгоритмы, 302 обобщенные функции, 268, 278 обобщенный класс, 297 обращение полиморфизма, 196, 197 объект, 358 для хранения информации, 147 эквивалентность, 240 объект-класс, 363 объект-фабрика, 363 _____________45^ объектно-ориентированное программирование, 20 объектно-ориентированные языки смешанного типа, 36 объекты, 35 взаимодействие друг с другом, 114 функционирующие автономно, 131 объявление объекта, 69 обязанности, 27 управление проектированием, 48 ограничение, и порождение подклассов, 149 оператор goto, 25, 320 ответственность, в ООП, 29 отличия C++ и Java, 88 отложенные решения, 57 отложенный метод, 270, 275 отношение «быть экземпляром», 185 «включать как часть», 185 отображение, 308 ошибка этапа выполнения, 210 этапа компиляции, 210 п пакеты в ООП-языках, 41 в языке Java, 332 парадигма генерации и тестирования, 116 программирования, 26 параметризованный класс, 297 параметрическая перегрузка, 261 параметрический полиморфизм, 268 перегруженное имя, 271 перегруженные функции, 102 перегрузка, 261, 268, 270 и переопределение методов, 331 и приведение типа, 272 параметрическая, 261 переименование и двусмысленность имен, 256 переменные автоматические, 98 выделение памяти, 98 динамические, 98 класса, 369 неизменяемые, 100 протокол, 61 как набор обязанностей, 29 прототип функции, 85 процедура, вызов, 28 псевдопеременная, 93 Р работа, управляемая событиями, 349 равенство в естественном языке, 240 в сравнении с идентичностью, 240 равенство объектов в языке C++, 246 в языке Java, 244 в языке Object Pascal, 244 в языке Objective-C, 244 в языке Smalltalk, 245 размножение экземпляров, для модулей, 41 разработка документации, 55 расширение и порождение подклассов, 149 регрессионное тестирование, 67, 362 рекурсивное определение, 118 родительский класс, 33, 143 руководство пользователя, 55 С сборка мусора, 99 связность коммуникационная, 318 логическая, 318 на уровне данных, 318 по совмещению, 317 последовательная, 318 функциональная, 318 связывание метода и сообщения, 196 позднее, 29 раннее, 29 «сделано не здесь», синдром администрирования, 193 селектор сообщения, 93 семантика американская, 213 копирования, 235 скандинавская, 213 указателей, 235 «серебряная пуля», 21 _____________459 символ, 79 системы хранения информации, 147 экспертные, 20 скандинавская семантика, 213 сложность, нелинейный рост, 37 сообщение, 27 составное имя, 225 состояние и поведение, 60 объекта, 70 специализация и порождение подклассов, 146 спецификация и порождение подклассов, 147 список аргументов сообщения, 93 способ связывания, 196 среда разработки, 184, 336, 348 LAF, 337, 348 ссылка в естественном языке, 240 статические типы данных, 151, 195 «стиль Гёделя», 382 структурное программирование, 20, 269 схема, 269 двойной диспетчеризации, 344 разработки, 336 Т таблица виртуальных методов, 380 диспетчеризации, 384 тестирование, 362 блоков, 67 и наследование, 362 регрессионное, 67, 362 системы в целом, 67 тип данных динамический, 151, 195 статический, 151, 195 «тьюринговская пропасть», 25 У указатель, 98, 100 управление памятью, 362 автоматическое, 98, 108 и классы, 362 утечка памяти, 100 через «кучу», 98 через стек, 98 утверждения, в языке Eiffel, 214 Серия М. Бабушкин, В. Коростелев, С. Иваненко Web-сервер в действии 272 стр., в продаже +CD! ISBN 5-88782-245-7 Книга является исчерпывающим практическим руководством по созданию Web-сервера. В ней приводится методика выбора необходимого аппаратного и программного обеспечения, подробно рассматриваются все популярные операционные системы (Windows NT 4.0, Novell NetWare 4.1, OS/2 Warp 4.0, UNIX) и наиболее распространенные Web-серверы, работающие под их управлением (US, Fast Track, NetWare Web Server, ICS, Apache). В книге описывается процесс установки и настройки Web-серверов, а также предлагаются примеры программ для сбора и анализа статис- тической информации. Большое внимание уделяется коммерческому использованию возможностей WWW, приводятся многочисленные примеры реализации различных подходов к бизнесу в Internet. Значительная часть книги посвящена рекомендациям по работе с Web-сервером, его наполнению информационными ресурсами и методам увеличения посещаемости сервера. На сопроводительном CD ROM вы найдете множество программ для управления Web-сервером. код 397, ориентировочная цена 28 000 руб. * CD код 411 ориентировочная цена 28 \ В. Андреев Microsoft Exchange в действии 272 стр., в продаже ISBN 5-88782-241-4 Mjprosoft, —"""r-ч'"' Exchange В книге Владимира Андреева, сертифицированного системного инженера Microsoft, рассматривается архитектура и функции системы информационного обмена Microsoft Exchange. Книга адресована сетевым администраторам и менеджерам информа- ционных систем, уже использующим Microsoft Exchange в своих организациях или рассматривающим такую возможность. Издание будет способствовать более точному пониманию места Microsoft Exchange в бизнес-процессе современной компании. Основное внимайие уделяется обеспечению надежного функционирования системы и возможностям ее расширения посредством создания ЭДриложений на базе ^|Kosoft Exchange. ^еод 419, ориентирование» цена 28 000руб. Б. Морис ^ HTML в действии^ „ 300 стр., в продаже -•"дйЙКвта! | ISBN 5-88782-216-3 г Кнага предназначена дня разработчиков Web-страниц, зн, и жеяаювдйх с помощью доступных современных средств красивымиц удобными дяя чтения. После беглого обзора5 основных тегов и атрибутов HTML ^^^^^Д| переходит к описанию средств, позвояйаощих улучшить дизайн страницы (табяи^Крйры и т. д.). Обсуждаются использование мультимедиа и встроенной анимации, а такж^^эможности Java и ActiveX. код 409, ориентир, иена 34 000 руб; дискета код 504, ориентир, цена 15 000руб. * Стоимость книг указана с учетом почтовых расходов. с основами языка HTML iTb свои страницы более ЛИТЕРАТУРА ПО INTERNET Т. Кенцл Форматы файлов Internet 320 стр., в продаже ISBN 5-88782-198-1 Доступное и систематичное изложение вопросов, связанных с использованием самых разных форматов файлов, встречающихся в Internet. В книге собраны сведения о наиболее популярных текстовых, звуковых, графических и видеоформатах, таких как HTML, CGML, PDF, TeX, GIF, TIFF, VRML, PND, JPEG, ZIP, UUE, XXE, MIME, AU, WAVE, AVI, QuickTime, MPEG и других. Автор рассказывает о том, как следует распаковывать файлы, сжатые различными архиваторами, как кодировать и декодировать данные. Прочитав эту книгу, вы научитесь распознавать «почерк» многих утилит, которые пора- ботали над файлом перед тем, как он попал на ваш компьютер. Начинающие пользователи узнают, как много информации, добытой в Сети, можно, оказывается, преобразовать в понятный вид. А знатокам Internet книга поможет разобраться с самыми хитрыми файлами, которые упорно не поддаются всем привычным способам обработки. На сопроводительном компакт-диске записано множество полезных утилит для просмотра, кодирования и преобразования файлов. Этот CD-ROM существенно облегчит жизнь всем, кому приходится постоянно иметь дело с Сетью. На сопроводительный компакт-диск к книге « Форматы файлов Internet» записано множество полезных утилит для просмотра, кодирования и конвертации файлов. Этот CD-ROM существенно облегчит жизнь всем, кому приходится постоянно иметь дело с информацией, полученной по Сети. код 352, ориент. цена 37 000 руб. *; cd код 346, ориент. цена 28 000 руб. Д. Гослинг, К. Арнольд Язык программирования Java 304 стр., в продаже ISBN 5-88782-218-Х Книга, выпускаемая по лицензии издательства Addison- Wesley, является каноническим описанием языка про- граммирования Java. Структура книги и стиль изложения напоминают «библию» программистов на С — работу «Язык программирования С» Б. Кернигана и Д. Ричи. Эта аналогия тем более оправдана, что один из авторов «Языка программирования Java» (Д. Гослинг) известен как основной разработчик этого языка. Книга в равной степени может служить учебником и справочником по Java, а многочисленные упражнения позволят читателю попрактиковаться в использовании популярного языка программирования. код 408, ориент. цена 37 000 руб. * В стоимость книг включены почтовые расходы. Заказ книг наложенным платежом: 197198, С.-Петербург, а/я 619 postbook@piter-press.ru