Данный файл является частью Руководства по TADS для авторов игр.
Copyright © 1987, 1996 Майкл Дж. Робертс (Michael J. Roberts). Все права защищены.

Руководство было преобразовано в формат HTML Н. К. Гайем (N. K. Guy), компания tela design.

Перевод руководства на русский язык - Валентин Коптельцев


Глава вторая


Об объектно-ориентированном программировании

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

Несмотря на такую путаницу, термин никак нельзя назвать бессмысленным. В этой главе мы попытаемся выяснить, что же все-таки собой представляет объектно-ориентированное программирование и как оно соотносится с TADS. В этой главе также будет рассказано о преимуществах объектно-ориентированного подхода именно для написания приключенческих игр.

Данная глава не является обязательной для изучения TADS, но она может помочь вам в понимании ряда общих идей, которые оказали влияние на структуру всей системы. Некоторые читатели пропускают эту главу при первом прочтении данного руководства и возвращаются к ней позже, уже получив общее представление о языке.


Основные принципы объектно-ориентированного программирования (ООП)

TADS является объектно-ориентированным языком программирования. Объектно-ориентированные языки программирования во многих отношениях похожи на "процедурные" языки, такие, как Си и Паскаль, но используют иной подход к решению проблем. Благодаря этой смене подхода ООП эффективно для построения целого ряда различных классов приложений, однако наибольший эффект достигается при разработке симуляторов. Текстовые квесты по своей сути также являются симуляторами.

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


Сталкивающиеся шары

Пусть требуется создать физическую модель, описывающую сталкивающиеся шары разных размеров. Традиционный подход к решению этой задачи примерно таков: определяется набор данных, описывающих каждый шар (например, его координаты, массу и ускорение); каждому шару присваивается уникальный идентификатор (например, организуется массив, значение индекса которого соответствует номеру шара), который позволит отличать каждый из шаров от всех других. Наконец, пишется подпрограмма с названием, скажем, bounce (столкновение); эта процедура должна на основе номера шара и его начальных параметров соответствующим образом изменять данные, описывающие шар.

В отличие от традиционного подхода объектно-ориентированная версия программы моделирует каждый из шаров посредством объекта. При этом объект, соответствующий конкретному шару, содержит не только его параметры, но и весь код, описывающий поведение шара при различных взаимодействиях. Так, каждый шар будет иметь собственный метод bounce (методом в объектно-ориентированном программировании называется подпрограмма, поставленная в соответствие конкретному объекту или группе объектов). Вместо того, чтобы вызывать подпрограмму bounce с аргументом, определяющим, скажем, шар № 3, необходимо будет передать объекту "шар № 3" сообщение, предписывающее ему выполнить столкновение ("посылка сообщения" - это принятый в ООП термин, означающий вызов метода).


Наследование и замена

Может показаться, что объектно-ориентированный подход приводит к увеличению трудозатрат, поскольку одну и ту же подпрограмму необходимо определить для каждого объекта. К счастью, в ООП предусмотрено средство, позволяющее избежать подобной рутинной работы. Это средство называется наследование. Объект может быть определен как принадлежащий какому-либо классу (называемому родительским для данного объекта), что позволит ему перенять (унаследовать) все методы и данные, определенные для этого класса. Разумеется, этот объект может определять и собственные данные и методы, которые могут дополнять или изменять унаследованные атрибуты. Когда объект определяет методы или данные, которые уже определены его родительским классом, определения, сделанные объектом, заменяют унаследованные.

Польза такого способа наследования и замены определений состоит в упрощении представления общих и специфических условий. Например, если имеется двадцать совершенно одинаковых мячей, а двадцать первый мяч по-другому ведет себя при столкновении (но во всем остальном он также идентичен предыдущим двадцати шарам), то сначала вы определите класс ball (шар), а все объекты, соответствующие двадцати одному шару, сделаете наследниками этого класса. Однако для двадцать первого шара вы определите собственную версию метода bounce. При традиционном подходе вам потребовалось бы вводить дополнительную проверку в подпрограмме bounce, чтобы установить, о каком именно шаре идет речь, и действовать соответственно.

Может показаться, что одно от другого мало чем отличается, но на самом деле объектно-ориентированный подход дает одно важное преимущество: при его использовании код, относящийся к специальным условиям, локализуется в соответствующем объекте. В то время как структурный язык программирования позволяет разбивать на модули код, объектно-ориентированные языки позволяют таким же образом распределять по модулям целые объекты - как их состояние, так и поведение. Большинство считает, что это облегчает написание кода, поскольку о специальных условиях приходится вспоминать только один раз вместо того, чтобы проверять все подпрограммы на предмет необходимости их изменения. Это бывает особенно удобно при отладке и доработке программ, поскольку весь код, относящийся к конкретному объекту, локализован в одном месте.

Обратите внимание, что, поскольку класс, в свою очередь, может быть членом другого класса, специализацию ("дерево") объектов можно продолжать до бесконечности. Например, можно определить два класса - rubberBall (резиновый шар) и billiardBall (бильярдный шар), каждый из которых будет наследовать свойства более общего класса ball, а также определять свои собственные. После этого можно, например, определить десять объектов для каждого класса, при этом данные объекты и сами могут определять собственные свойства и методы, заменяющие свойства и методы своих родительских классов.


Приключенческие игры как объектно-ориентированные приложения

Объектно-ориентированные языки программирования как нельзя лучше подходят для написания приключенческих игр. Когда игрок вводит команду в игре на TADS, система посылает соответствующие сообщения объекту или объектам, упоминающимся в команде. При этом облегчается задача определения классов объектов, которые реагировали бы тем или иным образом на соответствующие команды игрока. Например, объекты, которые игрок не может взять (такие, как телефонные будки или наковальни) отвечают на команду "взять" сообщением типа "Этого вы взять не можете", в то время как объекты, которые можно взять, будут при выполнении этой команды помещаться в инвентарь игрока. Файл основных определений advr.t содержит большое количество базовых классов, но подлинная сила TADS состоит в возможности определять собственные классы.


TADS и другие объектно-ориентированные языки программирования

Пользователи, имеющие опыт работы с другими объектно-ориентированными языками программирования (Си++ или Smalltalk), наверняка обратят внимание на то, что в TADS используется несколько иной подход к ООП. В частности, в TADS значительно слабее, чем в других языках, выражено различие между классами и объектами.

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

В TADS нет такого различия между классами и объектами, т. е. класс также представляет собой объект. Поэтому от родительского объекта/класса можно унаследовать как структуру данных, так и конкретные значения. Единственное различие между классом и обычным объектом в TADS заключается в том, что классы игнорируются синтаксическим анализатором команд игрока. Есть и еще одна особенность: лексические свойства объектов наследуются только от классов.

Для наглядной иллюстрации данного различия вернемся к нашему примеру со сталкивающимися шарами. Если у нас имеется двадцать шаров с одинаковой массой (скажем, 1 кг), то в обычном объектно-ориентированном языке вы сможете определить соответствующий шару класс (ball), в котором будет предусмотрено поле для указания массы шара (скажем, mass), однако само значение этой массы указано не будет. Поэтому при написании программы вам придется создать двадцать объектов, принадлежащих классу ball, и тем или иным способом присвоить полю mass каждого из них значение 1 кг. В TADS вы можете указать значение поля mass, равное 1 кг, только один раз - при определении класса ball, и все объекты, определенные как принадлежащие этому классу, унаследует значение этой массы автоматически.

Такое отличие подходов ведет к разным стилям программирования. Программа в TADS в основном состоит из определений объектов (экземпляров классов). Все экземпляры определены в программе со значениями свойств. В программах же на Си++ и Smalltalk обычно в основном определяются классы, а объекты создаются динамически.

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

Самая благородная функция любого объекта - быть предметом изучения.
МИГЕЛЬ ДЕ УНАМУНО (MIGUEL DE UNAMUNO), Туман (1914)


Глава первая Содержание Глава третья