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

Создание объектов и выполняемых систем

Специфицирование корня

После короткого экскурса в принципы проектирования вернемся назад к более насущным проблемам. Один из непосредственных вопросов: а как специфицировать корневой класс и корневую процедуру системы?

Среда разработкиEiffelStudio — позволяет задать такие свойства системы. Это часть установок проекта (Project Settings), доступная в среде через меню File —> Project Settings. Детали можно увидеть в приложении EiffelStudio.

Текущий объект и теория общей относительности

Видение выполнения системы позволяет нам понять фундаментальное свойство ОО-вычислений, которое можно было бы назвать общей относительностью, если бы этот термин не был уже использован много лет назад одним из выпускников ETH1 На всякий случай уточню, что речь идет об Альберте Эйнштейне. Главный вопрос: когда вы видите имя в классе, например, имя атрибута station в классе SIMPLE_STOP, то что оно означает в реальности?

В принципе, все, что нам известно, так это объявление и заголовочный комментарий:

station: STATION
  — Станция, которую представляет эта остановка.

Не настораживает "эта остановка"? В операторе, использующем атрибут, таком как sta-tion.setname ("Louvre"), у какой станции мы изменяем имя?

Ответ может быть только относительным. Атрибут ссылается на текущий объект, появляющийся только во время выполнения. Неформально с этой концепцией мы уже встречались. Теперь пора дать точное определение.

Определение: Текущий объект
В любой момент выполнения системы всегда существует единственный текущий объект, определяемый следующим образом.
  1. Корневой объект в момент начала выполнения является первым текущим объектом.
  2. В момент начала квалифицированного вызова x.f (...), где x обозначает объект, этот объект становится новым текущим объектом.
  3. Когда такой вызов завершается, предыдущий текущий объект вновь становится текущим.
  4. Никакие другие операции не являются причиной изменения текущего объекта.
  5. Для обозначения текущего объекта можно использовать зарезервированное слово Current.

Проследим за выполнением системы: корневой объект дается уже созданным; после возможного выполнения некоторых операций, в частности, создания новых объектов, может выполняться вызов, использующий в качестве цели один из созданных объектов. Он и становится на время текущим, в свою очередь, он может снова выполнить вызов на другой цели, которая и станет текущим объектом, и так далее. При завершении вызова предыдущий текущий объект восстанавливает свой статус текущего объекта.

 Схема выполнения программы

Рис. 6.20. Схема выполнения программы

Это отвечает на вопрос, что означает имя метода, когда оно появляется в операторах или выражениях (отличных от квалифицированных вызовов, когда оно появляется после точки, как f в x.f (...)): оно обозначает метод, применимый к текущему объекту.

В классе SIMPLE_STOP любое использование station, такое как Console.show (station.name), позволяющее отобразить имя станции остановки, означает поле "station" текущего SIMPLE_STOP объекта. Все это также объясняет слово "этот", используемое в заголовочных комментариях, вроде "Станция, которую эта остановка представляет".

Это соглашение является центральным для ОО-стиля программирования. Класс описывает свойства и поведение некоторой категории объектов. Он описывает эту цель путем описания свойств и поведения типичного представителя категории: текущего объекта.

Эти наблюдения приводят нас к обобщению понятия вызова. Рассмотрим оператор или выражение с точкой:

Console.show (station.name)  — Оператор
station.name   — Выражение

В обоих случаях вызывается компонент, примененный подобно всем вызовам, к целевому объекту: объекту, обозначающему Console в первом примере, и station — во-втором. Но каков статус у самих объектов Console и station? Они также являются вызовами, целевой объект которых — текущий объект. Фактически, можно было бы писать:

Current.Console
Current. station

где, как выше было замечено, Current обозначает текущий объект. Нет необходимости, однако, использовать квалифицированную форму вызова в таких случаях; неквалифицированная форма Console и station имеет тот же самый смысл. Определения следующие:

Определения: квалифицированный и неквалифицированный вызов
Вызов метода называется квалифицированным, если цель вызова явно указана, используя нотацию с точкой, как в x.f (args)..

Вызов называется неквалифицированным, если цель вызова не указана, таковой в этом случае является текущий объект, как в вызове f (args).

Важно осознавать, что многие выражения, чей статус до сих пор был для вас непонятен, фактически являются вызовами — неквалифицированными. Разнообразные примеры появлялись многократно:

Paris, Louvre, Line8    — В классе PREVIEW (лекция 2)
south_end, north_end, i_th     — В инвариантах LINE (лекция 4)
fancy_line     — В данной лекции

Все примеры относятся к этой категории. Инвариант класса LINE устанавливает:

south_end = i_th (1)

Это означает, что южный конец текущей линии метро является первой станцией на той же линии.

В вышеприведенном определении "текущего объекта" случай 4 говорит, что операции, отличные от квалифицированных вызовов, не изменяют текущий объект. Вот что истинно для неквалифицированных вызовов: в то время как x.f (args) делает объект, присоединенный к x, новым текущим объектом на время вызова, неквалифицированный вызов в форме f(args) не является причиной смены текущего объекта. Но это вполне согласуется с тем, что неквалифицированный вызов можно записать в квалифицированной форме Current/(args), где роль цели уже играет текущий объект.

Вездесущность вызовов: псевдонимы операций

Вызовы в наших программах фундаментальны и вездесущи. Наряду с квалифицированными вызовами в нотации с точкой, ясно говорящие о том, что имеет место вызов, простая нотация, подобная Console или Paris, также является примером вызовов — неквалифицированных.

Вызовы практически присутствуют даже в более обманчивых нарядах и масках. Возьмем, например, безобидно выглядящие арифметические выражения, подобные a + b. Вы можете предположить, что уж это выражение точно не является вызовом! Эти люди, помешанные на ОО-идеях, ничего не уважают, но всему есть предел, должно же быть что-то святое в программировании. К сожалению, предела нет. Нотация a+b формально является специальной синтаксической формой, на программистском жаргоне — "синтаксическим сахаром" — записи квалифицированного вызова a.plus (b).

Соглашение просто. В классах, представляющих базисные числовые типы — возьмем для примера класс INTEGER32 из EiffelStudio, — вы сможете увидеть методы, такие как сложение, объявленные в следующем стиле:

plus alias " + "(other: INTEGER_32) INTEGER_32 
    ...Остальная часть объявления...

Спецификация alias обеспечивает необходимый синтаксический сахар, допуская форму a + b, известную как инфиксная нотация, как синоним a.plus (b) обычной ОО-нотации с точкой.

Нет смысла ограничиваться только целыми или другими базисными типами. Предложение alias можно добавлять в объявления:

  • любого запроса с одним аргументом, такого как plus, допуская тем самым вызовы в инфиксной нотации, названной так потому, что знак операции разделяет операнды;
  • любого запроса без аргументов, например, unary_minus alias "—", допуская вызов в префиксной нотации, скажем, — a, как синоним a.unary_minus.

Допустимо, чтобы один и тот же знак появлялся как для бинарных, так и для унарных операций. Так, знак "—" появляется в унарном и в бинарном запросе, так что можно писать a — b вместо a.minus (b).

Допустимыми в alias-определениях могут быть знаки арифметических, булевских операций, операций отношения, но не только они; разрешается использовать последовательности символов, не включающие букв, цифр, знаков подчеркивания и не конфликтующие с предопределенными элементами языка. Этот механизм особенно полезен, когда создаются классы из проблемной области со своей символикой операций, что облегчает работу экспертов по данной проблематике с программным приложением.

В последующих лекциях мы увидим другие примеры того, как ОО-механизмы отступают от традиционной нотации, добавляя синтаксический сахар — нотацию с квадратными скобками, для записи доступа к элементам массива или словаря (хеш-таблицы): your_matrix [i, j] или phone_book_entry ["Jane"], представляющим аббревиатуры для вызова запросов — your_matrix.item (i, j), и phone_book_entry.item ("Jane"). Для достижения подобного эффекта достаточно объявить в обоих примерах метод, названный item, как псевдоним: item alias "[]".

Объектно-ориентированное программирование является относительным программированием

"Общая относительность" природы ОО-программирования могла на первых порах сбивать с толку, так как не позволяла полностью понимать сами по себе программные элементы — их необходимо было интерпретировать в терминах охватывающего класса.

Я надеюсь, что теперь вы лучше понимаете картину в целом и ее влияние на написание отдельных классов. Мы отказываемся от монолитности — архитектуры "все в одной программе", в пользу децентрализованных систем, сделанных из компонентов, разрабатываемых автономно и комбинируемых многими различными способами.

Кирилл Юлаев
Кирилл Юлаев
Как происходит отслеживание свободного экстента?
Федор Антонов
Федор Антонов
Оплата и обучение
Наталья Алмаева
Наталья Алмаева
Россия
Андрей Лучицкий
Андрей Лучицкий
Россия