Опубликован: 23.10.2005 | Доступ: свободный | Студентов: 4086 / 201 | Оценка: 4.44 / 4.19 | Длительность: 33:04:00
Специальности: Программист
Лекция 17:

От Simula к Java и далее: основные ОО-языки и окружения

Аннотация: Под влиянием языка Simula, введенного в 1967 г., появился ряд ОО-языков, отличающихся разнообразием подходов. Эта лекция описывает некоторые из них, привлекшие самое большое внимание: Simula, Smalltalk, C++ и другие ОО-расширения C, Java. В литературе до сих пор нет глубокого сравнительного описания важнейших ОО-языков. Цель этой лекции гораздо скромнее. Потому некоторые наиболее популярные языки описываются весьма коротко. Наша цель - изучить проблемы и концепции, находя их, где это возможно, даже если придется обратиться к менее популярным подходам. Для языка, имеющего практическую значимость, невелик риск пропустить что-то главное, поскольку многочисленные статьи и книги описывают его достаточно подробно. Настоящий риск таится в обратном: риск пропустить перспективную идею, просто потому, что поддерживающий ее язык (скажем, Simula) сейчас не так популярен. Таким образом, описание будет следовать не принципу равных возможностей в выборе примечательных языковых свойств, а принципу позитивных действий.
Ключевые слова: simula, ПО, тип данных, динамическое связывание, OLE, simulate, Smalltalk, algol-like, правильная программа, автоматическая сборка мусора, двусвязный, встроенные типы, фактический аргумент, атрибут класса, единичное наследование, redefine, polygon, triangle, полиморфизм, inspection, фактический тип, параллельный процесс, драйвер принтера, потоки управления, resume, инициализация экземпляров, detachable, децентрализовано, ограничение наследования, множественное наследование, активный объект, инициализация объекта, физическая система, список событий, reactivation, уведомление о событии, delay, Prior, целевой процесс, worker, hold, генератор псевдослучайных чисел, machinable, действующий субъект, член группы, компилятор, center, PARC, xerox parc, Lisp, метакласс, иерархия классов, Ada, синоним, приоритет операций, ожидаемое значение, переменная экземпляра, переопределение метода, super, статическое связывание, пересылка сообщений, контроль типов, figures, реализация языка, сборка мусора, статическая типизация, типизированный язык, методология программирования, Prolog, delphi, механизмы, автоматическое управление памятью, путь, Pascal, программная инженерия, flavor, MIT, атрибут, унификация, Common Lisp, Object, system, CLOS, ANSI, FORTRAN, Bell Laboratories, корректность программы, типизация, строгая типизация, assertion, предусловие, постусловие, MFC, dictionary, ссылочный тип, geek, BELLE, SUN, Java, Интернет, виртуальная машина, программа, корпорация, время выполнения, синтаксис, oberon, modula-2, equipping

В официальных описаниях языков для одних и тех же или схожих концепций используются различные термины. Мы старались применять "родные" термины, когда они отражают особенности языка. Однако для простоты и согласованности используется и терминология, принятая в книге (как попытка унификации), когда различия не так важны. Например, мы говорим о подпрограммах, процедурах и функциях в языке Simula, хотя соответствующие термины в официальном использовании Simula - процедура, нетипизированная процедура и типизированная процедура.

Simula

Simula - это несомненный основатель Дома Классов (Дворца Объектов). Создание его было завершено (если не принимать во внимание небольшие более поздние обновления) в 1967 г. В это, возможно, трудно поверить: оформившийся ОО-язык существовал и был реализован до структурного программирования, до публикации Парнасом статей по скрытию информации, задолго до появления фразы "абстрактный тип данных". Война во Вьетнаме еще освещалась в газетах, мини-юбки еще могли возбуждать, а на северных берегах Балтики несколько удачливых разработчиков ПО, ведомые горсткой мечтателей, уже использовали силу классов, наследования, полиморфизма, динамического связывания и других прелестей ОО.

Основные понятия

Simula, по существу, является второй попыткой. В начале 60-х был разработан язык, известный как Simula 1, для поддержки моделирования дискретных событий. Хотя он не был ОО-языком в полном смысле термина, но суть он уловил. Собственно Simula - это Simula 67, созданный в 1967 г. Далом и Нигардом (Kristen Nygaard, Ole-Johan Dahl) из Университета Осло и Норвежского Компьютерного Центра (Norsk Regnesentral). Нигард потом объяснял, что решение сохранить название отражало связь с предыдущим языком и с сообществом его пользователей. К несчастью, это название долгое время для многих людей создавало образ языка, предназначенного только для моделирования событий, что было довольно узкой областью приложения, в то время как Simula 67 - это общецелевой язык программирования. Единственные его компоненты моделирования - это набор инструкций и библиотечный класс SIMULATION, используемый небольшим числом разработчиков Simula.

Название было сокращено до Simula в 1986 г., текущий стандарт датируется 1987 г.

Доступность

Simula часто представляется как респектабельный, но более не существующий предок. В действительности он еще жив и используется небольшим, но восторженным сообществом. Определение языка поддерживается Группой Стандартов Simula (Simula Standards Group). Существуют компиляторы и ПО от нескольких компаний, в основном скандинавских.

Основные черты языка

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

Simula - ОО-расширение языка Algol 60. Большинство правильных программ на Algol также являются правильными на Simula. В частности, основные структуры управления такие же, как в Algol: цикл, условный оператор, переключатель (низкоуровневый предшественник команды case в Pascal). Основные типы данных (целые, действительные и т. д.) тоже взяты из Algol.

Как и Algol, Simula использует на самом высоком уровне традиционную структуру ПО, основанную на понятии главной программы. Выполняемая программа - это главная программа, содержащая ряд программных единиц (подпрограмм или классов). Программная среда Simula поддерживает независимую компиляцию классов.

Simula использует структуру блока в стиле Algol 60: программные единицы, такие как классы, могут быть вложены друг в друга.

Все реализации Simula поддерживают автоматическую сборку мусора. Есть маленькая стандартная библиотека, включающая, в частности, двусвязные списки, используемые классом SIMULATION, изучаемым далее в этой лекции.

Как и в нотации этой книги, большинство общих сущностей, не относящихся к встроенным типам, обозначают ссылки на экземпляры класса, а не сами экземпляры. Однако это их явное свойство, подчеркиваемое нотацией. Тип такой сущности объявляется как ссылочный ref(C), а не просто C, для некоторого класса C. Для них используются специальные символы для присваивания, проверки на равенство и неравенство ( :-, ==, =/= ), в то время как целочисленные и действительные операнды используют другие символы для этих целей ( :=, =, /= ). Выше в одной из лекций даны обоснования за и против этого соглашения.

Для создания экземпляра используется выражение new, а не команда создания:

ref (C) a; ...;a :- new C

Выражение new создает экземпляр C и возвращает ссылку на него. Класс может иметь аргументы (играющие роль аргументов процедур создания в нашей нотации):

class C (x, y); integer x, y
    begin ... end;

В этом случае при вызове new следует передать соответствующие фактические аргументы:

a :- new C (3, 98)

Аргументы могут использоваться в подпрограммах класса. Но в отличие от возможности использования нескольких команд создания (конструкторов), данный подход дает только один механизм инициализации.

Кроме подпрограмм и атрибутов, класс может содержать последовательность инструкций, тело класса. Если тело существует, то вызов new будет выполнять его. Мы увидим, как использовать эту возможность, чтобы заставить классы представлять не пассивные объекты, как в большинстве других ОО-языков, а активные элементы, подобные процессам.

Механизм утверждений в языке не поддерживается. Simula поддерживает единичное наследование. Вот как класс B объявляется наследником класса A:

A class B;
    begin ... end

Для переопределения компонента класса в классе наследника, нужно просто задать новое объявление. Оно имеет приоритет над существующим определением (эквивалента оператора redefine нет).

Первоначальная версия Simula 67 не имела явных конструкций скрытия информации. В последующих версиях, компонент, объявленный как protected, недоступен клиентам. Защищенный компонент, объявляемый как hidden, недоступен потомкам. Незащищенный компонент может быть защищен потомком, но защищенный компонент не может экспортироваться потомками.

Отложенные компоненты задаются в форме "виртуальных подпрограмм", появляющихся в параграфе virtual в начале класса. Нет необходимости объявлять аргументы виртуальной подпрограммы, как следствие, разные эффективные определения виртуальной подпрограммы могут иметь разное число и типы аргументов. Например, класс POLYGON может начинаться так:

class POLYGON;
    virtual: procedure set_vertices
begin
    ...
end

позволяя потомкам задавать различное число аргументов типа POINT для set_vertices: три - для TRIANGLE, четыре - для QUADRANGLE и т. д. Эта гибкость подразумевает некоторую проверку типов во время выполнения.

Пользователям C++ следует опасаться возможной путаницы: хотя С++ был инспирирован Simula, он использует другую семантику virtual. Функция С++ объявляется виртуальной, если целью является динамическое связывание (как отмечалось, это один из самых противоречивых аспектов С++, разумнее динамическое связывание подразумевать по умолчанию). Виртуальным процедурам Simula соответствуют "чистые виртуальные функции" C++.

Simula поддерживает полиморфизм: если B - потомок A, присваивание a1 :- b1 корректно для a1 типа A и b1 типа B. Довольно интересно, что попытка присваивания почти рядом: если тип b1 является предком типа a1, присваивание будет работать, если во время выполнения объекты имеют правильное отношение соответствия - источник является потомком цели. Если соответствия нет, то результатом будет ошибка во время выполнения, а не специальная величина, обнаруживаемая и обрабатываемая ПО (как при попытке присваивания). По умолчанию связывание статично, за исключением виртуальных подпрограмм. Поэтому если f - не виртуальный компонент, объявленный в классе A, a1.f будет обозначать A версию f, даже если есть другая версия в B. Можно при вызове насильно задать динамическое связывание через конструкцию qua1Qua (лат.) - где, через, с помощью., как в:

(a1 qua B). f

Конечно, теряется автоматическая адаптация операции к ее целевому объекту. Однако можно получить желаемое поведение динамического связывания (его можно считать изобретением Simula), объявляя полиморфные подпрограммы как виртуальные. Во многих рассмотренных примерах полиморфная подпрограмма не была отложенной, но имела реализацию по умолчанию с самого начала. Для достижения того же эффекта разработчик Simula добавит промежуточный класс, где подпрограмма виртуальна.

В качестве альтернативы использования qua, инструкция inspect дает возможность выполнять различные операции на сущности a1, в зависимости от фактического типа соответствующего объекта, обязательно представляющего собой потомка типа A, объявленного для a1:

inspect a1
    when A do ...;
    when B do ...;
    ...

Этим достигается нужный эффект, но лишь при замороженном множестве потомков класса, что вступает в конфликт с принципом Открыт-Закрыт.