/
Автор: Телло Э.
Теги: аналитическая химия программирование программное обеспечение языки программирования операционная система windows
ISBN: 5-88182-015-0
Год: 1993
Текст
Объектно-ориентированное
программирование
в среде Windows
OBJECT-ORIENTED
PROGRAMMING
FOR WINDOWS ™
Ernest R. Tello
John Wiley & Sons, Inc.
New York e Chichester • Brisbane ■ Toronto ■ Singapore
ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ
ПРОГРАММИРОВАНИЕ
В СРЕДЕ WINDOWS
Эрнест Телло
Перевод с английского
Д. М. Арапова
А. К. Петренко
Москва
Издательство ВЫСШАЯ ШКОЛА
Издательство НАУКА-УАЙЛИ
Акционерное общество AKME
1993
ББК 24.4.1
T31
Рецензенты: И. С. Кондратьев, В. А. Семичев
Телло Э. Р.
T31 Объектно-ориентированное программирование в среде
Windows: Пер. с англ.— М.: Наука-Уайли, 1993. — 3 47c.:
ил. — Пер. изд.: Tello, Ernest R. Object-OrientedProgramming^
for Windows/ Covers Windows З.х. John Wiley & Sons, Inc.
США, 1991. — ISBN 5-88182-015-0: 50000 экз.
Система Windows фактически стала стандартом для IBM PC АТ и после¬
дующих моделей. Однако программирование в среде Windows является
весьма непростым делом. Одним из перспективных методов снижения слож¬
ности разработки программ в Windows является использование технологии
объектно-ориентированного программирования (ООП).
Наряду с понятиями и методами ООП в книге описаны возможности
MS-Windows ЗсХ. Изложение иллюстрировано множеством примеров про¬
грамм на языке Актор, который воплощает в себе лучшие черты классиче¬
ского языка ООП Смолток и языка Си.
Для разработчиков пользовательских оконных интерфейсов в различных
системах, преподавателей и студентов ВУЗов, изучающих ООП.
2402010000 - 109 без объявл. ББК 24.4.1
001(01) - 93
ISBN 0-471-52754-8 (англ.) © Copyright by John Wiley & Sons,
Inc., 1991
ISBN 5-88182-015-0 (рус.) © Д. М. Арапов, А. К. Петренко,
перевод, примечания, 1993
■ ПРЕДИСЛОВИЕ
Эта книга написана в помощь как начинающим, так и более
опытным программистам. Ясные и подробные объяснения призва¬
ны помочь новичкам войти в курс дела, не испытывая при этом
неуверенности и страха. Основные подходы проиллюстрированы
примерами. Воспользовавшись ими, неофит получает работающую
схему, на базе которой он может создать собственную программу.
Этому весьма способствует высокая степень модульности, прису¬
щая объектно-ориентированному программированию. Для более
опытного программиста предлагаются все приемы использования
объектно-ориентированного подхода, дающие доступ к средствам
оболочки Windows.
■ ЗАЧЕМ НУЖНО ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ
ПРОГРАММИРОВАНИЕ ДЛЯ WINDOWS?
Для миллионов машин с операционной системой MS DOS оболочка
Windows стала стандартом de facto. В настоящее время в среду
Windows перенесены наиболее важные прикладные программы,
такие, как Excel, Pagemaker, Designer, Ami, Corel Draw, и сотни
других. Одновременно широко стал применяться объектно-ориен¬
тированный подход. Такие фирмы, как Lotus, AT&T, Microsoft,
Borland, Apple и Next, восприняли его как самую современную на
сегодняшний день методику программирования.
Глава 1 дает обзор ключевых аспектов функционирования окру¬
жения Windows. В ней содержатся примеры различных программ,
написанных для Windows, и обсуждаются некоторые технические
аспекты, скажем, различия управления памятью в разных режи¬
мах: реальном, стандартном и усовершенствованном 386.
Глава 2 рассматривает достоинства (а их множество) объектно-ори¬
ентированного подхода и его недостатки (их всего несколько).
Преимущества обусловлены в основном высокой модульностью,
6
Предисловие
присущей объектно-ориентированному подходу. Объектно-ориен¬
тированный подход упрощает жизнь в основном программисту,
однако часть преимуществ напрямую доступна и пользователю. В
дальнейшем я перечислю и объясню характерные особенности
объектно-ориентированных систем, а также покажу значение каж¬
дого из них для всего подхода в целом.
Затем рассматриваются такие инструменты объектно-ориентиро-
ванного программирования, как Views (фирма CNS), предназна¬
ченный для разработки прикладных программ для Windows на
объектно-ориентированном диалекте Си, язык Actor компании
WhiteWater Group, С++ фирмы Zortech. Си в настоящее время —
наиболее популярный язык для создания коммерческих программ,
поэтому вполне закономерно уделить одну главу объектно-ориен¬
тированному Си, а также рассмотреть другие известные диалекты:
С++, Objective-С и CTalk. Приводится несколько примеров про¬
грамм на объектно-ориентированном Си. Предполагается, что чи¬
татель не будет пользоваться каким-либо одним определенным
диалектом, поэтому специальные разделы посвящены переводу
программ с одного объектно-ориентированного диалекта Си на
другой. В ряде случаев, когда это не совсем просто, приводятся
версии одной программы для разных диалектов.
Поскольку Windows — графическая оболочка, она позволяет
включать, в прикладную программу различную графику, начиная
с диаграмм, и кончая чертежами и мультипликацией. Графика
становится все более важным компонентом современных про¬
грамм. Объектно-ориентированный подход уже оказал значитель¬
ное влияние на программирование графических систем. Отдельная
глава посвящена применению объектно-ориентированного подхо¬
да для решения задач, специфичных для среды Windows.
Пользовательский интерфейс в средах, подобных Windows, стано¬
вится одной из наиболее трудоемких частей программ. Объектно¬
ориентированный подход особенно ценен тем, что минимизирует
эту часть работы за счет повторного использования отдельных
частей программ и адаптации существующего кода. Windows
способствует применению объектно-ориентированного подхода,
поскольку с самого начала естественно представлять в виде клас¬
сов такие кирпичики пользовательского интерфейса, как диалого¬
вые окна, кнопки, меню и т.п., и создавать на их базе
специализированные элементы, пригодные в разных случаях.
Рассматриваются также подходы к расширению средств пользова¬
тельского интерфейса Windows.
Продолжит разговор о технике рассказ о системном подходе к
разработке объектно-ориентированных программ. Пользователя
следует предостеречь от искусственной подгонки задачи под одну
Зачем нужно объектно-ориентированное программированиедля Windows? 7
из известных моделей и воодушевить на поиски нестандартных
решений. Вместе с тем, для любых применений важен набор
принципов, разработанных в последние 10 лет. Акцент будет
сделан на тех аспектах объектно-ориентированного проектирова¬
ния, которые напрямую связаны с повышенной степенью модуль¬
ности, характерной для объектно-ориентированных инструментов
среды Windows.
Продвинутая техника программирования для Windows предпола¬
гает умение использовать информацию, содержащуюся в конфи¬
гурационных файлах WDJJNt создавать специализированные
версии стандартных объектов Windows и прикладные программы,
выполняющиеся при нажатии на клавишу мыши на любом из
предназначенных для них файлов данных. Также важно уметь
правильно выбирать параметры управления памятью.
Одна из наиболее трудных проблем при разработке пользователь¬
ского интерфейса состоит в правильном встраивании приложения
в программную среду. Ее автоматическому решению способствует
объектно-ориентированное программирование. С помощью ис¬
пользования набора классов можно определять экспортируемые
методы, необходимые для широкого спектра прикладных про¬
грамм. Последние могут адаптировать эти методы по мере необхо¬
димости.
При создании больших программных проектов преимущества объ¬
ектно-ориентированного подхода становятся особенно очевидны.
Пример такого проекта приведен в гл. 9. Эта программа дополняет
окружение Windows и значительно расширяет сферу его примене¬
ния. Приводя пример программы, который читатели могут исполь¬
зовать непосредственно, я рассчитываю увеличить число ее
пользователей. Поскольку читатели обычно используют примеры
в качестве образца, помощь в разборе примера сделает более
вероятным то, что они возьмутся за его адаптацию и развитие. Это
тот случай, когда достоинства объектно-ориентированного подхода
становятся видимыми конечному пользователю.
Ключевой особенностью окружений типа Windows является их
способ управления памятью. Здесь объектно-ориентированный
подход также важен, поскольку модульное построение про¬
грамм — необходимая предпосылка для эффективного управления
памятью в Windows. Объектно-ориентированный подход дает про¬
граммисту максимум средств для модульного проектирования и
кодирования.
Глава 1
Ш ВВЕДЕНИЕ
В WINDOWS 3.0
Новая версия графического интерфейса пользователя (GUI —
Graphic User Interface) сводит до минимума различия между
стилями программного обеспечения наиболее известных компью¬
теров, лишая смысла высокие цены на одни из них. Пакет
Windows способен покорить всех пользователей от новичков до
асов, в том числе и программистов.
Windows предлагает новый стандарт графического интерфейса
пользователя для недорогих компьютеров. Windows стал единой
программой, заменив три существовавшие ранее. Сейчас пришло
время появления новых важных приложений, способных занять
место рядом с редактором Word for Windows, деловой программой
Excel и системой настольной типографии PageMaker. В отличие от
Presentation Manager для OS/2 Windows не требует ни покупки
дополнительной аппаратуры, ни изучения новой операционной
системы. В настоящее время почти все важные прикладные про¬
граммы перенесены или переносятся в Windows. Огромное число
машин, на которых работает это окружение, и почти отсутствующее
функциональное различие между ним и графической средой компь¬
ютеров Macintosh гарантируют его популярность в 90-х годах.
Эта глава содержит большинство сведений о среде Windows и тех
ее возможностях, которые необходимы пользователю. В ней гово¬
рится о трех режимах функционирования Windows и о том, когда
и как их использовать. Приводятся основные концепции и прин¬
ципы, включая концепцию графического интерфейса пользовате¬
ля. Наконец, дается обзор основных средств Windows и их
применений на практике. Делается попытка объяснить, чем удоб¬
ны оконные интерфейсы вообще и Windows в частности и дается
описание, как с ними работают. Рассматриваются понятие пиктог¬
раммы @соп) и отличия пиктограмм от окон (Window), а также
Многозадачность
9
различные типы окон и способы взаимодействия с ними при
помощи манипулятора ”мышь” и клавиатуры. Говорится также о
линиях прокрутки (Scroll bars), о распахивании и сжатии окон, о
работе с меню. Описывается Не1р-система (контекстно-зависимая
система подсказки) оболочки Windows.
■ ЧТО НОВОГО В ВЕРСИИ 3.0
Достаточно взглянуть на экран Windows, чтобы заметить, что все
электронные кнопки имеют ”тень”, из-за которой они кажутся
выпуклыми. Изменение формы тени при ’’нажатии” кнопки уси¬
ливает визуальный эффект. Старая программа MS DOS Executive
заменена двумя другими: Program Manager и File Manager. Новый
козырь среды Windows — пиктограммы. Изменены цвета. Еще
одно положительное отличие от предыдущих версий — независи¬
мость от внешних устройств. Не последнее место в новом облике
Windows занимают пропорциональные шрифты.
Для конечного пользователя Windows 3.0 — единая программа.
Покончено со специализированными версиями для машин на
основе процессоров 286 и 386. Достаточно приобрести одну про*
грамму и она будет работать на любом компьютере. Однако
запускать ее можно в одном из трех режимов: стандартном,
реальном и усовершенствованном 386. Существенно отличает эту
версию расширенная поддержка приложений, не рассчитанных на
Windows. Особенно это относится к усовершенствованному 386
режиму. Повсеместное наличие средств Help — еще одно достоин¬
ство новой версии. За счет стандартизации Help может быть создан
даже для самых небольших программ. В итоге конечный пользо¬
ватель может получить детальную информацию о каждом прило¬
жении. Появилась настоящая многозадачность. Это прежде всего
важно для тех, у кого имеется возможность работать в усовершен¬
ствованном 386 режиме.
Прежде чем стать экспертом в области программирования для
Windows, необходимо хорошо познакомиться с работой в этой среде.
Знакомство с сильными и слабыми сторонами этого окружения
позволит вам научиться писать более эффективные программы.
■ МНОГОЗАДАЧНОСТЬ
Чтобы появилось окно Task List (список задач), достаточно уста¬
новить курсор в любую точку фона экрана и дважды нажать
кнопку мыши. В этом окне перечислены все выполняющиеся в
данный момент процессы Windows. Над этим списком можно
выполнять различные действия, однако самая типичная опера¬
ция — это выбор прикладной программы и вытаскивание ее окна
10
Введение в Windows 3. О
наверх. Для большинства пользователей Task List становится
привычной деталью сеанса работы в Windows. Особенность новой
версии: в закрытом ящике в левом углу каждого окна появилась
опция Switch to (переключить на), которая служит для переклю¬
чения управления на любую загруженную в данный момент про¬
грамму Windows. Столь простое переключение с одной прикладной
программы на другую порождает эффект слияния приложений в
одну общую программу.
■ МЕНЕДЖЕР ПРОГРАММ (PROGRAM MANAGER)
Program Manager при помощи пиктограмм изображает доступные
группы программ. Эти группы могут быть открыты, превращаясь
в окна, которые содержат пиктограммы, соответствующие при¬
кладным программам. Как и другие окиа, окна групп могут менять
размер. Имеются два способа автоматической расстановки окон: в
виде каскада и вплотную друг к другу, без перекрытий (подобно
кирпичной кладке или паркету). Стандартный, поставляемый с
Windows набор состоит из групп Main, Accessories, Games,
Windows Applications и Non-Windows Applications. Однако поль¬
зователь может изменить этот набор. Когда в менеджере программ
открыто более одной группы, соответствующие им окна могут быть
расставлены в виде каскада или в виде паркета (tile).
■ ОСНОВНАЯ ГРУППА (MAIN GROUP)
В эту группу первоначально входят утилиты File Manager, Print
Manager, Clipboard, Dos Prompt, Windows Setup и Control Panel.
Чтобы создать новую группу, достаточно выбрать опцию New в
меню File и поименовать группу. Если вы не сообщили имя файла,
Windows создаст его самостоятельно. Включение программ в груп¬
пу происходит похожим образом: необходимо указать только имя
выполняемого файла и директория. Для программ Windows в
”папке” группы немедленно появится пиктограмма этой програм¬
мы. Если при открытии приложения вам понадобится открыть
документ или другой файл данных, можно воспользоваться оп¬
цией Run меню File. В этом случае вы просто вводите командную
строку с именем загружаемого файла, как если бы это была
обычная программа DOS, вызванная из командной строки.
■ МЕНЕДЖЕР ФАЙЛОВ (FILE MANAGER)
File Manager служит для обычной работы с файлами и для созда¬
ния и модификации групп программ. Прикладная программа
может быть вызвана как из менеджера программ, так и из менед¬
жера файлов. Нажатие кнопки мыши на пиктограмме папки
Менеджер файлов (File Manager)
11
(слева от имени исполняемого файла) запустит соответствующее
приложение.
Подобно менеджеру программ, менеджер файлов позволяет стро¬
ить каскад окон или расставлять их в виде паркета. Последний
способ весьма полезен для одновременного показа исходного и
целевого директориев при копировании и перемещении группы
файлов. Удобство использования паркетного расположения окон
несколько снижается из-за того, что Windows требует, чтобы для
каждого открытого поддиректория родительские директории были
также открыты, что приводит к загромождению экрана. Чтобы
сделать исходный и целевой директории более доступными, вам
придется вручную собирать, перемещать порожденные окна, из¬
меняя их размеры.
Один из способов уменьшения ’’толчеи” на экране состоит в том,
чтобы воспользоваться средством раскрытия поддиректориев до
нужной степени в дереве директориев. Те из директориев, которые
содержат поддиректории, помечены знаком ”+” на пиктограмме
’’папки”. Единственное нажатие кнопки мыши на этом знаке
раскрывает директорий таким образом, что становятся видны его
непосредственные поддиректории. Двойное нажатие на пиктог¬
рамме, как и прежде, породит отдельное окно для выбранной
директории. По-другому раскрыть несколько уровней директориев
можно, воспользовавшись опциями Expand One Level (Раскрыть
один уровень), Expand Branch раскрыть связь), Expand All рас¬
крыть все) и Collapse Branch (Свернуть связь) из меню Tree
(Дерево).
Может оказаться полезным счетчик свободного пространства на
диске, расположенный в нижней части окна с корневым директо-
рием. Удобную возможность предоставляет команда Move (Пере¬
местить), позволяющая перемещать файлы, не используя команд
Сору и Delete. При выполнении операций над группами файлов по
умолчанию для каждого из них используются диалоги подтверж¬
дения. Появление соответствующих диалогов может быть подав¬
лено с помощью одного из пунктов меню Options. File Manager
позволяет копировать директории целиком. Для этого достаточно
выбрать директорий, в меню File указать функцию копирования
и сообщить имя директория, в который требуется копировать.
File Manager позволяет ассоциировать файлы данных с тем или
иным приложением. При выборе файла с указанным расширением
автоматически вызывается прикладная программа с данным фай¬
лом в качестве параметра. Наиболее частый повод для редактиро¬
вания файла WIN.INI состоит как раз в построении таких
ассоциаций.
12
Введение в Windows 3. 0
Управление принтерами осуществляется через Control Panel (па¬
нель управления). Когда открыто окно Control Panel, пользователь
выбирает пиктограмму Printers и открывает соответствующее ди¬
алоговое окно. В этом окне можно задать тип принтера, выбрав
его из списка поддерживаемых принтеров или указав одну из
опций Generic СРодовой) или Unlisted Printer (Не из списка).
Аналогичным образом выбор пиктограммы Font (Шрифт) позво¬
ляет установить шрифт.
Среда Windows может многими способами адаптироваться к по¬
требностям и привычкам пользователя. Большинство из них будут
рассмотрены в этой главе, исключение составит лишь управление
памятью, которое будет описано в гл.10. Прежде всего я объясню
назначение четырех инициализационных файлов Windows. Будет
рассказано также об использовании средств Control Panel и Macro
Recorder для учета специфических требований к окружению.
Control Panel дает пользователю возможность создать оригиналь¬
ный фон для Windows и свою собственную схему использования
цветов. Это напоминает оформление интерьера для конторы или
квартиры. Здесь же могут быть выбраны и добавлены новые
шрифты, установлены текущие время и дата, а также режим
работы принтеров.
Windows поставляется с набором мощных вспомогательных про¬
грамм, подчеркивающих достоинства этой среды. Все эти програм¬
мы, за исключением Cardfile и Setup, будут рассмотрены далее.
Итак, перед нами: Clock (часы), Paintbrush (редактор картинок),
Notepad (записная книжка), Calendar (календарь), Clipboard (кар¬
ман), Terminal и PJF Editor (редактор файлов типа PD?).
■ КАРМАН (CLIPBOARD)
Clipboard — одно из самых важных средств Windows, с его
помощью можно переносить данные и изображения из одной
программы в другую. Присутствие Clipboard отмечено соответст¬
вующей пиктограммой на экране. Пиктограмма раскрывается в
виде окна, позволяющего просматривать, но не редактировать его
содержимое. Редактирование запрещено, поскольку обычно требу¬
ет сложных механизмов поддержания целостности. Для редакти¬
рования следует использовать другие инструменты. Содержимое
Clipboard можно записывать на диск, это позволяет программам,
работающим в разных режимах, иметь общие данные. В этом
случае программа-источник помещает данные в Clipboard, послед¬
ний сохраняет их в виде файла на диске. После этого можно выйти
из Windows и запустить окружение в режиме, в котором работает
программа-приемник. Далее необходимо считать файл в Clipboard,
и с его помощью вклеить данные в программу-приемник. В насто¬
Калькулятор
13
ящее время Clipboard имеет средства для показа своего содержи¬
мого в различных форматах. Например, в текстовом режиме, если
вы загрузили что-либо в Clipboard, то в меню Display будут
доступными опции: Owner Display, Text и OEM Text В зависимо¬
сти от выбранной опции ваш пакет будет выглядеть по-разному.
Прикладные программы, не ориентированные на Windows, также
могут копировать информацию в Clipboard (это доступно в усовер¬
шенствованном 386 режиме). Возможен и перенос информации в
противоположном направлении. Для этого используются два ме¬
ханизма. Первый состоит в том, что, если вы находитесь в не-
Windows программе, работающей с экраном, вы можете ужать
образ этой программы на экране до пиктограммы DOS. (При этом
содержимое экрана сохранится.) Если открыть управляющее меню
для этой пиктограммы, то вы обнаружите две новые опции: Edit
редактировать) и Settings (установки). Опция Settings содержит
диалог, с помощью которого можно обеспечить просмотр текущего
экрана не-Windows программы в окне Windows. В этом окне с
помощью мыши можно пометить область, которую требуется
скопировать на Clipboard. Далее необходимо воспользоваться вто¬
рым механизмом. Выберите опцию Edit, и вы получите подменю
с опцией Сору. Вклеивание работает аналогичным образом.
■ ПЕРЕХВАТ ЭКРАНА
При необходимости Windows 3.0 позволяет в любой момент сохра¬
нить содержимое экрана. Для этого служит комбинация клавиш
Shift-PrintScreen. Вместе с тем можно перехватить и содержимое
каждого отдельного окна. Для этого следует воспользоваться ком¬
бинацией клавиш Alt-PrintScreen. Экраны данных не-Windows
программ можно перехватить с помощью Clipboard. В усовершен¬
ствованном 386 режиме можно перехватить содержимое окна, в
котором работает программа. Это относится и к графическим
программам, но реализовано в настоящее время не для всех
режимов работы дисплея. В основном это ограничение касается
режимов VGA с наибольшим разрешением.
■ КАЛЬКУЛЯТОР
Безусловно, наиболее примечательной особенностью нового каль¬
кулятора является режим научных расчетов. Не теряя результатов
вычислений, можно переключаться с обычного режима работы на
научный и обратно. В новом режиме доступно множество матема¬
тических функций. Возможны три варианта длин чисел: двойное
слово, слово и байт. Калькулятор полностью использует возмож¬
ности функциональной клавиатуры. Функциональные клавиши
реализуют тригонометрические функции, логарифм, экспоненту,
14
Введение в Windows 3.0
квадратные и кубические корни. Нажатие клавиши ”h” переклю¬
чает между обычными и гиперболическими функциями. Широкие
возможности открывает функция biverse, которая обращает по¬
следнюю использованную функцию. Ее можно рассматривать как
”Undo” для калькулятора.
Вычисленные на калькуляторе значения могут копироваться в
Clipboard и вклеиваться затем в электронные таблицы Excel или
в другое приложение. С помощью Clipboard можно копировать и
в обратном направлении. Например, полученные в электронной
таблице числа могут быть перенесены в калькулятор для последу¬
ющих вычислений.
В режиме научных расчетов калькулятор способен выполнять
статистическую обработку. Если выбрать кнопку Sta, появится
новое окно, в котором можно запоминать списки чисел и вычис¬
лять такие функции, как сумму, среднее, стандартное отклонение
и некоторые более сложные. Калькулятор будет особенно полезен
программистам, поскольку он позволяет работать в различных
системах счисления: десятичной, двоичной, шестнадцатеричной и
восьмеричной. Основание системы счисления автоматически учи¬
тывается при отображении текущего значения.
Другая вспомогательная программа Calendar отображает даты в
виде привычного календаря. Числа записаны в квадратиках и
расставлены в столбцы по неделям. Имеются несколько способов
поставить метку, благодаря которым с первого взгляда видны
даты, на которые назначены встречи. Календари рассчитаны на
неопределенный срок вперед и способны удовлетворить даже ав¬
тора фантастического романа. Летоисчисление начато, однако, с
1980 г., и нет возможности использовать даты в прошлом.
Cardfile (картотека) — это еще одна удобная программа. С по¬
мощью CHpboard в нее и из нее можно копировать графические
изображения. Это значительно расширяет возможности програм¬
мы по сравнению с записной книжкой. Если у вас имеется соот¬
ветствующая аппаратура, вы можете использовать оцифрованные
фотографии моделей, предметов, пейзажей и т.п. К сожалению,
вне зависимости от выбора цвета для области текстовых данных
возможны только черно-белые фотографии.
Чтобы получить в распоряжение приложений как можно больший
объем памяти, в зависимости от типа используемого компьютера
и его конфигурации указываются разные опции. Наличие несколь¬
ких опций, дающих при взаимодействии разные результаты,
может несколько озадачить. При проектировании управления
памятью для приложения программист должен знать все способы,
какими пользователь Windows может конфигурировать память, и
понимать, какую их комбинацию следует использовать для дости¬
Работа с числовыми данными
15
жения наилучших результатов. Программистам следует быть осо¬
бенно осторожными при использовании драйверов памяти фирмы
Microsoft, таких, как HIMEM. SYS, SMARTDRV.SYS и
EMM386.SYS, и представлять, как они влияют друг на друга и их
комбинация на вашу прикладную программу.
Программисты, разрабатывающие программы для Windows, дол¬
жны быть знакомы с графикой, используемой в этой среде. Про¬
граммисты, не знакомые с ней, могут начать обучение с программы
Paintbrush. Это единственная вспомогательная программа
Windows, использующая не одну, а две кнопки мыши. Расскажу,
как пользоваться этой программой на примере создания закончен¬
ной картинки. В процессе ее рисования использованы почти все
возможности программы, проиллюстрированы такие средства, как
сжатие и растяжение участков изображения, разработка собствен¬
ной палитры, преобразование файлов в формате Microsoft Paint и
печать картинок.
В этом разделе рассматриваются общие принципы работы с гра¬
фикой в Windows. Для окружения Windows уже существует набор
замечательных графических программ, таких, как Designer, Corel
Draw, Zing, Drafix CAD и Picture Publisher. Следует рассказать
также о нескольких базах данных для графических изображений.
Мы рассмотрим некоторые инструменты, используемые для редак¬
тирования битовых карт и пиктограмм, например Icon Editor.
Среда Windows активно используется для подготовки современ¬
ных продуктов мульти-медиа (Multi-media). Программистам, ра¬
ботающим в среде Windows, необходимо быть в курсе этих
разработок.
■ РАБОТА С ЧИСЛОВЫМИ ДАННЫМИ
Для численного анализа в Windows имеется множество превосход¬
ных средств. В их числе калькулятор с его обычным режимом и
режимом научных расчетов, упомянутая ранее электронная таб¬
лица Excel, которую можно использовать вместе с калькулятором.
Возможность создания нескольких связанных между собой окон
придает электронным таблицам дополнительное измерение. Для
выполнения повторяющихся последовательностей действий, не
обязательно локализованных в одном приложении, служит Macro
Recorder. Для работы с числовыми данными имеется мощный
продукт Microcalc фирмы Anderson Consulting. Его возможности
по достоинству оценят научные работники.
Word for Windows — это самый современный текстовый процес¬
сор. Он может служить примером программы, в полной мере
реализующей возможности Windows. Умение эффективно исполь¬
16
Введение в Windows 3. 0
зовать подобный инструмент поможет сберечь много сил и време¬
ни. Достаточно упомянуть средство Glossary, позволяющее изба¬
виться от повторного набора. В среде Windows работают и другие
текстовые процессоры, такие, как Ami и Legend. В настоящее время
текстовый процессор — это лишь один из участков производствен¬
ного цикла настольной типографии. Pagemaker — современная
программа настольной типографии, развивающаяся ”в ногу со
временем”. Будут показаны области практического применения
этой программы и обсуждены ее достоинства и недостатки. Чтобы
закончить рассказ о текстовой обработке в Windows, необходимо
рассказать о сканерах и средствах распознавания символов.
Обмен данными присутствует в Windows во всем его разнообразии:
от передачи информации и картинок средствами динамического
обмена данными (DDE) до вывода на печать. В этой главе будет
рассказано и об основных сетевых средствах. Чтобы достигнуть
наилучших результатов при печати в окружении Windows, необ¬
ходимо освоить программу Print Manager. Terminal — это еще одна
программа, используемая для связи с внешним миром. Для тех,
кто использует сеть, предназначена специальная версия програм¬
мы Setup. Процесс установки Windows для узла сети будет описан
далее. Дается описание коммуникационного программного обеспе¬
чения третьих фирм, скажем, пакета Crosstalk.
Читателю уже становится ясно, какие выгоды сулит окружение
Windows. Невозможно перечислить все способы, которыми можно
свести различные прикладные программы в единый технологиче¬
ский процесс, автоматизируя повторяющиеся действия. В качест¬
ве примера представьте себе настольную типографскую систему,
использующую Macro Recorder для переноса в Pagemaker элемен¬
тов, изготовленных при помощи Word for Windows, Paintbrush и
Excel. Обратимся к работе с графикой. С помощью Clipboard,
утилит перехвата экрана и преобразования графических форматов
можно переносить картинки из одной программы в другую, обо¬
гащая изображения специфичными для каждой из них визуаль¬
ными эффектами. Это, по-видимому, наиболее важное достоинство
среды Windows. Как бы ни старались программисты, но даже в
лучших программах не хватает какой-нибудь возможности. Ин¬
тегрированная среда, подобная Windows, позволяет комбиниро¬
вать лучшие средства всех программ в рамках одного проекта.
Реальные преимущества Windows видны при работе с превосход¬
ными программами, доступными в данной интегрированной среде.
Лучший способ проиллюстрировать эти преимущества состоит в
демонстрации приемов использования таких программ.
Управление печатью (Printer Manager)
17
■ ЗАПИСЬ MAKPO (MACRO RECORDER)
Эта утилита предназначена для записи последовательностей нажа¬
тий клавиш и операций с мышью. Такой последовательности
можно сопоставить одну клавишу и записать в файл вместе с
другими макрорасширениями. Хотя Macro Recorder способен со¬
хранять и движения мыши, следует, по возможности, избегать
таких макро, выполняя те же действия другим способом. Рекомен¬
дуется пользоваться эквивалентными последовательностями на¬
жатий на клавиатуре.
К числу удачных особенностей утилиты Macro Recorder следует
отнести возможность прервать запись макро в любой момент и
запомнить записанное. Это очень важно, поскольку в противном
случае пользователь попадает в странную ситуацию, когда ему
приходится записывать действия, связанные с перемещением в
точку прекращения записи макро. В некоторых случаях необхо¬
димо, чтобы макро переносило бы вас к экрану или окну, которое
вы только что покинули, чтобы войти в Recorder. Для этого также
служит прерывание в произвольной точке. Достаточно с помощью
Ctrl-Break прервать запись в точке, где вы хотите, чтобы макро
закончилось, при этом возникнет диалоговое окно, позволяющее
окончить процесс записи в нужной точке.
Запись макро для повторного воспроизведения в среде, в которой
активны одновременно несколько программ, — сложная задача,
поэтому имеются несколько режимов записи и воспроизведения.
Например, нужно решить, будут ли операции с мышью записы¬
ваться лишь для одного окна или для всего экрана, воспроизводить
ли последовательность с той скоростью, что и при записи, или с
максимально возможной и т.д. Recorder разработан в расчете на
запись длинных последовательностей действий, характерных для
демонстрационных программ. Безусловно, у подобных утилит име¬
ется множество заранее неизвестных разработчику применений.
Когда макро вызывается изнутри прикладной программы, на
экране может возникнуть пиктограмма, изображающая песочные
часы. Она призывает пользователя немного подождать, что может
несколько испортить демонстрацию. Если возникают подобные
проблемы, то имеет смысл разработать более эффективные макро.
■ УПРАВЛЕНИЕ ПЕЧАТЬЮ (PRINTER MANAGER)
После того как с помощью Control Panel задана конфигурация
принтера, с ним можно работать, используя Printer Manager. Эта
программа осуществляет печать в фоновом режиме, т.е. в то время,
когда идет печать, на компьютере можно выполнять другую рабо¬
18
Введение в Windows 3. 0
ту. При работе с программами, не ориентированными на Windows,
они выполняют вывод обычным способом.
При выборе пиктограммы Printer Manager раскрывается окно,
отображающее текущее положение дел с печатью. Сюда входят:
тип установленного принтера, способ его подключения к компью¬
теру, текущий статус. После имени принтера выдается список
файлов, стоящих к нему в очередь на распечатку. Для распечаты¬
ваемого в данный момент файла сообщается процент уже выведен¬
ной части текста. Манипулируя мышью, можно при желании
переупорядочить очередь файлов.
■ ПАНЕЛЬ УПРАВЛЕНИЯ (CONTROL PANEL)
В версии 3.0 Control Panel полностью переделана. Font Editor
(Редактор шрифтов) поддерживает пропорциональные шрифты.
Под набором шрифтов понимается совокупность размеров для
выбранного типа шрифта, адаптированных под определенные дис¬
плеи и принтеры. Windows поддерживает как экранные (растро¬
вые), так и принтерные (векторные) шрифты. Диалоговое окно
Fonts позволяет увидеть пример, набранный указанным шрифтом.
Выбор пиктограммы Colors (цвета) открывает диалоговое окно,
позволяющее переопределять цвета, используемые стандартно для
управляющих элементов, букв и фона текстового окна. Способ
задания цветов полностью интерактивен. На одной стороне окна
пользователь видит модель экрана Windows. На модели можно
указать элемент, для которого требуется изменить цвет. На другой
стороне этого окна расположен набор ”ящичков с краской”. Если
интересующий цвет отсутствует, то его можно изготовить, смешав
существующие. Выбранную схему использования цветов можно
сохранить в файле. В исходную поставку Windows входит несколь¬
ко таких схем на выбор.
Открыв пиктограмму Desktop, вы сможете поменять цвет ”обоев”
для экрана Windows. ’’Обои” могут иметь как регулярный рисунок,
так и представлять собой графическую картинку.
■ УПРАВЛЕНИЕ УСОВЕРШЕНСТВОВАННЫМ
386 РЕЖИМОМ
Если вы используете усовершенствованный 386 режим, то Control
Panel автоматически включает еще одну пиктограмму. Установка
конфигурации в этом режиме включает выбор порядка, в котором
прикладные программы взаимодействуют с такими устройствами,
как принтеры. Опции Control Panel предлагают способы разреше¬
ния конфликтов между прикладными программами, когда по
крайней мере одна из них не рассчитана на работу в Windows.
Инициализационные файлы
19
Назначение опции Always Warn 0Гредупреждать всегда) сообщать
пользователю о всех попытках захватить порт ввода-вывода со
стороны прикладных программ. Пользователь должен выбрать ту
единственную программу, которая получит доступ к устройству.
Опция Never Warn (Никогда не предупреждать) довольно опасна,
поскольку не исключено, что две программы доберутся до принте¬
ра или модема одновременно и в результате выдадут абракадабру.
Опция Idle позволяет задать промежуток времени, по истечении
которого порт становится доступным запросившей его задаче.
Помните, что одни принтеры могут при перезагрузке требовать
больше времени, чем другие. Остальные опции усовершенствован¬
ного 386 режима касаются многозадачности и будут рассмотрены
далее.
■ УПРАВЛЕНИЕ МНОГОЗАДАЧНОСТЬЮ
Опция Minimum Time-SHce служит для задания времени в милли¬
секундах, которое должна отработать прикладная программа,
прежде чем управление будет передано другой программе. Не имея
возможности точно замерить миллисекунды, я решил не пытаться
проверить выполнение этой опции. Опции Windows in Foreground
и Windows in Background служат для управления разделением
времени между интерактивными и фоновыми задачами. Эти пара¬
метры устанавливают пропорции между ними в квантах времени.
Обычно приложения получают по одному кванту времени. Опция
Exclusive in Foreground позволяет программам, с которыми идет
диалог, получать 100% времени.
Многозадачность Windows не распространяется на работу с дис¬
ком. Запись файлов на диск и форматирование не могут выпол¬
няться в фоновом режиме, когда вы делаете другую работу. Это
сделано для безопасности. В противном случае могут возникнуть
ситуации, когда дисковые операции не в состоянии будут успешно
завершиться.
■ ИНИЦИАЛИЗАЦИОННЫЕ ФАЙЛЫ
Имеются следующие инициализационные файлы:
CONTROL.ENtt
PROGRAM.ENTI
SYSTEM.mi
WIN.INI
WEmLE.ENTI
SYSTEM.INI и WE4.E4I — это стандартные инициализационные
файлы. SYSTEM.INI содержит информацию об аппаратной конфи¬
20
Введение в Windows 3.0
гурации, а WIN.INI — сведения о выбранных в соответствии с
набором выполняемых программ и вкусами пользователя режи¬
мах. Разделы используются для разделения режимов на группы.
Использование квадратных скобок ([]) обязательно. Для записи
каждого режима используется синтаксис: ключ = значение.
Ключ — это имя режима, представляющее собой произвольную
комбинацию букв и цифр, за которой следует знак равенства (=).
Значениями режимов могут быть целые числа, строки или строки
в кавычках в зависимости от типа соответствующего параметра.
Имеются 4 типа прикладных программ:
1. Приложения Windows 3.0.
2. Приложения для более ранних версий Windows.
3. Не-Windows программы.
4 Резидентные программы.
Приложения, рассчитанные на Windows 3.0, слегка отличаются
от приложений, подготовленных для более ранних версий этой
среды. Между собой они также различаются. Имеются некоторые
приложения, разработанные специально для защищенных режи¬
мов 286 и 386 процессоров. Чтобы упростить жизнь пользователю
и облегчить конфигурирование системы в зависимости от требова¬
ний приложений, в PJF Editor предусмотрены специальные сред¬
ства.
■ РЕДАКТОР .PIF ФАЙЛОВ (PIF EDITOR)
Каждый файл FJF состоит из двух групп опций. Одна предназначена
для реального и стандартного режимов, другая-для усовершенство¬
ванного 386 режима. PIF Editor автоматически показывает опции,
соответствующие текущему режиму. Однако при желании вы всегда
можете изменить режим изнутри редактора.
Примечательно, что файлы PJF можно использовать для .BAT
файлов DOS, если последние требуется вызывать из Windows.
Опция ’’минимум требуемой памяти” файла PIF сообщает
Windows, сколько памяти необходимо освободить для запуска
прикладной программы. При этом не говорится, сколько памяти
будет доступно, если она будет запущена. Файлы PIF можно
использовать несколькими интересными способами. Для одного
приложения можно определить альтернативный PJF файл, и сде¬
лать оба файла доступными из одной группы, позволив запускать
одну программу в разных конфигурациях.
Многозадачность включает возможность устанавливать приорите¬
ты основных и фоновых процессов, а также опцию Detect Idle Time
(Обнаружение времени ожидания). Приоритеты задаются числами
Microsoft Word for Windows
21
от 0 до 1000 и служат для разделения времени центрального
процессора. Так, например, если имеются 4 задачи с приоритетами
150, 100, 50 и 50, то, для того чтобы определить, какая часть
процессорного времени достанется каждой из задач, нужно сло¬
жить приоритеты (150+100+50+50=350), при этом каждая из
задач получит приблизительно (Приоритет/З50)*100% времени
процессора. Иными словами, если 100% времени процессора раз¬
делить на сумму приоритетов всех задач и умножить поочередно
на приоритет каждой из задач, то мы получим процент времени,
который посвятит ей процессор. Если включена опция Detect Idle
Time, то в те моменты, когда активная задача ожидает ввод с
клавиатуры, Windows будет переключаться на другие приложения.
■ КЛАВИШИ АКТИВИЗАЦИИ, ОПРЕДЕЛЯЕМЫЕ
ПРИКЛАДНОЙ ПРОГРАММОЙ
Это средство существует только в усовершенствованном 386 режи¬
ме. Windows позволяет назначить прикладной программе клави¬
шу, при нажатии которой она становится активной. Эта клавиша
работает независимо от того, в каком приложении вы в данный
момент работаете.
■ УПРАВЛЕНИЕ ПАМЯТЬЮ
Какие факторы влияют на количество памяти, находящейся в
распоряжении пользователя? В настоящее время их несколько:
• общее количество оперативной памяти в машине;
• тип памяти;
• режим, в котором работает Windows;
• установленные драйверы.
В реальном режиме Windows может использовать Expanded ото¬
бражаемую память стандарта ЬШ EMS. Если вы стартуете в
реальном режиме, Windows автоматически определяет лучший
способ использования этой памяти. С помощью опций командной
строки вы можете получить более полный контроль над управле¬
нием памятью.
■ MICROSOFT WORD FOR WINDOWS
Практическое знакомство с современной прикладной программой
даст сведения о наиболее важных аспектах программирования для
Windows. Редактор Word for Windows фирмы Microsoft — одна из
программ, в максимальной степени использующая возможности
22
Вввдвнив в Windows 3.0
среды Windows 3.0. По этой причине я расскажу вам о наиболее
интересных из предоставляемых этой программой возможностях.
Word for Windows — это текстовый процессор, которым могут
пользоваться все пишущие, независимо от профессии. В этой
программе несколько изюминок. Во-первых, она содержит встро¬
енный процессор заголовков (Outline processor). Напомню, что
процессор заголовков — это специальная программа, позволяю¬
щая создавать структуру заголовков и подзаголовков, запоминать
связанные с ними куски текста, высвечивая нужные из них по
мере надобности. Во-вторых, Word for Windows содержит боль¬
шинство средств, характерных для настольных издательских сис¬
тем.
■ РАБОТА С ЗАГОЛОВКАМИ
Начну с процессора заголовков, поскольку этим уникальным ин¬
струментом пользуются многие профессионалы. Вне зависимости
от типа документа, который вы создаете, он почти наверняка
разделен на несколько частей или сегментов. Даже если конечный
пользователь не знает о них, они все равно составляют существен¬
ную часть процесса написания. Процессор заголовков позволяет
выбирать и рассматривать лишь интересующие в данный момент
части документов. Представьте на миг, что вы пишете сценарий
для мульти-медиа. При этом вы пользуетесь структурой актов и
сцен, типичной для крупных систем. Тогда можно сделать види¬
мыми только те сцены, которые нужны для одной определенной
цели. Вам может потребоваться изменить действия одного из лиц.
В этом случае необходимо высветить только те сцены, в которых
оно появляется.
В любой момент вы можете сжать текст, оставив от него один
скелет. Вы уже могли заметить, что процессор заголовков можно
использовать во многих случаях. Для программистов он может
быть полезен, поскольку помогает сохранять структуру програм¬
мы ясной. Опция Arrange All позволяет расставлять открытые
окна в виде паркета, нажатие на кнопку мыши позволяет начать
взаимодействие с документом из любого окна.
■ NEWWAVE
Фирма Hewlett-Packard — одна из тех, чья деятельность не сво¬
дится к копированию принципов графического пользовательского
интерфейса, популяризированного фирмой Apple. В окружении
NewWave сделано несколько новых важных шагов по сравнению
с моделью, предложенной в компьютерах Xerox и Macintosh.
Безусловно, общий стиль NewWave объектно-ориентированный,
Office Tools
23
но с появлением этой системы данный термин получил дополни¬
тельный смысл. Объекты для конечного пользователе аналогичны
объектам для программиста. В NewWave объект — это файл
данных вместе с обрабатывающей его программой. Такая связь
более содержательна, чем обычная возможность: указав файл,
загрузить программу, которая использует этот файл в качестве
параметра. С помощью активных объектов Object Management
Facility производит многие операции, в том числе поддержание
связей между прикладными программами. Автоматизация упро¬
щает процесс подготовки с помощью нескольких программ состав¬
ного документа, включающего, скажем, текст, статистику и
графику. В этой среде может работать средство Agent, позволяю¬
щее записать последовательность операций в нескольких разных
приложениях и автоматизировать ее.
Если документ состоит из элементов, построенных различными
прикладными программами, например текстовым процессором,
электронной таблицей и программой построения графиков, он
называется составным. NewWave содержит еще один способ объе¬
динения объектов — используя "контейнеры”. ”Контейнер” — это
объект, состоящий из других объектов и показывающий их имена
или пиктограммы, Объект А содержит объект В, если между ними
существует связь по вложенности. Кроме этого объекты могут
иметь информационные связи. Когда несколько прикладных про¬
грамм работают с одними данными, при этом не происходит
одномоментного копирования последних. Вместо этого строятся
информационные связи. При необходимости изменить данные все
разделяющие их объекты автоматически получают обновленную
версию. Как следствие этого Object Manager для поддержания цело¬
стности должен создавать резервные копии составных объектов.
Для того чтобы иметь доступ ко всем возможностям NewWave,
прикладная программа должна быть написана в расчете на это
окружение. Однако некоторыми его свойствами могут пользовать¬
ся и приложения, специально не ориентированные на NewWave,
также есть способ улучшить взаимодействие программы со средой
NewWave с помощью процесса инкапсуляции. NewWave Office —
окно, аналогичное по своим функциям MS Windows Executive. Оно
содержит пиктограммы с изображением папок и мусорной корзины
и напоминает пользователю этим обычную конторскую обстановку.
■ OFFICE TOOLS
В NewWave имеется уникальный тип объекта — Office Tool
(конторский инструмент). Его можно рассматривать как суперобъ¬
ект. Единожды созданный он не имеет себе подобных. Конторские
инструменты отличаются от других объектов NewWave Office
24
Введение в Windows 3. 0
пиктограммами, создающими иллюзию трехмерности. Инстру¬
менты не могут создаваться или уничтожаться пользователями.
Их создание — прерогатива разработчиков.
Стандартные инструменты NewWave включают File Drawer (Ото¬
бражение файлов), WasteBasket (Корзина для использованных
бумаг), Printers и Dictionaries (Словари). File Drawer — это кон¬
тейнер, в котором могут помещаться до 200 произвольных объек¬
тов. Эти объекты обычно содержат папки, которые в свою очередь
могут содержать до 200 объектов, в том числе и другие папки.
Однако один объект может одновременно находится лишь в одной
папке. Инструмент Printers позволяет распечатать любое количе¬
ство выбранных объектов на любом из принтеров, установленных
для NewWave, в том числе и на подключенных через сеть. Инст¬
румент Dictionary используется для создания словаря, который
может использоваться совместно с основным словарем программы
проверки орфографии.
Объект WasteBasket интересен тем, что это контейнер, но исполь¬
зуется он не для хранения, а длй ’’выбрасывания”. Хотя он
позволяет уничтожить файл, но файл не исчезает немедленно, а в
течение еще некоторого времени может быть восстановлен.
К настоящему времени с помощью программистов в NewWave
перенесены некоторые впечатляющие приложения. Разумеется, в
первую очередь это уже популярные программы, такие, как элек¬
тронные таблицы Microsoft Excel. Фирма Samna предоставила
новую версию программы Ami — Ami Professional, представляю¬
щую собой мощный текстовый процессор. В число многообещаю¬
щих входят такие пакеты, как Graph Plus фирмы Micrografix,
Forest & Trees — интеллектуальная СУБД фирмы Channel
Computing, программа создания полноценной мультишшкации
Video NewWave (фирма New Media Grafix).
■ TOOLBOOK
Toolbook — это конструктор, позволяющий ”собирать” электрон¬
ные книги и другие прикладные программы, практически не
прибегая к программированию. С применением программирова¬
ния можно добиться еще более впечатляющих результатов. В
интерактивном режиме создаются все основные экраны програм¬
мы, а для расширения функциональных возможностей пишутся
утилиты. Toolbook содержит средства гипертекста, выполненные
в метафоре книги, дающей возможность пользователям, не имею¬
щим опыта разработки программ, создавать программное обеспе¬
чение, ориентированное не гипертекст. Разработке прикладных
программ непрограммистами способствует возможность восприя¬
Toolbook
25
тия экрана программы как книги — предмета для большинства
людей более привычного. Метафора книги богаче, чем метафора
картотеки, поскольку книга — это сложная структура, включаю¬
щая индекс, оглавление, библиографию и приложения. Появляет¬
ся возможность строить электронные книги, сочетающие сильные
стороны как бумажных, так и электронных носителей информации.
Программирование в стиле Toolbook особенно подходит для постро¬
ения приложений для Windows 3.0. Таким образом могут быть
построены отнюдь не все прикладные программы, но большинство
из них. Собрать прототип из готовых частей — дело нескольких
дней, в то время как на программирование могут уйти недели и
даже месяцы. Хотя это кажется преувеличением, но смею утвер¬
ждать, что это правда.
Toolbook: Объекты и сообщения
Программы в системе Toolbook строятся из объектов и сообщений,
что облегчает их разработку. Каждое приложение Toolbook содер¬
жит иерархию взаимосвязанных объектов со встроенной схемой
рассылки сообщений. Книги и другие приложения, разрабатыва¬
емые для Toolbook, конструируются в виде иерархии объектов,
посылающих и принимающих сообщения, которые отражают со¬
общения уровня Windows. На языке OpenScript можно кодировать
обработчики, ответственные за реакцию на сообщения, которые
можно послать как из других программ на OpenScript, так и из
окна команд. Хотя система объектов ТооШоок не содержит кон¬
цепции класса, а следовательно, и механизма наследования, в ней
возможны некоторые ’’трюки’*, в определенной степени компенси¬
рующие эту нехватку. Рассматриваемая в этой книге система
программирования позволяет пользоваться большинством из этих
трюков, чтобы, насколько это возможно, восполнить отсутствие
классов.
Графика и анимация
К числу наиболее привлекательных особенностей системы
Toolbook относится использование объектно-ориентированной гра¬
фики и анимации. Последняя позволяет буквально оживить элек¬
тронные книги. Движущиеся элементы существенно отличают
такие книги от обычных пассивных изданий. Они более не пред¬
ставляют собой сухих, инертных наборов текстов. Они как бы
танцуют перед пользователем на экране. В Toolbook имеется
множество способов анимации графики и текста, включая опти¬
ческие эффекты стирания и растворения. На этих эффектах по¬
строены переходы между страницами.
26
Введение в Windows 3.0
Серьезная работа с Toolbook неизбежно включает подготовку и
запись картинок в библиотеку, в которой их легко найти и из
которой просто извлечь. Изначально Toolbook включает коллек¬
цию клипов и программу-менеджер. К сожалению, эта программа
в определенной степени замкнута. Вы можете добавлять и извле¬
кать изображения и страницы, но создавать новые типы картинок
или новые программы нельзя.
Средства анимации позволяют перемещать изображения, делая их
на время невидимыми, а затем показывая в соответствии с заранее
разработанным планом или по требованию пользователя. Элемен¬
ты могут менять размер и место расположения, поворачиваться и
даже искажаться.
OpenScript
Модуль, написанный на языке OpenScript системы Tookbook, —
гибкое средство, в максимально возможной для столь простого
языка степени напоминающее английский. Чаще всего он исполь¬
зуется для написания обработчиков сообщений. Обработчик на
языке OpenScript — это фрагмент кода, определяющий действия
объекта в ответ на сообщения. Типичный пример использова¬
ния — создание для электронной кнопки программы, которая
определяет действия кнопки в ответ на сообщение ButtonUp.
Имеются три основные способа доступа программы на языке
OpenScript к ресурсам других приложений MS Windows:
1. Использование протокола DDE (Dynamic Data Exchange —
динамический обмен данными).
2. Выполнение внешних функций, записанных на языке ко¬
манд приложения.
3. Вылов функций из DLL (Dynamic Link Library — динами¬
ческих библиотек).
Следующие параграфы содержат описания этих трех техник.
Динамический обмен данными (DDE)
Обмен данными между приложениями Toolbook и Windows — это
улица с двусторонним движением. Внешние программы могут
запрашивать данные Toolbook, а приложения Toolbook — пользо¬
ваться данными других программ. Это может быть оформлено в
виде удобного протокола вида ”дай-возьми”. Так, например, в
Toolbook нет электронных таблиц, но в таких случаях вы можете
совместить средство вызова внешних функций с динамическим
обменом данных и достичь требуемого результата.
Toolbook
27
Доступ к динамически подключаемым библиотекам DLL
Одним из средств, увеличивающих мощность язьнсов сценариев
типа OpenScript (далее мы будем использовать термин ’’скрипт”),
служат динамические библиотеки Windows. DLL — это модульный
подход к совместному использованию несколькими программами
общего, единожды загружаемого кода. Каждая программа, вместо
того чтобы строить собственную копию кода, обслуживается един¬
ственной копией. Скрипт может пользоваться функциями из ди¬
намических библиотек. К Windows 3.0 прилагается пример
библиотеки PBrush.DLL, содержащей код утилиты PaintBrush.
Toolbook поддерживает одновременную работу с восемью DLL. Для
доступа к ресурсам окружения Windows 3.0 включает три таких
библиотеки, кроме этого большинство прикладных программ
включают в себя по крайней мере одну доступную извне DLL.
Внешнее выполнение
OpenScript позволяет выполнять команды в других приложениях
Windows, например в электронной таблице Excel. Благодаря этому
можно строить приложения, пересекающие границы нескольких
программных продуктов, интегрируя их в единый процесс реше¬
ния задачи. В идеале пользователь должен быть в состоянии
полностью сосредоточиться на задаче, не отвлекаясь на использу¬
емые инструменты. В настоящее время программное обеспечение
чрезвычайно ’’навязчиво” в том смысле, что пользователь должен
думать и об инструментальной программе, и о решаемой задаче.
Возрастающие интеграция и стандартизация графического интер¬
фейса позволяют сделать важный шаг к созданию окружения, не
требующего от пользователя понимания, какую программу он
использует на каждом этапе. Интерфейс высокого уровня дает
возможность не задумываться над тем, какая программа активна в
данный момент. Средство внешнего выполнения языка OpenScript
предназначено для создания таких программных систем.
Благодаря процедурам модульной разработки, многие из которых
могут быть закодированы в виде скриптов, автоматизируется
большая часть работ. Примером такой процедуры будет создание
фона для новой страницы путем модификации существующего
фона. Последний может быть отредактирован внутри той же книги
без внесения изменений в исходный фон. Хотя это и не очень
сложные процедуры, они могут оказаться не очевидными.
По-видимому, один из наиболее трудоемких процессов цикла
разработки в среде Toolbook — это выбор ключевых слов и соот¬
ветствующих им скриптов. Было бы замечательно найти способ
автоматизации процесса поиска вхождения в текст ключевых
слов. Сложность состоит в том, что на момент кодирования трудно
28
Введение в Windows 3.0
предсказать все интересующие пользователя детали. Один из
возможных подходов состоит в том, чтобы программа останавли¬
валась на всех страницах в указанном пользователем диапазоне и
спрашивала, следует ли занести в определенное поле ключевое
слово. Программу такого типа можно без труда написать на языке
OpenScript.
Электронные библиотеки
Средства окружения Windows и язык OpenScript позволяют созда¬
вать не только электронные книги, но и целые электронные
библиотеки. Пользовательский интерфейс рассчитан на поддерж¬
ку целой системы книг, скажем энциклопедии. Одно из главных
ограничений системы Toolbook — невозможность одновременного
открытия более 10 файлов. При разработке больших программ
следует помнить об этом. В приложениях, загружающих дополни¬
тельные книги, необходимо следить за тем, чтобы общее их число
не превышало 10. При необходимости загрузить дополнительно
еще одну книгу требуется спросить пользователя, какую из уже
загруженных книг следует выгрузить.
■ WINGZ: ЭЛЕКТРОННАЯ ТАБЛИЦА
НОВОГО ПОКОЛЕНИЯ
Wingz — еще одно достижение в программировании для среды
Windows. Этот продукт фирмы Informix представляет собой элек¬
тронную таблицу. Электронные таблицы — весьма консервативная
область программного обеспечения, однако Wingz обогатил ее
несколькими новшествами. Дальнейшее изложение призвано по¬
казать, что представляет собой с точки зрения пользователя гра¬
фический интерфейс Windows.
В настоящее время на рынке много мощных и привлекательных
программ, и вы можете подумать, что Wingz еще одна в этом ряду.
Однако за последние 10 лет число изобретений в области электрон¬
ных таблиц можно пересчитать по пальцам. Прогресс в основном
состоял во внесении всевозможных дополнений. Я считаю, что в
этой области имеется широкий простор для творчества, и не
удивлюсь, если увижу здесь что-либо действительно новое.
Таблицы Wingz первоначально были разработаны для компьютера
Macintosh. Сейчас они доступны на ГОМ PC под управлением
Windows 3.0 и на рабочей станции Sun. Существенный прорыв в
технологии электронных таблиц выразился в появлении следую¬
щих трех вещей:
• быстрых, гибких трехмерных таблиц;
• управляемой пользователем многоцветности;
Wingz: электронная таблица нового поколения
29
мощного языка программирования HyperScript.
Безусловно, нельзя не упомянуть пиктограммы Tool Box и
Operators, позволяющие создавать таблицы практически при по¬
мощи одной мыши.
Познакомимся с Wingz
После того как вы поняли, что представляет собой Wingz, у вас,
естественно, мог возникнуть вопрос: "Почему никто не сделал
этого раньше?” Это риторический вопрос, главное, что мы имеем
новые важные возможности, которые, вероятно, в скором времени
появятся и у других таблиц. Что появилось в Wingz сравнительно
со стандартным набором средств электронной таблицы? В общем,
всего несколько вещей. Наиболее существенной будут мощные и
изящные 3-мерные таблицы и графики двадцати типов. Но есть и
кое-что еще. Оригинальным образом использован цвет. Каждая
клетка может использовать свою комбинацию цвета фона и букв,
включая цвета из палитры пользователя. Обладателям цветных
мониторов открыт совершенно новый путь к ясным, понятным и
привлекательным таблицам. Но это еще не главное. Wingz содер¬
жит встроенный язык HyperScript, оставивший далеко позади
другие макроязыки для электронных таблиц. Одного этого доста¬
точно, чтобы считать Wingz революционно новым продуктом, а
это еще не все.
Средний пользователь Excel немедленно полюбит Wingz. Правиль¬
ное сочетание презентационных возможностей и вычислительной
мощи делает этот продукт привлекательным. Как я заметил, ранее
за десять последних лет электронные таблицы не претерпели
существенных изменений. По-прежнему требуется чудовищно
нудная работа и упорство, чтобы с их помощью получить что-ни¬
будь полезное. Язык HyperScript, однако, дает возможность суще¬
ственно изменить ситуацию. Правильное использование этого
простого, но мощного язьпса позволяет автоматизировать многие
вещи. Еще важнее возможность строить родовые приложения,
обслуживающие широкий спектр запросов и непрерывно совер¬
шенствующиеся с течением времени. Это придает смысл описан¬
ной технологии. В настоящее время, когда электронные таблицы
обрабатывают огромные объемы данных, новые подходы способны
сократить бесконечные часы нудной работы. Из сказанного стано¬
вится ясным, что Wingz и его производные имеют большое будущее.
Новое в редактировании электронных таблиц
Wingz включает некоторые новые средства редактирования элек¬
тронных таблиц, в частности набор инструментов, основанный на
пиктограммах. Например, пиктограммы Operators служат для
30
Введение в Windows 3.0
внесения знаков операций (например, знака доллара, обозначаю¬
щего абсолютный адрес клетки) в формулу с помощью одного
нажатия кнопки мыши. Функции склейки усовершенствованы по
сравнению со способом, реализованным в Excel. Тип склейки
выбирается в диалоговом окне при помощи электронных кнопок.
Большая часть типов, среди которых осуществляется выбор, при¬
сутствует на экране одновременно. Изменение размеров строк и
столбцов таблицы осуществляется простым нажатием кнопки мы¬
ши на рамке строки или столбца и последующим растягиванием
(сжатием) элемента до требуемого размера. Между клетками раз¬
ных таблиц могут устанавливаться связи, правда, для этого вторая
таблица должна быть активна, а пересчет не происходит автома¬
тически после внесения изменений.
Мощная графика
Пакет Wingz предоставляет пользователю 20 типов 3-мерных
графиков, включая даже топографические карты. Поддерживают¬
ся также графики, использующие полярную систему координат.
Каждый тип графиков позволяет осматривать себя со всех сторон,
изменяя угол зрения с помощью включенных в диалог регулято¬
ров. Возможен учет перспективы при построении изображений.
Но на этом дело не кончается. Имеются средства рисования, с
помощью которых можно импортировать и наклеивать на графики
созданные вне пакета картинки и клипы. Предусмотрены допол¬
нительные возможности для создания пользовательской палитры
путем смешения цветов.
Пользовательские управляющие элементы
и диалоги
Wingz позволяет в интерактивном режиме создавать элементы
управления, скажем кнопки, и размещать их в нужном месте на
экране; делать такие рутинные действия, как добавление к тек¬
стовому полю электронной таблицы линии прокрутки. Вы можете
записать последовательность операций в виде макроса и связать
его с кнопкой пользователя. Элементы управления и другие объ¬
екты могут объединяться в группы и работать сообща. Объекты
присоединяются к клеткам таблицы. Изменение размеров клеток
влечет за собой изменение размеров объектов, поэтому их обычно
создают после завершения разметки таблицы. Для объектов и
управляющих элементов существует множество стилей изображе¬
ния, включая трехмерный стиль Drop, использующий создавае¬
мый тенями эффект объемности. Доступ ко всем этим средствам
можно получить с помощью языка HyperScript.
Wingz: электронная таблица нового поколения
31
Секреты мастерства
В системе Wingz объектами называют почти все расширения
таблицы. Для создания объектов служат инструменты иа Wingz
Tool Box. Пользователь может разработать целиком весь экран,
раскрывая при этом все возможности ориентированных на графи¬
ку таблиц. При первом взгляде на систему Wingz можно не
заметить таких ее достоинств, как наличие многоформульных
критериев поиска в базах данных, использование многооконности,
возможности создания таблиц, зависящих от двух переменных,
нестандартные форматы чисел и средства выбора сетки. Клетки
Wingz, например, не обязаны иметь один и тот же размер и, как
уже упоминалось, могут быть раскрашены в разные цвета. Запом¬
нив несколько правил и немного поэкспериментировав, вы сможе¬
те за счет упомянутых средств придать данным таблицы
удивительный вид. Использование различных цветовых схем по¬
зволяет мгновенно по цветовым пятнам оценивать содержащуюся
в таблицах информацию. Электронные таблицы превращаются из
места хранения данных в привлекательный инструмент рекламы.
Построение баз данных
Подобно большинству современных электронных таблиц Wingz
позволяет строить базы данных, поддерживающие быстрое и лег¬
кое редактирование и хорошую визуализацию данных. Для работы
с базами данных можно использовать разнообразЕые макросы и
программы. Для разработки табличных баз важно строить форму¬
лы, макросы и программы, не зависящие от числа строк таблицы.
Вы должны уметь добавлять или удалять записи по мере надобно¬
сти, не заботясь о том, как работает та или иная утилита. Wingz
содержит развитые средства задания запросов на поиск в базе
данных. Например, имеется возможность задавать поля, не вклю¬
чаемые в результат поиска, который по выбору пользователя
может быть подсвечен или занесен в отдельную группу клеток.
Операции с матрицами
Массив клеток, с которым оперируют как с целым, называется
матрицей. Если у матрицы число столбцов равно числу строк, она
называется квадратной. Wingz позволяет работать с очень боль¬
шими матрицами. Удавалось строить и выполнять операции с
матрицами 96 * 96. Можно задать и большие матрицы. Матрицы
могут быть использованы для разных целей, например для реше¬
ния систем, включающих до 100 линейных уравнений. В клетки
матрицы можно занести коэффициенты системы и автоматически
получить решение системы.
32
Введение в Windows 3.0
Разработка совершенствуемых моделей бизнеса
В области деловых применений электронные таблицы удобно
использовать для разработки точных численных моделей бизнеса.
Подобные модели нельзя сделать за один прием. Идеальный метод
разработки табличной модели состоит в непрерывном внесении
изменений, повышающих точность модели. Существенным пре¬
пятствием на этом пути бывают форматы формул. У электронных
таблиц отсутствует простой способ уточнения формул. Однако
такой способ можно предусмотреть. Один из вариантов состоит в
том, чтобы располагать коэффициенты формул в клетках таблицы.
При этом вместо констант в формулах будут стоять ссылки на эти
клетки. Изменение формул можно свести к изменениям значений
клеток коэффициентов. Можно придумать процедуры, автомати¬
зирующие процесс уточнения.
Прогноз продаж и прибылей
Wingz позволяет использовать апробированные методы прогнози¬
рования, такие, как многокритериальная регрессия, алгоритм Box-
Jenkins, временные ряды, метод Census X-11, систему Foran. При
возможности следует строить родовые таблицы, которые можно
легко адаптировать, уточнять и использовать повторно. В этом и
заключена суть новых электронных таблиц. Многие из современ¬
ных таблиц содержат средства, приспособленные в основном для
презентаций. Предполагается, что в определенных обстоятельст¬
вах подобные украшения окупаются. Основное достоинство Wingz
состоит в том, что эта система делает наилучшим образом все, что
умеют делать современные электронные таблицы.
Усовершенствованная техника моделирования
Более совершенная техника моделирования предполагает постро¬
ение модели некоторого предприятия вместе с включающей его
отраслью. Такая модель способна предсказывать необычные и
важные события. В статистическом моделировании обычно выби¬
раются различные индикаторы, про которые известно, что они
позволяют прогнозировать изменения рынка. Часто эти индикато¬
ры никак напрямую не связаны с данным бизнесом. Зависимость
имеет корреляционный характер. Предложены методы использо¬
вания знаний об индустрии, не сводящиеся к экстраполяции
предшествующих тенденций. Продвинутая техника моделирова¬
ния позволяет не только экстраполировать предшествующую ста¬
тистику, но и строить предсказания исходя из полной модели.
Несмотря на всю сложность подобной деятельности, Wingz позво¬
ляет пытаться решать подобные задачи прогнозирования.
Wingz: электронная таблица нового поколения
33
Введение в Hyperscript
Третьей (после 3-мерной графики и многоцветных таблиц) особен¬
ностью Wingz,,дeлaющeй эту систему поистине уникальной, явля¬
ется язык Hyperscript. Это язык программирования "полного
профиля”, содержащий набор поддерживающих разработку инст¬
рументов. Это самый мощный язык программирования для элек¬
тронных таблиц, какой я когда-либо видел. Кроме обычных
макросов, автоматизирующих повторяющиеся действия,
Hyperscript позволяет создавать четыре специализированные типа
макросов: связанные с таблицей, с полем, с кнопкой и управляю¬
щие. Эти макросы связываются с объектом соответствующего типа
и обрабатывают поступающие от пользователя события. Кроме
выполнения макросов из файлов команды Hyperscript могут
выполняться из строки ввода и через систему меню Wingz. Строка
ввода удобна для интерактивного управления сеансом работы, а
также для проверки возникающих идей, перед кодированием их
в виде макросов. Я должен также упомянуть поддержку современ¬
ных управляющих структур и рекурсии.
Создание приложений на языке Hyperscript
Процесс написания приложений Hyperscript включает редактиро¬
вание, отладку, компиляцию макросов и связывание их с табли¬
цами, текстовыми полями или управляющими элементами.
Макросы редактируются в специальном окне, которое называется
окном макросов. Поиск синтаксических ошибок производится при
компиляции. Для отладки используется традиционная техника:
включение выдачи сообщений и использование комментариев для
изоляции сомнительных мест. Из языка Hyperscript можно до¬
браться до любой формулы или пункта меню. С помощью этого
языка можно задавать собственные функции. Эти функции ничем
не отличаются от других функций Hyperscript. Подстановка, а
точнее операторы GET SCREPT позволяют ссылаться на них из
других файлов макросов. Правильное использование этого средст¬
ва позволяет приспособить язык к вашим потребностям. Таким
образом открытый язык для открытого продукта позволяет созда¬
вать открытые приложения.
Программирование интерфейса с пользователем
Язык Hyperscript содержит средства полного контроля над поль¬
зовательским интерфейсом, включая возможность добавления но¬
вых подменю и даже новых строк меню. Wingz содержит пример,
из которого ясно, как с помощью Hyperscript может быть построен
пользовательский интерфейс Wingz. Использование ключевого
слова submenu позволяет создавать иерархические меню. Имеется
2-857
34
Введение в Windows 3.0
средство создания ’’всплывающих” меню, которые не привязаны
к определенной позиции основного меню. С помощью команды
ADD POPUP MENU вы можете определить, в каком месте экрана
оно будет появляться.
Программирование графиков
Команда DRAW помогает создавать интересные графики. Можно
разработать свои собственные типы двух- и трехмерных графиков
и, как это принято, связать их со специальными электронными
кнопками или пунктами меню. Пользовательская графика может
включать сплайны, дуги, стрелки, специальные символы и З-мер-
ные тени. Можно разрабатывать большие графические приложе¬
ния, включающие интерактивное взаимодействие с
пользователем, пошаговое построение графиков, допускающее в
любой момент прерывание его пользователем, который может
изменить параметры таблицы. В принципе, описанная ниже воз¬
можность использования внешних функций позволяет построить
графическую систему любого типа. Hyperscript дает возможность
строить открытые системы, зависящие только от мастерства поль-
зователя-разработчика.
Ограничения
Поскольку вывод из прочитанного очевиден, я в заключение
обзора постараюсь сказать о недостатках этого продукта, если
смогу их найти.
Одним из них является ограничение на функцию GOAL. Ей
необходимо задать начальное приближение, затем она проведет до
20 итераций, уточняющих результат. В таблицах Excel, Quattro
Pro и 123/G это сделано удачнее. Если кто-нибудь скажет, что для
этого можно воспользоваться языком Hyperscript, то он будет не
прав. Во-первых, большинство пользователей не умеют писать
алгоритмы такого рода, а, во-вторых, для того, чтобы достичь
приемлемой скорости выполнения, их следует кодировать на язы¬
ке ассемблера.
Другой недостаток состоит в отсутствии формульного задания
матричных операций. В этом отношении Wingz напоминает
Quattro Pro, в котором матричные вычисления выполняются ин¬
терактивно из меню. Использовавшим формулы с матрицами
системы Excel придется потрудиться, переходя в Wingz.
Я бы посоветовал включить в следующие версии Hyperscript набор
операций для выполнения других программ Windows и для пере¬
дачи им команд. Это сделает Hyperscript не только языком для
Wingz, но и для Windows в целом. И наконец, в краткий список
моих пожеланий входит возможность вызова из языка Hyperscript
Сеанс работы с Windows 3.0
35
функций, содержащихся в библиотеках DLL. Может быть, для
этого придется ввести специальный формат вызова. Это средство
откроет перед языком Hyperscript целый мир.
■ СЕАНС РАБОТЫ С WINDOWS 3.0
Я решил отобразить на экране предопределенную схему использо¬
вания цветов. Для этого, не покидая свой текстовый процессор,
вызвал наверх Task Switch и переключил управление на Program
Manager, а оттуда запустил Control Panel. Пока я это делал, окна
начали накладываться ’’кипой” поверх текстового процессора,
оставляя, однако, для меня некоторую часть его окна, таким
образом сохраняя за мной возможность вернуться в него одним
нажатием кнопки мыши. Я раскрыл пиктограмму Colors (цвета)
окна Crayon и как только появился макет экрана Windows, нажал
кнопку мыши на элементе Color Palette. Раскрылось новое окно с
”ящичками красок”, среди которых нужно было выбирать. Я
решил покрасить верхнюю строку меню в темно-зеленый цвет. Для
этого я выбрал соответствующую часть макета окна, а затем нажал
кнопку мыши на зеленом цвете. В результате верхняя линия меню
на макете сделалась зеленой. Я выбрал заголовок и нажал кнопку
мыши на желтом цвете, его цвет также изменился. Текст меню
должен быть ярким, четким и ясно видимым. Я выбрал малино¬
вый. Затем я проделал это же с линией прокрутки, и она стала
светло-зеленой. Я нашел, что это сочетание очень приятно глазу,
и направился в меню File с целью сохранить эту схему использо¬
вания цветов на жестком диске.
Далее я захотел попробовать, как работает ”горячая клавиша”
PrintScreen на одной графической программе, не ориентированной
на работу в Windows. Я вернулся в Program Manager и вошел в
группу не-Windows программ. Там я выбрал пиктограмму
Autosketch. Запустилась программа Autosketch фирмы Autodesk,
в которой я загрузил выполненный на синем фоне чертеж локомо¬
тива. Когда построение было закончено, я нажал комбинацию
клавиш Shift-PrintScreen и монитор мигнул. По этому признаку
я понял, что, вернувшись в Clipboard среды Windows 3.0, я получу
результат. Я покинул программу Autosketch, и на экране вновь
возникла среда Windows. Я открыл пиктограмму Paintbrush. С
помощью опции View этой программы удалил палитру и набор
инструментов с экрана, тем самым получив его в свое распоряже¬
ние целиком. Затем я выбрал опцию Paste в меню Edit и после
нескольких подготовительных помигиваний курсора мой локомо¬
тив импортирован в окружение Windows, где и был сохранен в
форме побитового изображения.
36
Введение в Windows 3. 0
Далее я попытался использовать эту картинку в качестве фона
экрана Windows. Однако она напомнила мне стол моего отца,
сплошь покрытый синими чернильными пятнами. Поскольку это
не совсем то, что мне нужно, я вызывал другую графическую
программу — Music Printer Plus, которая используется для редак¬
тирования и проигрывания музыки в студии MIDI (цифровой
интерфейс с музыкальными инструментами). Я загрузил аранжи¬
ровку моей песенки ,rForever” для небольшого ансамбля, нажал
Shift-PrintScreen, и экран снова подмигнул мне. Я вышел из
программы, опять вошел в PaintBrush и скопировал содержимое
Clipboard. Получив в программу, предназначенную для графиче¬
ского дизайна, ноты, я не удержался и раскрасил нотный лист в
несколько цветов. Меня всегда интересовало, что произойдет, если
раскрасить ноты. Действительно, получается неплохая картинка.
Я сохранил ее. Далее я вошел в утилиту Desktop и использовал
многоцветный нотный лист в качестве фона экрана.
Мне захотелось выполнить обратное действие: перенести инфор¬
мацию из Clipboard в не-Windows программу. Для начала я вызвал
CTALK VIEWS Browser — объектно-ориентированный инстру¬
мент, весьма полезный, но со слабым редактором. Я хотел найти
какой-нибудь код, похожий на тот,что хотел написать сам, чтобы
отредактировать его в соответствии со своими потребностями. Я
нашел искомый текст, выделил его при помощи мыши и скопиро¬
вал на Clipboard. Затем я вызвал Programmer’s Workbench, нажал
на AltEsc и свернул его в пиктограмму DOS. Затем я вызвал
управляющее меню этой пиктограммы и поочередно воспользовал¬
ся опциями Edit и Paste. Затем снова раскрыв пиктограмму,
увидел, что в редактор была вклеена моя программа. Я сохранил
результаты и закончил сеанс работы с Windows, удовлетворен¬
ный тем, что эта система работает в полном соответствии с
документацией.
Глава 2
Я ЧТО ТАКОЕ ОБЪЕКТНО¬
ОРИЕНТИРОВАННОЕ
ПРОГРАММИРОВАНИЕ?
■ ВВЕДЕНИЕ
Несмотря на то что эта книга посвящена объектно-ориентирован¬
ному программированию (ООП) для Windows, для начала важно
познакомиться с объектно-ориентированным программированием
как таковым, вне связи с окружением Windows. В результате
читатель сможет более объективно подойти к тому, что будет
рассматриваться далее. Предполагается, что читатель умеет про¬
граммировать, но не знаком с объектно-ориентированным подходом.
С моей точки зрения, ООП представляет собой самую высокую
стадию развития технологии программирования. В той же мере,
в какой структурный подход к программированию в языках Алгол
и Паскаль был новым шагом по сравнению с Фортраном, ООП
представляет собой новую стадию эволюционного развития. По
мере развития технологии, раньше или позже, большинство про¬
граммистов оценит его по достоинству. По этой причине в индуст¬
рии производства программ ему уделяется постоянное внимание.
■ ПАРАДИГМ Ы П РОГ-РАМ M И PO ВАН И Я
Для некоторых покажется удивительным, что в программирова¬
нии существуют различные парадигмы, представляющие собой
разные подходы к написанию программ. Большинство программи¬
стов знакомы лишь с одной из них: процедурным программирова¬
нием. Кроме этого существует объектно-ориентированная, о
которой пойдет речь в этой книге, а также имеется программиро¬
вание, основанное на правилах, логическое программирование,
параллельное программирование, визуальное программирование,
38
Что такое объектно-ориентированное программирование?
программирование, основанное на потоках данных, и другие пара¬
дигмы. Почему же их столько?
Отчасти потому, что программирование сравнительно новая дис¬
циплина, а отчасти из-за желания людей решать разные задачи.
Кроме того, наиболее популярная в данный момент компьютерная
архитектура не является единственной. В настоящее время прово¬
дится большое число экспериментов с машинами, имеющими
нетрадиционные архитектуры, многие из которых рассчитаны на
применение других парадигм программирования. Общая природа
цифровых машин позволяет с большей или меньшей эффективно¬
стью моделировать одну архитектуру с помощью другой. Из архи¬
тектур наиболее удачны те, в которых за счет аппаратуры и
программного обеспечения достигнуты наивысшая скорость и про¬
стота использования.
■ ОБЪЕКТНО-ОРИЕНТИРОВАННЫЙ ПОДХОД
Если вы зададите вопрос? ”Что такое объектно-ориентированное
программирование?”, то будьте готовы услышать от разных людей
разные ответы на него. Во многих случаях этот термин употреб¬
ляется не в его точном смысле. Даже в тех случаях, когда вопрос
касается не конкретных продуктов или инструментов, акцент
опять-таки будет поставлен по-разному, в зависимости от того,
какие именно стороны объектно-ориентированного программиро¬
вания собеседник считает наиболее полезными и интересными.
Получается, что каждый пользователь вкладывает в ответ свое
собственное понимание этого подхода. В каком смысле этот термин
используется в данной книге?
Прежде чем двигаться дальше, попытаемся исключить всякую
путаницу в терминологии. Это необходимо, поскольку термин
’’объектно-ориентированный” используется по крайней мере еще
в одном смысле, а именно для обозначения специального типа
прикладных графических программ. Этот термин в последнем
случае служит для противопоставления пиксел-ориентированных
и экранно-ориентированных графических программ. Объектно¬
ориентированная графика предполагает, что полная картина скла¬
дывается из нескольких самостоятельных объектов, над каждым
из этих объектов всегда можно выполнить отдельное преобразова¬
ние. Обычная же практика предполагает хранение массива пиксе¬
лов. В этой книге слова ”объектно-ориентированный” имеют
другой смысл. Я использую этот термин для описания стиля и
инструмента программирования, независимо от типа приложения.
Верно также, что с помощью объектно-ориентированной техники
программирования удобно создавать графические программы опи¬
санного типа. Впрочем, объектно-ориентированные графические
приложения могут быть закодированы и без применения объект¬
Объектно-ориентированный подход
39
но-ориентированной техники программирования, и, наоборот,
пиксел- и экранно-ориентироваыные графические программы мо¬
гут быть написаны с применением объектноориентированного
подхода. В данной книге прикладная программа будет называться
объектно-ориентированной, если она написана с применением
объектно-ориентированной методологии программирования.
Для начала мы будем пользоваться следующим предварительным
определением ООП.
Объектно-ориентированное программирование (ООП) — это способ
программирования, обеспечивающий модульность программ за
счет разделения памяти на области, содержащие данные и проце¬
дуры. Области могут использоваться в качестве образцов, с кото¬
рых по требованию могут делаться копии.
Сделаем остановку, чтобы разобраться, что имеется в виду. В
соответствии с этим определением объект понимается как участок
памяти. Самое время развеять некое недоразумение. Многие вос-
прияимают слова ’’объектно-ориентированный” буквально, счи¬
тая, что это тин программирования, имеющий дело с реальными,
земными объектами, а не с состоянием программируемого компь¬
ютера. Доля правды в этом, конечно, есть, но это не определяющая
сторона ООП.
Наиболее важен выбранный способ разделения памяти компьюте¬
ра, позволяющий каждому из модулей или объектов функциони¬
ровать относительно независимо друг от друга. Такое разделение
обладает многочисленными преимуществами при программирова¬
нии. Правда, большинство программистов выделит в первую оче¬
редь те из них, которые наиболее интересны для них самих или
произвели на них наибольшее впечатление.
Какой смысл вкладывается в слова ’’разделение памяти компью¬
тера”? Это означает, что имеется система разбиения памяти ком¬
пьютера на функционально относительно независимые области.
Эти области независимы в том смысле, что могут использоваться
в разных программах без модификации с полной уверенностью,
что ни одна из них не будет затерта при включении ее в другое
окружение.
В нашем определении сказано, что в выделенных областях разме¬
щаются не только данные, но и код выполняемых процедур. Это
разделение существенно для защиты объектов. Если бы любой
процедуре был разрешен доступ к памяти объекта, то с его
данными могли бы происходить непредсказуемые события, что,
очевидно, сказалось бы на выполнении им своих функций. По этой
причине активные процессы объектно-ориентированной системы
оформляются как локальные функции и процедуры. Они,и только
они, имеют доступ к данным объекта. Таким образом объект
защищает себя от разрушения данных в результате внешних
40
Что такое объектно-ориентированное программирование?
событий. В результате, как только функциональный элемент про¬
граммы написан и полностью отлажен, он становится работоспо¬
собным независимо от последующих дополнений и модификаций
в использующей его программе.
Наконец, в определении сказано, что структура может использо¬
ваться в качестве образца для изготовления копий. Это означает,
например, что как только определен объект ”окно”, так сразу
можно создать столько таких окон, на сколько хватит памяти. При
этом не понадобится создавать дополнительный код, позволяющий
убедиться в отсутствии взаимодействия. Утверждая возможность
копирования, я при этом имею в виду только способ поведения
объектов. Внешне все выглядит так, как если бы существовало
несколько полных копий. Пожалуйста, отметьте, что из сказанно¬
го не следует способ реализации.
Я МЕТАФОРЫ ОБЪЕКТНО-ОРИЕНТИРОВАННОГО
ПРОГРАММИРОВАНИЯ
Каждая парадигма программирования имеет свои метафоры, по¬
могающие программисту думать о структуре программы. Инфор¬
матика полна метафор, постепенно переходящих в
программистский жаргон. Двумя яркими примерами могут слу¬
жить термины ’’память” и **окно”. Если остановиться и подумать,
то мы обнаружим, что новые значения этих слов очень далеки от
первоначальных. Память и окна, о Kotopbix говорится в курсе
информатики, слабо напоминают предметы реального мира, дав¬
шие им имена. Так или иначе, но термины — это просто ярлыки,
которыми мы без труда пользуемся.
К метафорам, объясняющим важные концепции программирова¬
ния, надо относиться особенно аккуратно. Чему можно уподобить
ООП? Прежде чем ответить на этот вопрос, отметим, что такие
основные концепции ООП, как ’’передача сообщений” и ’’наследо¬
вание”, имеют метафорический смысл. По крайней мере в случае
этих двух метафор аналогия может быть успешно продолжена.
Некоторые из метафор, используемых для объяснения объектно¬
ориентированных систем, менее прозрачны и, как следствие, ме¬
нее удобны. В своей книге ’’Объектно-ориентированное
программирование” ее автор Брэд Кокс использует для объяснения
основных принципов объектно-ориентированных систем метафо¬
ры программной интегральной схемы и фабрики. Как будет пока¬
зано далее, несмотря на определенное педагогическое значение,
эти метафоры не могут быть развиты глубже для описания объек¬
тно-ориентированных систем. Эти системы содержат средства,
делающие их технологически совершеннее, чем интегральные
схемы и фабрики. Причина этого кроется в их предельной модуль¬
ности. Нельзя воспользоваться стандартной ИС для построения
Передача сообщений
41
новой интегральной схемы. Продукция фабрик обычно мало напо¬
минает фабрики, на которых она произведена, в то время как для
объектно-ориентированного программирования характерно ис¬
пользование существующих объектов для изготовления более но¬
вых, наследующих все свойства своих предшественников.
S АКТИВНЫЕДАННЫЕ
Весьма удобно рассматривать объекты как попытку создания
активных данных. Смысл, вкладываемый в слова ’’объект пред¬
ставляет собой активные данные”, основан на объектно-ориенти¬
рованной парадигме выполнения операций, состоящей в посылке
сообщений. В посылаемых объекту сообщениях указывается, что
мы хотим, чтобы он выполнил. Так, например, если мы хотим
вывести на экране строку, то мы посылаем строке сообщение,
чтобы она изобразила себя. В этом случае строка — это уже не
пассивный кусок текста, это активная единица, знающая, как
правильно производить над собой различные действия. По многим
причинам имеет смысл объединять операции с объектами с самими
объектами.
Наиболее существенно различие, проводимое в программирова¬
нии, между данными и процедурами. Оно в определенной степени
отражает различие между аппаратурой процессора и схемами
памяти. • На такой архитектуре машины основана модель програм¬
мы, в которой данные запоминаются в памяти, а инструкции
процессоров манипулируют этими данными. В этой модели данные
выполняют пассивную роль, а оперирующие над ними инструкции
являются активными элементами. ООП — это шаг на пути к
другой модели программирования. Объект одновременно представ¬
ляет собой и данные и код. Это форма активных данных.
■ ПЕРЕДАЧА СООБЩЕНИЙ
Один из основных способов взаимодействия объектов в ООГ1 — это
передача сообщений. Связь между объектами осуществляется пу¬
тем передачи сообщений. Сообщения могут быть приняты или
отвергнуты. Объект принимает те сообщения, которые опознает, а
остальные игнорирует. В этом случае способы взаимодействия
объектов полностью контролируются. Поскольку этому протоколу
следуют все объекты, внешний по отношению к объекту код не
может непредсказуемым или нежелательным способом влиять на
функционирование этого объекта.
Посылка сообщений требует, как минимум, указания адресата и
имени посылаемого сообщения. Часто требуется также задать
параметры. В качестве аргументов могут использоваться имена
переменных, известных исключительно принимающему сообще¬
ние объекту, или имена глобальных переменных. Объекту можно
42
Что такое объектно-ориентированное программирование?
посылать сообщение записать себя на диск, вывести на экран,
уничтожить себя и т.д. Имеется ли глубинная связь между пере¬
дачей сообщений и активными данными? Это сложный вопрос.
Для правильного ответа на него изложенных до сих пор сведений
недостаточно. Пока можно сказать, что передача сообщений при¬
дает поведению объектов (с точки зрения внешнего наблюдателя)
характер активных данных. Изнутри объекта по-прежнему суще¬
ствует четкое разделение пассивных данных и активных процедур.
Какое влияние на систему оказывает использование передачи
сообщений? Вновь с окончательным ответом придется повреме¬
нить. В общих чертах ответ состоит в том, что передача сообщений
поддерживает продвинутую технику программирования, которая
использует разделение труда между принимающими сообщения
объектами. Хотя тот же результат можно было бы получить и без
передачи сообщений, как правило, это гораздо труднее и требует
больших знаний и мастерства от программиста. В целом, объекты
в системах, основанных на обмене сообщениями, обладают уни¬
кальной степенью автономности. В определенном смысле каждый
объект ведет себя как маленький специализированный компью¬
тер, взаимодействующий с другими специализированными компь¬
ютерами. Это, разумеется, тоже метафора. Как мы увидим далее
в этой книге, модель передачи сообщений дает множество инте¬
ресных и мощных алгоритмов, которые, не будь этой модели,
могли бы не прийти в голову программистам и разработчикам.
■ КЛАССЫ: ПОРОЖДЕНИЕ ЭКЗЕМПЛЯРОВ И
НАСЛЕДОВАНИЕ
Концепция класса является одной из базисных для ООП. В этом
контексте класс понимается как образец для создания настоящих,
работающих объектов определенного типа. Во многих объектно¬
ориентированных системах класс сам считается объектом, облада¬
ющим, правда, очень ограниченным набором возможностей.
Говорится, что создается экземпляр класса, когда класс использу¬
ется в качестве ’’штампа” для изготовления одного или нескольких
объектов его типа; созданный объект называется экземпляром
класса. Практическая применимость структуры класса обусловле¬
на наличием средств наследования, т.е. способности создавать
классы, автоматически моделирующие поведение других классов.
Если класс В моделирует свойства класса А, то мы называем В
подклассом А, а класс А соответственно суперклассом для В. В таком
случае говорят, что В полностью наследует поведение класса А.
Отдельно взятая возможность создания класса, наследующего
поведение другого класса, лишена смысла. Подкласс создается для
того, чтобы добавить новые элементы в унаследованное поведение.
Обычно подклассы характеризуются более специализированным
Классы: порождение экземпляров и наследование
43
по сравнению с соответствующими им суперклассами поведением.
Некоторые объектно-ориентированные системы содержат средства
множественного наследования. Это означает, что у класса может
быть более одного суперкласса. В таких системах новый класс
иногда может быть создан только за счет наследования от двух
или более суперклассов, в других случаях дополнительные аспек¬
ты поведения вносятся с помощью ручного кодирования. В любом
случае полученный класс может быть использован для ’’штампов¬
ки” объектов, поведение которых будет отличаться от поведения
суперкласса или суперклассов.
В процессе определения подклассов в объектно-ориентированной
системе появляется так называемая иерархия классов. Эта иерар¬
хия представляет собой дерево или сеть классов. Дерево (сеть)
начинается с наиболее общих классов и спускается к наиболее
специализированным листьям. Одной из основ, определяющих
мощь объектно-ориентированного окружения, служит иерархия
классов и ее возможности. Программисты расширяют объектно¬
ориентированный язык, наращивая иерархию классов и словарь
сообщений, которыми обмениваются объекты.
Активные элементы объектов называются методами, они напоми¬
нают функции, локализованные в данном объекте. Имена методов
часто называют селекторами, поскольку они позволяют системе
выбрать, какой следует выполнить код при вызове по имени.
Стабильность протокола взаимодействия объектов при расшире¬
нии языка составляет одно из наиболее важных достоинств объек¬
тно-ориентированных систем, определяющее способность
эффективно управлять процессом разработки больших систем, в
котором заняты несколько программистов. Эффективность дости¬
гается за счет минимизации числа связей между программистами
и объема программной документации.
Для достижения этих целей используется перегрузка. Перегруз¬
ка — это соглашение, по которому методы разных классов, выпол¬
няющие одинаковые действия, носят одно и то же имя. В
большинстве языков со строгой типизацией функции, выполняю¬
щие одни и те же действия, но над элементами разных типов,
должны называться по-разному. Так, например, для деления
целых и действительных чисел необходимо иметь две разные
функции, скажем, divideInteger и divideReaL В системах с боль¬
шим количеством классов это может значительно осложнить про¬
граммирование, ибо программист должен будет непрерывно
искать имена функций. Перегрузка дает возможность пользовать¬
ся одним именем, скажем divide, для функций нескольких клас¬
сов. При этом реализация метода может различаться от класса к
классу. За счет этого пользователь получает единый протокол для
широкого спектра операций классов, облегчающий использование
44
Что такое объектно-ориентированное программирование?
системы. Программист освобождается от обязанности просматри¬
вать написанный другими программистами код для того, чтобы
узнать, как к нему обратиться и как его использовать.
■ ТИПЫ ОБЪЕКТНО-ОРИЕНТИРОВАННЫХ СИСТЕМ
Имеются несколько критериев классификации объектно-ориенти¬
рованных систем. Наиболее четко различаются чистообъектные и
гибридные системы. В чистообъектной системе всё является объек¬
том. Примером такой системы служит Смолток. В этой системе
даже классы являются объектами, экземплярами некоторого клас¬
са. На первый взгляд это кажется странным, но никакого пара¬
докса здесь ыет. Это прямой результат стремления получить
полностью согласованную систему. Подобно тому как объекты
создаются из классов, служащих для них моделью или образцом,
так и сами классы представляют собой объекты, создаваемые в
соответствии с шаблоном, заключенным в определенном классе.
Этот класс обычно называют метаклассом.
Въедливый читатель несомненно почувствовал знакомые очерта¬
ния замкнутого круга. В голову приходит мысль: ’’Если классы
всегда являются экземплярами какого-то другого класса, то отку¬
да взялся первоначальный класс?” Ответ прост: в этом месте
принцип нарушен. Ибо объектно-ориентированная система не
логическая загадка, а практический инструмент. И то, что мета¬
класс, используемый в качестве образца, не является экземпляром
класса, не недостаток системы. Она вполне работоспособна и берет
все, что можно, от объектно-ориентированной парадигмы.
В гибридных системах объекты сосуществуют с обычными элемен¬
тами языков программирования. В качестве примеров можно
привести различные объектно-ориентированные расширения Си и
Лиспа, некоторые детали которых будут рассмотрены в следующей
главе. Поскольку объекты включены в существующий язык про¬
граммирования, не все элементы гибридных систем представляют
собой объекты. Возможности инкапсуляции и защиты данных
зависят от структуры и реализации базового языка. Обычно суще¬
ствуют средства обхода защиты.
Другим критерием классификации объектно-ориентированных си¬
стем служит тип механизма наследования. Имеются два механиз¬
ма: одиночное и множественное наследование. Как уже
говорилось, системы с множественным наследованием позволяют
классу наследовать более чем от одного суперкласса. Множествен¬
ное наследование появилось сравнительно недавно, и многие реа¬
лизации этого средства не содержат.
Множественное наследование не столь просто. Одна из причин
этого состоит в потенциальных конфликтах имен переменных и
методов в разных классах, из которых ведется наследование. И
Как работаютобъектно-ориентированные системы?
45
хотя эта ситуация сравнительно редка, все же необходим строгий
механизм разрешения конфликтов. Пока не придуман общеприз¬
нанный метод обработки таких ситуаций.
Последний критерий состоит в наличии или отсутствии параллель¬
ной обработки. Так, в параллельной системе обмен сообщениями
может происходить одновременно, в то время как в последователь¬
ной системе объект ожидает возврата сообщения.
В табл. 2.1 перечислены пять признаков, определяющих степень
объектной ориентированности системы.
Таблица 2.1. Степени объектной ориентированности систем
Kpumepuu уровня
1. Классы и множество экземпляров
2. Инкапсуляция функций и данных
3. Связывание периода выполнения
4. Множественное наследование
5. Обмен сообщениями
■ КАК РАБОТАЮТ ОБЪЕКТНО-ОРИЕНТИРОВАННЫЕ
СИСТЕМЫ?
В некоторых приложениях программист может не учитывать прин¬
ципы ООП, однако для большинства приложений, связанных с
искусственным интеллектом, это невозможно. Одна из целей объек-
тно-ориентированных систем — позволить использовать код, не
заставляя программиста вдаваться в детали его устройства. Многие
современные программные проекты требуют до предела и даже сверх
того использовать возможности системы программирования. Вот
причины, по которым я хочу завершить эту главу общим рассказом
о базисных механизмах, используемых в объектно-ориентированных
системах для раскрытия присущих им возможностей.
Что в действительности происходит, когда объекту передается
сообщение? В большинстве объектно-ориентированных систем
объекты состоят из видимой и приватных частей. Нет необходи¬
мости создавать копии неизменяющихся частей объектов, это
приведет к разбазариванию ресурсов памяти. Как правило, общие
для всех объектов одного типа части запоминаются на уровне
класса. В эту часть чаще всего попадают код методов и переменные
класса, которые составляют разделяемую часть объектов. Когда
послано сообщение оно обрабатывается подпрограммой, которая
просматривает область памяти класса. Имена и значения перемен¬
ных экземпляров приватны. Каждый объект (экземпляр) содер¬
жит свои собственные переменные. Класс содержит информацию
46
Что такое объектно-ориентированное программирование?
о том, какие переменные создавать, но запоминаются они самим
экземпляром. Если разыскиваемые данные или код унаследованы
у другого класса, они запоминаются единожды на более высоком
уровне. Использующие их подпрограммы переадресуются по
иерархии до тех пор, пока не достигнут класса, у которого эти
данные или код наследуют остальные.
■ ИСПОЛЬЗОВАНИЕ ОБЪЕКТНО-ОРИЕНТИРОВАННЫХ
СИСТЕМ
В основном благодаря Смолтоку, первому подлинно объектно-ори¬
ентированному языку, системы этого типа славятся специальными
средствами пользовательского интерфейса, облегчающими про¬
граммисту жизнь. Многооконная, ориентированная на мышь сре¬
да, ставшая популярной для всех типов прикладных программ,
первоначально прочно ассоциировалась с объектно-ориентирован¬
ными системами. Некоторые уникальные особенности и требова¬
ния объектно-ориентированного программирования
способствовали популярности визуализирующих программу инст¬
рументов. Так, например, с объектно-ориентированными система¬
ми ассоциируется программа броузер (Browser). Как и следует из
названия, броузер — это средство, позволяющее программисту по
желанию просматривать некоторые части программного окруже¬
ния. В частности, в Смолтоке имеется System Browser, позволяю¬
щее рассматривать иерархию классов системы, а затем перейти к
одному из ее элементов для редактирования.
Объектно-ориентированные системы содержат множество возмож¬
ностей, но они доступны только тем, кто знает, как ими пользо¬
ваться. В таких системах существует уникальное число
соглашений, ответственность за соблюдение которых ложится на
программиста. Начнем с того, что программист должен знать
базисные действия, такие, как определение класса, создание объ¬
екта, выполнение операций. Кроме этого он обязан знать, какие
методы доступны объектам какого класса. Зачастую в объектно¬
ориентированной системе имеется множество полезных классов и
методов, однако программист не получит от них никакой пользы,
если не будет знать, что они делают и как их использовать.
■ ПОЧЕМУ ПРИМЕНЯЮТ
ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ
ПРОГРАММИРОВАНИЕ?
Существуют разные причины, по которым программисты прихо¬
дят к выводу о важности и даже незаменимости ООП. Одного
наиболее привлекает исключение избыточного кода, другого —
возможность защиты объектов от кода других частей программы.
Некоторые выводы
47
Для третьего важна экономия времени за счет построения програм¬
мы из готовых, отлаженных частей, вместо того чтобы начать
кодирование с пустого места. Наконец, существуют программи¬
сты, которым нравится возможность создавать произвольное ко¬
личество независимых друг от друга объектов. Большинство
объектно-ориентированных систем предоставляют программам все
эти возможности. Какие из них окажутся наиболее важными,будет
зависеть от требований проекта и предпочтений реализатора. По
мере роста знаний и опыта в области объектно-ориентированных
систем ваши предпочтения могут изменяться. Но это не столь
важно, поскольку теоретически программы могут пользоваться
всеми этими возможностями одновременно.
Когда программисты прекратили использовать двоичный код и
перешли к ассемблерам, понимающим заменяющие двоичные
команды мнемонические имена, шансы на выживание старого
метода сильно упали. С тех пор прогресс в технологии программи¬
рования стал более плавным, и существенные изменения не всеми
воспринимаются одновременно. Разработка компилируемых язы¬
ков высокого уровня привела к широкому их использованию. Но
компиляторы не вытеснили ассемблеры. Правильнее считать их
следующим слоем программного обеспечения, вставшим между
машиной и программистом, чтобы дополнительно облегчить про¬
цесс написания программ.
■ НЕКОТОРЫЕ ВЫВОДЫ
ООП нельзя рассматривать как абсолютно новое, не имеющее
прецедентов направление в информатике. Оно просто аккумули¬
рует последние достижения в области языков программирования,
делая новый шаг в сторону ясности, модульности и эффективно¬
сти. С некоторой точки зрения, ООП можно рассматривать как
попытку довести идеи структурного программирования до логиче¬
ского завершения. В структурном программировании переменные
могут быть локализованы в процедурах, передающих друг другу
в качестве аргументов строки и числа. Объектно-ориентированное
программирование делает здесь небольшой шаг вперед. Перемен¬
ные локализуются теперь не только в процедурах. Основным
строительным блоком становятся объекты, т.е. защищенные обла¬
сти памяти, которые могут содержать как локальные данные, так
и локальные процедуры. Более того, строительные блоки не взаи¬
модействуют друг с другом посредством передачи параметров.
Локализованные в объектах процедуры, обычно называемые ме¬
тодами, служат сообщениями, которые посылают и принимают
объекты. В этом отношении объекты напоминают маленькие ком¬
пьютеры, находящиеся внутри основного компьютера, каждый из
которых обладает своими областями данных и кода.
48
Что такое объектно-ориентированное программирование?
В большинстве объектно-ориентированных систем имеются два раз¬
ных типа объектов: классы и экземпляры. Классы могут быть
логически связаны друг с другом. При этом один из них называется
подклассом, а другой — суперклассом. В целом, суперкласс — это
более абстрактный класс, а подкласс — более конкретный. Так,
например, мы можем создать класс Мебель, а затем создать класс
Шкаф как подкласс класса Мебель и КнижныйШкаф как подкласс
класса Шкаф. В этом примере класс Мебель будет суперклассом для
класса Шкаф, а Шкаф — суперклассом для класса КнижныйШкаф.
У объектно-ориентированных систем имеются как минимум три
непосредственных преимущества: одно состоит в том, что если вы
написали код для класса, то вы можете иметь столько экземпляров
этого класса, сколько позволяет память. Класс — это просто
образец, по которому строится каждый экземпляр, который в свою
очередь снабжается собственной областью памяти, недоступной
для других объектов иначе, как посредством обращения к локаль¬
ным методам этого объекта. Это, в частности, означает, что в
объектно-ориентированной системе могут совершенно свободно
сосуществовать, не влияя друг на друга, произвольное количество
таких объектов, как графические окна, редакторы, интерпретато¬
ры и т. п. Другое преимущество предоставляется механизмом
наследования. Подклассы автоматически получают все перемен¬
ные и методы своих суперклассов. То есть более специализирован¬
ные функции могут быть написаны за счет добавления частей,
делающих их уникальными. При эгом все остальные свойства
будут унаследованы автоматически. Возможность иметь один и тот
же интерфейс с широким спектром типов объектов составляет
третье преимущество. Это достигается за счет того, что для методов
разных объектов, имеющих разную реализацию, может использо¬
ваться одно и то же имя. При этом различие в реализации остается
невидимым пользователю. Например, мы можем создать несколь¬
ко различных классов, соответствующих разным многогранникам.
Затем в каждом из этих классов мы зададим методы, вычисляю¬
щие объем и площадь поверхности тела. Формулы и их програм¬
мная реализация могут различаться, но имена, с помощью
которых вызываются эти методы, будут одинаковы. К этим мето¬
дам можно обратиться, написав Тетраэдр.Объем или Куб.Объем.
В каждом случае будет вызван требуемый метод, который вернет
искомый объем.
Некоторые считают основной особенностью ООП возможность ис¬
пользовать один и тот же код в разных программах. Однако
подобную возможность дают и библиотечные функции. Ключевые
преимущества ООП становятся очевидными не сразу, но опытные
программисты, которые поработали с такими системами, подтвер¬
дят, что объектно-ориентированные языки значительно облегчают
работу над большими программами. Это не приходит, однако, само
Дополнительные заботы программиста
49
собой. Ключ к успеху кроется в правильном разбиении программы
на части. Одновременно необходимо изучить правильную технику
кодирования и приемы, упрощающие работу членам команды
программистов. Границы объектно-ориентированных систем
обычно несколько размыты. Части приложений разбросаны по
большому количеству классов и подклассов. Для эффективного
программирования в таких системах важно иметь адекватные
инструменты и эффективные методы поддержания целостности и
правильной организации приложения.
Очень важно отметить, что ООП не следует рассматривать как
нечто, что можно освоить в один момент. Дело в том, что парадиг¬
ма ООП существенно отличается от всего того, к чему привыкли
программисты. Подготовка требует интенсивного пополнения зна¬
ний. Другими словами, легкодоступный модульный код полезен
только тем программистам, которые владеют имеющимся у них
инструментом и техникой его использования. Многие программи¬
сты сопротивляются изучению новых языков, не говоря уже о
новых программных парадигмах, поэтому имеет смысл вновь
перечислить преимущества ООП для практического программиро¬
вания. Как вы видите, их четыре:
1. Стандартные соглашения вызова для широкого диапазона
операций, реализующих различные модификации одной
темы.
2. Возможность управлять очень большими программными
проектами, разбивая большие задачи на небольшие, неза¬
висимые и легко обозримые части.
3. По-настоящему модульное программное окружение, сводя¬
щее повторы при кодировании к минимуму.
4. Возможность порождать множество экземпляров функции
или объекта, используя один и тот же код. Разные экземп¬
ляры не оказывают влияния друг на друга.
■ ДОПОЛНИТЕЛЬНЫЕ ЗАБОТЫ ПРОГРАММИСТА
До сих пор при изложении материала подразумевалось, что ООП
предъявляет дополнительные по сравнению с обычным подходом
требования к программисту. В чем они состоят? Во-первых, это
задача модульного проектирования. Определенная степень мо¬
дульности возможна и при разработке программ, использующих
и обычную процедурную парадигму. Объектно-ориентированные
системы допускают большую степень модульности. Разработка
систем со столь высокой степенью модульности — дело довольно
сложное. В ООП требуется как можно лучше предвидеть потреб¬
ности системы. В идеальном случае программист, разрабатываю¬
щий поведение объекта, кодирует его в наиболее абстрактном виде.
50
Что таков объвкт.чо-орнвнтированнов программирование?
Код должен быть написан так, чтобы быть применимым в макси¬
мально возможном количестве случаев.
Последнее правило очень важно. Я сформулирую некоторые след¬
ствия из него. Если вы иезнаксмы с ООП, вам кое-что будет не
вполне понятно, и я дам некоторые вводные объяснения. Если вы
не поймете их с первого раза, то у вас будут еще возможности. Как
я заметил ранее, с использованием механизма наследования мож¬
но строить специализированные версии существующих классов,
наследующих поведение родовых. В этом состоит один из ключей
к модульности в ООП, Если при проектировании программы вы
задумаетесь чуть-чуть о всех подобных программах, то сможете
написать более общий код. Рассмотрим пример, из которого будет
ясно, что я имею в виду.
Представим, что мы пишем программу, которая будет использова¬
на неопытными пользователями. Вначале мы напишем упрощен¬
ную версию программы, а затем поэтапно добавим более сложные
возможности. Рассмотрим небольшую часть программы, отвечаю¬
щую за ввод-вывод файлов. Для начала мы разработаем класс FILE
с методами readFile и writeFile для чтения и записи файлов. Они
неплохо работают, но не предусматривают средств защиты для
неопытных пользователей. Далее, когда мы будем писать оконча¬
тельную версию системы, содержащую дополнительные средства
защиты, нам не понадобится переписывать методы readFile и
writeFile. Мы создадим подкласс SAFEFILE класса FILE с собст¬
венными версиями этих двух методов. Начнем с того, что будем
контролировать наличие достаточного для размещения буферов
объема оперативной памяти и места на диске. Затем напишем
новую версию метода writeFile, которая вначале вызывает этот
проверочный код, а затем обращается к родительской версии
метода. Если первоначальная версия writeFile была корректной,
созданный метод будет применим в любой программе, которая
что-либо записывает на диск. Нам не потребуется более переписы¬
вать этот метод, разве что мы захотим увеличить его производи¬
тельность. Недостающие средства могут быть добавлены
описанным модульным способом.
Из сказанного становится понятна важность родового кода. Опи¬
санный класс FILE не был создан специально для разрабатываемой
программы. В идеале такие классы пишутся единожды, в расчете
на все будущие программы. Подкласс SAFEFILE был написан в
расчете на нашу программу, но нет причин, по которым он не мог
бы быть вставлен в другой код с другими данными. Это функцио¬
нально завершенная программная единица, которую включить в
другую программу столь же просто, как сменить лампочку. Лам¬
почка ничего не знает ни о форме абажура, ни о материалах, из
которых он сделан. Ее задача светить, и благодаря модульному
проектированию она займет точно то же место, что и старая. В
Дополнительные заботы программиста
51
случае объектно-ориентированного программного обеспечения
этот принцип доведен до совершенства. Мы изначально знаем, что
новые части без всяких проблем заменят старые, поскольку они
являются их наследниками. Они наследуют все, что мы пожелаем,
от родителей и предоставляют новые услуги.
Как вы увидели из этого примера, я не зря говорил о том, что
программист, использующий ООП, должен думать о системе в
целом. Прежде чем начать работу над программой, он должен
тщательно изучить систему. Давайте уточним это. Совокупность
элементов, из которых строятся объектно-ориентированные про¬
граммы, называется библиотекой классов. С ней-то и должен быть
хорошо знаком программист. Это знакомство позволит ему не
изобретать велосипед и не создавать массу кода, дублирующего
существующие в библиотеке классов средства. Однако для этого
необходимо знать, какими возможностями обладает библиотека
классов и как это Может быть использовано.
На практике фотографическая память от программистов, исполь¬
зующих ООП, не требуется. Они подобно остальным полагаются
на справочные материалы. Был разработан удивительный инстру¬
мент, в котором соединены система экранных подсказок и окру¬
жение для разработки и редактирования программ. Этот
инструмент называется броузер (Browser). Популярность ему при¬
несла система программирования на языке Смолток.
Благодаря броузеру, программист получает отличный обзор биб¬
лиотеки классов, имея инструмент для более детального изучения
отдельных классов и их поведения. Не покидая этого средства, он
может проектировать и кодировать собственную программу. Это
основной, самый важный инструмент для объектно-ориентирован¬
ного программирования. Одну из ключевых возможностей про¬
граммы Browser предоставляет окно, в котором находится список
всех классов системы. При выборе одного из классов в специаль-
ных окнах отображаются его локальные функции и переменные.
Затем при выборе одного из методов на отдельной панели высве¬
чивается его код. Обычно в системе присутствуют средства для
добавления и удаления классов из библиотеки. В результате
появляется гибкий инструмент, сочетающий возможность про¬
смотра ресурсов системы с разработкой и кодированием новых
программ. Программа Browser — это не просто визуализатор, Это
основной, интегрирующий инструмент, который помогает одно¬
временно рассматривать существующую систему и разрабатывать
программный проект.
Гпава 3
Я ОБЪЕКТНО-ОРИЕНТИРОВАННЫЕ
РАСШИРЕНИЯ СИ: ЯЗЫКИ
И ИНСТРУМЕНТАЛЬНЫЕ
СРЕДСТВА
"Си++ — молодой язык, e нем имеется множество темных закоулков,
из которых не видно хороших выходов”
Справочник по компилятору Zortech С++
Эта гдава будет полезна читателю, программирующему на Си и
интересующемуся объектно-ориентированными инструментами,
основанными на Си, и расширениями самого языка. В настоящее
время растет число инструментов, позволяющих программировать
на объектно-ориентированных диалектах Си для окружения
Windows. Те, кто решился использовать один из них, найдут здесь
кое-какую информацию, касающуюся этих становящихся все бо¬
лее популярными языков и инструментов.
■ ОБЗОР СРЕДСТВ СИ++ 2.0
Язык Си++ представляет собой объектно-ориентированное расши¬
рение языка Си, разработанного Бьерном Строуструпом для ком¬
пании АТ&Т. Архитектура Си++ основана на классах,
понимаемых как определяемые пользователем типы. Как вы по¬
мните, классами в объектно-ориентированных языках называют
шаблоны, по которым может быть изготовлено неограниченное
число экземпляров, и родовой код, который может быть исполь¬
зован в более специализированных классах. Из этого не видно
никакой непосредственной связи с типами данных. Реализация
классов в Си++ как способа определения пользовательских типов
представляет собой специфичный для Си подход к объектно-ори¬
Структуры и классы
53
ентированному программированию, призванный обеспечить есте¬
ственный способ расширения языка.
Си++ использует четыре спецификатора класса памяти: auto,
extern, register и static. Я специально буду называть их классами
памяти, чтобы не путать их с классами в смысле ООП. В табл. 3.1
перечислены зарезервированные слова языка Си++.
Таблица 3.1. Зарезервированные слова Си++
asm
auto
class
const
do
double
float
for
inline
int
overload
private
return
short
struct
switch
unsigned
virtual
break
case
continue
default
eke
enum
friend
goto
long
new
protected
public
signed
sizeof
this
template
void
volatile
catch char
delete
extern
if
operator
register
static
typedef union
while
■ СТРУКТУРЫ И КЛАССЫ
Каждая структура или класс Си++ содержит набор данных, кото¬
рые могут принадлежать как базисным типам, так и объединениям
или другим структурам или классам. Как структуры, так и классы
могут содержать функции-члены. Как это проявляется при коди¬
ровании? Си++ просто использует ключевое слово class для зада¬
ния новых классов. Приведем пример определения класса set
(множество):
class set {
struct set_member{
int member;
set_member* next;
set_member(int m,set_member*n);
};
};
Гораздо чаще класс определяется как подкласс какого-либо из
существующих классов. Например, мы можем написать такое
определение класса competitor (конкурент):
class competitor: public business{
//.. .
} /‘
Это означает, что класс competitor является подклассом класса
business.
54 Объектно-ориентированные расширения Си: языки и инструментальные средства
■ МНОЖЕСТВЕННОЕНАСЛЕДОВАНИЕ
Возможность производить подкласс одновременно более чем от
одного класса — очень существенное свойство языка Си++. При
определении класса может быть указан список, перечисляющий
все родительские классы, у которых данный класс наследует
содержащиеся в них данные и функции-члены. Для определения
класса, наследующего более чем от одного родительского класса,
используется расширенная нотация. Приведем пример:
class competitor: public business,public adversary{
//.. .
} ;
Класс competitor (конкурент) определен как производный одновре¬
менно двух классов: business и adversary (противник).
■ ФУНКЦИИ-ЧЛЕНЫ
Методы, которые в Си++ называют функциями-членами, задаются
посредством двойного двоеточия (::). По синтаксису вначале ста¬
вится имя класса, затем двойное двоеточие, а после него имя
функции. Например
set::setsize
определяет setsize как функцию-член класса set. Допускается
создание объектов типа void*, представляющих собой объекты
неизвестного типа.
■ РЕГУЛИРУЕМАЯ СТЕПЕНЬ ИНКАПСУЛЯЦИИ
Си++ обладает уникальным количеством опций, регулирующих
степень инкапсуляции данных объекта. Элемент может быть оп¬
ределен как friend (друг), private (приватный), protected (защи¬
щенный) или public (общедоступный). По умолчанию элементы
класса считаются приватными. Это означает, что доступ к ним
ограничен функциями-членами данного класса. Если элемент оп*
ределен как protected, он будет во всем подобен приватным, за
исключением того, что будет разрешен доступ к нему функциям —
членам классов наследников.
■ ФУНКЦИИ-ДРУЗЬЯ
В Си++ могут быть определены функции-друзья, которые, не
будучи функциями-членами класса, имеют доступ к защищенным
переменным класса. Зачем это нужно? Во многих случаях функ¬
ция производит операции над объектами разных классов, не
связанных отношениями наследования. Рассмотрим, например,
матрицу и вектор. Если операция одновременно выполняется и
Перегрузка операторов и функций
55
над матрицей, и над вектором, то ее следует определить как
функцию-член одного из классов, скажем класса ’’матрица”, и
указать в качестве функции-друга в другом классе. При этом она
получит доступ ко всем необходимым для выполнения операции
элементам.
Создание экземпляра класса сводится к объявлению элемента, так
же как это происходит и для других типов данных. Ключевое слово
volatile при декларации объекта сообщает компилятору, что объ¬
ект может изменять значение в результате неявных действий.
Возможность декларировать переменную в любом месте блока
кода, а не только в его начале, отличает Си++ от Си. Это удобно,
поскольку вы можете располагать объявления переменных рядом
с использующими их операторами. Другое существенное различие
между Си++ и ANSI Си связано с семантикой декларации
void fun () ;
которая в Си++ означает, что функция не имеет ни одного пара¬
метра, а в ANSI Си — что у функции имеется неизвестное число
аргументов неизвестных типов. Определять функцию без аргумен¬
тов лучше всего следующим образом:
void fun(void);
В этом случае места разночтениям нет. Эта декларация имеет один
и тот же смысл в ANSI Си и в Си++.
Есть и другие правила, которым необходимо следовать во избежа¬
ние коллизии имен с названиями внутренних объектов компиля¬
тора. Главным является запрет на использование символа
подчеркивания в качестве первого или последнего знака иденти¬
фикатора. Таким образом, на идентификаторы _wrong и wrong_
наложено табу.
■ ПЕРЕГРУЗКА ОПЕРАТОРОВ И ФУНКЦИЙ
Перегрузка позволяет операторам и функциям, работающим с
различными типами данных и классами, использовать одно и то
же имя даже в тех случаях, когда новые операторы и функции
кодируются совершенно иным способом.
Подобные вещи выглядят неуклюже в языках со статическим
контролем типов и разрешением всех связанных с типизацией
проблем во время компиляции. Языкам обработки списков и
интерпретируемым языкам, таким, как Лисп, Пролог и Смолток,
вполне свойственны функции, способные оперировать данными
многих типов, не требуя при этом различного кода для разных
типов операндов. Именно это вносит определенное изящество в
программы, поскольку всегда удается делать вызовы функций
логичными, уместными и легко запоминаемыми.
56 Объектно-ориентированные расширения Си: языки и инструментальные средства
Перегрузка операторов — это изначально присутствующая в Си
возможность, позволяющая операторам, к примеру +, работать с
целыми числами, числами с плавающей точкой и указателями.
Для определения новых функций, которые перегружают имеющи¬
еся, используется ключевое слово overload. Так, например, если
мы хотим определить универсальную функцию печати, следует
записать:
overload
print (int) ,print (double) ,print (long) ,print (char*) ,k
В Си++ имеется полезная возможность определять функции, при¬
нимающие неопределенное число параметров. Такие средства
обычно ассоциируются с языком Лисп и подобными ему. Для
задания неопределенного списка аргументов служит многоточие.
Например, декларация может иметь вид:
int read( char*...);
Ее можно использовать вместе с произвольным числом идущих
подряд аргументов.
■ ПРОТОТИПЫ
При задании прототипа новой функции обычно указываются че¬
тыре ее характеристики: 1) имя, 2) тип возвращаемого результата,
3) *сласс памяти и 4) число и тип передаваемых ей параметров.
Прототип или декларация функции имеет вид:
класс-памяти тип имя-функции (список-параметров);
Пример прототипа функции для Си++ имеет вид
extern int multiply (int x,int у, ...);
В этой декларации многоточие означает, что функция может
принимать более двух аргументов. В прототипе функции разреша¬
ется определять значение по умолчанию для ее параметров. Для
этого мы можем написать:
extern int multiply (int x=2,int y=3, ...);
В таком случае вызов функции может вовсе не содержать парамет¬
ров
multiply () ;
Будет возвращено произведение выбираемых по умолчанию зна¬
чений, т.е число 6. В Си++ статические функции-члены играют ту
же роль, что й методы классов в других объектно-ориентирован¬
ных языках. Эти функции могут быть вызваны без ссылки на
какой-либо конкретный экземпляр класса. То же самое относится
и к статическим данным класса, играющим роль переменных
класса в других объектно-ориентированных языках.
Виртуальные функции и классы
57
■ ВИРТУАЛЬНЫЕ ФУНКЦИИ И КЛАССЫ
Концепция виртуальных функций — одна из наиболее сложных
и трудных для понимания в Си++. Сами по себе виртуальные
функции дают ограниченный вариант позднего связывания. Когда
функция-член объявляется виртуальной, она может быть переоп¬
ределена в любом из производных классов. На первый взгляд это
не отличается от перегрузки функций, также позволяющей пере¬
определять функции в других классах. Однако имеется существен¬
ное различие. На деле перегрузка функций означает лишь
перегрузку имен, которые компилятор "расщепляет” на несколько
разных внутренних имен. Перегруженные функции, как правило,
никак не связаны друг с другом. Они просто изображаются с
помощью одного и того же имени. Виртуальные функции, напро¬
тив, полагаются на дополнительные структуры данных, поддер¬
живающие связь между разными версиями функции. Они
используют механизм косвенных указателей на функцию. В этом
месте во время выполнения допускается модификация. Хотя поч¬
ти все в коде скомпилировано и жестко связано, благодаря при¬
менению указателей на указатели модификация данных может
изменить вызываемую функцию. Мы имеем имя, указывающее на
другой указатель, который, в свою очередь, указывает на функ¬
цию. Если адрес этого второго указателя заменить на адрес указа¬
теля другой функции, то во время выполнения будет вызвана она.
Для формализации этого механизма служат виртуальные \ функ¬
ции. Другое важное различие между перегруженными и виртуаль¬
ными функциями состоит в том, что при переопределении
виртуальной функции она остается верной форме исходной функ¬
ции. Она должна иметь то же число параметров тех же типов. Это
происходит из-за того, что связь между ними не ограничивается
использованием одного имени. В исходной версии Си++ Строуст-
руп предполагал использовать виртуальные функции для написа¬
ния интерактивных программ, в которых пользователю нет
необходимости точно знать способ достижения результата. Он
определил это так: ”Этот стиль особенно удобен в интерактивных
программах, когда базовое программное обеспечение не делает
различия между объектами разных типов. Пользователь в типич¬
ном случае указывает на объект и спрашивает: ”Кто ты?” или
”Вьшолни свое делоГ’ При этом не сообщается ничего о типе
объекта. Программа может и должна понимать это самостоятель¬
но.” (Строуструп Б. Язык программирования Си++. — М.: Радио
и связь. — 1991. — С. 37).
Виртуальные классы не имеют ничего общего с виртуальными
функциями. Они возникли из-за потребности управлять механиз¬
мом множественного наследования. Обычно, если класс наследует
58 Объектно-ориентированные расширения Си: языки и инструментальные средства
у двух классов, имеющих общего предка, он ведет себя так, как
если бы содержал несколько копий данных и функций этого
общего предка. Если предок определен как виртуальный, то во
всех его наследниках используется единственный экземпляр дан¬
ных и функций независимо от того, сколькими путями он косвенно
унаследован у предка.
■ КОНСТРУКТОРЫ И ДЕСТРУКТОРЫ
Экземпляр данных декларируется подобно любой другой структу¬
ре данных, однако все экземпляры должны быть инициализиро¬
ваны. Это можно сделать, вызвав функцию-член, называемую
конструктором. Конструкторы должны кодироваться для каждого
класса объектов отдельно, поскольку они представляют собой то,
что в действительности создает объект. Кроме конструкторов
существуют деструкторы, которые уничтожают объекты, освобож¬
дая занятую ими память и делая ее значение неопределенным.
Си++ не содержит множества встроенных средств для обеспечения
ввода-вывода. Они могут быть добавлены разработчиками. Однако
стандартизация в этой области имеет критическое значение. В
Си++ существуют три вида областей видимости: локальная, файл
и класс. Локальная область видимости предполагает задание имен
в блоке кода и их локализацию внутри него. Область видимости
класса соответствует именам, определенным внутри класса и не¬
видимым вне операций, принадлежащих этому классу. Область
видимости файла содержит имена, объявленные вне блоков и
классов.
Для получения готовой программы на Си++ обычно требуется
выполнение четырех шагов: 1) предварительная обработка,
2) трансляция, 3) компиляция и 4)связывание. После всего ска¬
занного многим захочется попробовать написать что-нибудь на
этом языке. Что представляет собой обычная программа на Си++?
Сколь легко ее читать? Чтобы помочь ответить на этот вопрос, я
включил пример программы, реализующей хорошо известный
алгоритм решета Эратосфена. Вы можете оценить Си++ по листин¬
гу 1. Обратите внимание на то, что для выделения комментариев
вместо обычной для Си комбинации /* используется пара //.
ЛИСТИНГ 1
#include am.h
class prime {
int n; //номер простого числа
int p; //само простое число
public:
// Конструктор
prime( int nn=l,int pp=2){ n=nn;p=pp; }
/ / Переход к следующему простому числу
Конструкторы и деструкторы
59
prime& operator++();
// Определяет номер простого числа
int n_th() {return n}
/ / Определяет значение простого числа
int prm() {return p}
i,
// Вывод простого числа
ostream& operator<(ostream& s,prime &p)
{
return s<p.prm();
//Переход к следующему простому числу
prime& prime::operator++()
{
++n; // Увеличим порядковый номер на 1
// Специальный случай для 2
if(p==2)
//Следующее простое равно 3
++p;
return (*this);
1/
// Ищем следующее нечетное простое число
for (;;)
{
p =+ 2;
int is_prime = 1;
int loop_limit = (p/2);
for (int a=3; а; ++а)
//Число p простое?
if ( ((p/а)*a)==p)
{
is_prime = 0; break;
}
1/
// Мы нашли простое?
if(is_prime)
break;
// Не простое, смотрим дальше
}
return(*this);
1/
// Простейшая головная программа, использующая класс
// prime.
main()
{
const int n_primes = 100; //Напечатать 100 простых чисел
prime number;
while (number.n_th())
{ //
// Печатаем номер простого числа и само число
cout<number.n_th()<"\t"<number<"\n";
// Переходим к следующему простому
++number;
}
60 Объектно-ориентированные расширения Си: языки и инструментальные средства
■ КОМПИЛЯТОР ФИРМЫ ZORTECH
К числу достоинств этого быстрого и гибкого компилятора с языка
Си++ относится поддержка разработки программ для MS
Windows. Этот Си++ в отличие от многих систем представляет
собой не препроцессор, а полноценный компилятор. Интегриро¬
ванная среда дает возможность компилировать программы, не
выходя из редактора. Компилятор Zortech позволяет отключить
пропуск неявных определений функций. По умолчанию использу¬
ется режим автопрототипирования. Он состоит в том, что при
использовании функции, у которой отсутствует прототип, компи¬
лятор изготовит его в соответствии со способом вызова функции.
Zortech С++ стремится выйти за рамки языка программирования
и стать средой программирования. Полная система включает
редактор, отладчик и библиотеку инструментальных классов.
■ МОДЕЛИ ПАМЯТИ
Компилятор Zortech поддерживает 5 моделей памяти: ТДМ,С и
L. Их спецификации приведены в табл. 3.2. По умолчанию исполь¬
зуется малая (small) модель памяти. Архитектура определяется
выбором длинной или короткой схемы адресации. Ближние (near)
адреса позволяют ссылаться только в пределах текущего сегмента,
дальние (far) адреса дают возможность сослаться на любое место
памяти^ Указатели на функции и данные в Zortech С++ могут быть
как ближними, так и дальними. Различные комбинации приводят
к перечисленным в таблице пяти моделям.
С помощью ключевых слов near и far можно строить программы,
использующие смешанные модели памяти.
С наличием моделей памяти связана проблема, состоящая в труд¬
ности изменения всевозможных адресов при смене модели памяти,
Таблица 3.2.
Модели
памяти
Название
Объем
Объем
Дополнительные
модели
кода
данных
условия
T (Tiny)
64 К
64 К
Код+Статические+
Крошечная
Глобальные < 64K
S (Small)
64 К
64 К
Малая
M (Medium)
1 Мб
64 К
Средняя
С (Compact)
64 К
1 Мб
Компактная
L (Large)
1 Мб
1 Мб
Большая
Отладчик для Си++
61
которая может понадобиться из-за увеличения программы. Дру¬
гим недостатком будет специфичность понятий near и far для
архитектуры i80x86 и связанное с этим отсутствие переносимости
программ.
■ КАРТА ПАМЯТИ ПРОГРАММ
Карта памяти программ Zortech С++ содержит следующие основ¬
ные сегменты:
Code Код
FarData Дальние данные
NearData Ближние данные
Stack Стек
NearHeap Ближняя куча
FarHeap Дальняя куча
■ ОТЛАДЧИК ДЛЯ СИ++
Один из немногих отладчиков для Си++, продукт фирмы Zortech,
представляет собой программу, имеющую самостоятельное значе¬
ние с посвященным ей отдельным руководством. Подобно боль¬
шинству современных отладчиков он требует включения в код при
компиляции специальной информации. Отладчик совместим с
отладчиком CodeView фирмы Microsoft, но требует, чтобы вклю¬
ченная в код отладочная информация была преобразована к его
формату. При компиляции для включения отладочной информа¬
ции необходимо указать опцию -g. Поскольку подготовка програм¬
мы, состоящей из нескольких модулей, значительно сложнее,
рекомендуется использовать для такой компиляции программу
Make. В отличие от ориентированного на работу в терминах строк
CodeView, отладчик для Zortech С++ ориентирован на операторы,
т.е. наименьшей обрабатываемой единицей служит не строка кода,
а оператор.
Основной экран отладчика разделен на три части. Первая — это
строка меню, вторая, занимающая большую часть экрана, содер¬
жит 3 окна. В нижней строке отображаются команды, доступные
при помощи функциональных клавиш. Отладчик работает в двух
основных режимах: в режиме оконных команд (Window Control
Mode) и в режиме меню (Menu Control Mode). Первый используется
по умолчанию. Основное различие между этими двумя режимами
состоит в обработке нажатий клавиш. Программа поддерживает
использование мыши, при работе с ней различие между этими
двумя режимами менее заметно.
Несмотря на то что отладчик начинает работу с тремя окнами,
пользователю доступны 15 окон плюс окно подсказки. Говоря о
первых трех окнах, заметим, что в первом и самом большом из
них отображается текст программы или машинные коды, в одном
62 Объектно-ориентированные расширения Си: языки и инструментальные средства
Таблица 3.3. Окна отладчика Zortech С++
Клавиша
Окно
Подсказки
Автоматических переменных
Файлов
Функций
Данных
Буфера трассировки
Условных точек остановки
Выражений
Просмотра памяти
Исходного текста
Регистров
Директория
Символов
Сопроцессора i8087
Классов
Буферов
F1
F2
F3
F4
F5
F6
F7
F8
F9
F10
Alt-Fl
Alt-F2
Alt-F3
Alt-F4
Alt-F5
Alt-F6
из меньших — данные, а в другом — имена автоматических
переменных. В каждый момент времени пользовательский ввод
’’фокусируется” только в одном окне отладчика. В табл. 3.3
приведены список окон отладчика и функциональные клавиши, с
помощью которых они появляются на экране. Кроме упомянутых
окон в отладчике имеется экран вывода программы, отображаю¬
щий результаты вывода из программы на экран. Обычный экран
отладчика возникает только при остановке отлаживаемой про¬
граммы. Отладчик может работать в одном из двух режимов: с
использованием исходного текста или отображая ассемблерные
команды.
Для отладки программ на Си++ в отладчик добавлены окно Class
и средство для ’’расщепления” имен перегруженных функций. В
окне Class в алфавитном порядке перечислены все классы, исполь¬
зуемые в программе. Изначально видны только имена классов.
Каждое из имен можно раскрывать, получая список базовых для
данного классов, список прототипов функций, список функций-
членов, расположенные лесенкой. Если поместить курсор на имя
функции-члена и нажать ENTER, то в окне Source появится
исходный текст этой функции. Нажатие Alt-E переводит в режим
редактирования этого текста.
Если класс наследует от одного или нескольких базовых классов,
то ситуация несколько сложнее. Переменные, унаследованные от
этих классов, могут находиться в окнах автоматических перемен¬
ных, данных и выражений, скрытые внутри объектов базового
Заключительные замечания о Си++
63
класса. Для того чтобы увидеть его содержимое, может понадо¬
биться раскрыть его.
В отладчике предусмотрена зашита памяти по записи. Это не
означает, что отладчик не позволяет записывать в определенные
места памяти. Он только отслеживает такие операции. При первой
записи в защищенную область памяти на экране появляется
сообщение, а все дальнейшие обращения протоколируются для
последующего анализа. Это средство используется в сочетании с
окном просмотра памяти.
■ КЛАССЫ БИБЛИОТЕКИ ZORTECH TOOLS
Bitvec
Slist
Gslist
Gqueue
Gstack
Iqueue
fetack
Dnode
Dlist
Gdlist
Dynarr
GDynArr
Node
Bintree
Hash
Ghsearch
Bcd
Gvma
Gvms
Rt_clock
Directory
Filename
Lntvector
Ce_handier
3tring_editor
Date_editor
Window_set
Window
Money
Event
Event_queue
Самыми интересными из перечисленных классов .* считаю Dlist,
Dnode, Dynarr, Rt_clock, Event и Event_queue. Любопытны также
и классы Node и Bintree.
Наиболее противоречивые чувства вызывают классы, р^ализу^-
щие виртуальную память. Очевидно, не стоит судить о них, кг
увидев, как они работают. Несмотря на то что дин^мычеокис
массивы не столь привлекательны по сравнению с массивами>
способными к росту во время выполнения, их все-таки полезно
иметь.
■ ЗАКЛЮЧИТЕЛЬНЫЕ ЗАМЕЧАНИЯ 0 СИ++
У языка Си++ я вижу два основных недостатка: 1) неясный
синтаксис объектно-ориентированных операций и 2) отсутствие
динамического связывания и типизации. Сразу же должен пре¬
дупредить, что механизм виртуальных функций дает средства
динамической типизации. Однако по-прежнему сущестъует мно¬
жество причин по которым желательно иметь связывание во время
выполнения. Это особенно важно для программ, которые должъ.ы.
эффективно работать в ситуациях, явно не предусмотренных прсь
граммистом.
64 Объектно-ориентированные расширения Си: языки и инструментальные средства
Си++ — необычная, но интересная попытка создания нового типа
объектно-ориентированного языка. Если обратиться к перечислен¬
ным ранее критериям, то Си++ содержит отсутствующий в боль¬
шинстве объектно-ориентированных языков для
микрокомпьютеров механизм множественного наследования. В то
же время у него отсутствуют связывание периода выполнения и
средства комбинации методов. Одним из нововведений языка Си++
стали защищенные члены, а именно разрешение доступа к ним со
стороны классов-наследников. Я считаю, что эту возможность следу¬
ет включить и в другие объектно-ориентированные системы.
Некоторые новые термины, скажем использование ключевого
слова static в сочетании с переменными и функциями класса,
следует признать полезными. Ключевое слово static выявляет
возможность использования переменных класса в качестве кон¬
стант во время выполнения. Я полагаю, что и изменение их во
время выполнения может оказаться полезным.
В список пожеланий к Си++ следует добавить стандартную биб¬
лиотеку классов, включающую, вероятно, такие возможности, как
ввод-вывод. Важно иметь иерархию стандартных структур дан¬
ных. Библиотека Zortech Too!s — существенный шаг в этом
направлении. Заключенные в ней идеи могут быть использованы
и в других объектно-ориентированных диалектах Си. Другим
пожеланием, которое может быть реализовано только при наличии
связывания периода выполнения, будет динамическое переопреде¬
ление классов, подобное реализованному в Common Lisp Object
System. Идеям еще надо дать оформиться, прежде чем будет
достигнуто соглашение по объектно-ориентированному Си. Я ду¬
маю, что разработчики всех диалектов согласятся, что хотя в этой
области достигнут существенный прогресс, объектно-ориентиро¬
ванные расширения Си — это новое направление, в котором,
прежде чем принимать окончательные решения, требуется нако¬
пить опыт и набор подходов.
■ ЯЗЫК OBJECTIVE-C
Первым реализованным объектным расширением Си был
Objective-С корпорации StepStone. Он был разработан программи¬
стами, работающими на Си, которые стремились использовать
преимущества ООП при работе над традиционными программны¬
ми проектами. Несмотря на такую направленность, я считаю, что
Objective-C — это многообещающее окружение для разработки
самых современных приложений.
Текущая версия представляет собой относительно законченную
реализацию Objective-C, отражающую многолетний опыт решения
проблем, с которыми сталкиваются^ программисты. Из-за сложно¬
Язык Objective-С
65
стей с некоторыми классами в предыдущих версиях базовой
библиотеки она непрерывно подвергалась переработке. Таким об¬
разом, существующая иерархия классов представляет собой резуль¬
тат нескольких лет эволюции.
Компилятор Objective-С состоит из двух выполняемых файлов:
программы-драйвера OBJCC.EXE и самой программы Objective-C
(файл OBJC.EXE). Драйвер вначале вызывает программу CL.EXE
фирмы Microsoft, которая проверяет синтаксис, а затем компиля¬
тор с Objective-C. Во время связывания не нужно указывать
библиотеки, поскольку ссылки на них встраиваются в .OBJ файлы
самим компилятором.
Возможность динамического связывания помещает Objective-C в
категорию средств, пригодных для построения серьезных, харак¬
терных для реальной жизни приложений. Это ему удается несмот¬
ря на то, что он компилирует код в Си, а далее работает обычный
Си-компилятор. Гибридность языка Objective-C существенно отли¬
чает его от других объедтно-ориентированных систем. Он похож
на объектно-ориентированный Лисп тем, что позволяет писать на
базисном языке (в данном случае — Си). Objective-C и Смолток
имеют существенно разные объемы доступного для повторного
использования кода в исходных текстах. Хотя Objective-C и пред¬
лагает в этой области значительно больше, чем Си++, он сущест¬
венно отстает от Смолтока, самого старого из
объектно-ориентированных языков.
В определенном отношении Objective-C скорее консервативный
объектно-ориентированный язык. В нем нет новых концепций по
сравнению с теми, что появились в Смолтоке много лет назад. В
нем реализованы даже не все возможности Смолтока. Если рас¬
сматривать его как гибрид Смолтока и Си, то доминировать
безусловно будет Си. Если рассматривать отличия этого языка от
Си, мы обнаружим один новый тип данных, объекты и одну новую
операцию — сообщение.
Синтаксис Objective-C почти везде прост. Для объявления новых
классов йспользуется знак равенства (=), двоеточие служит для
указания суперкласса. Другие элементы объявления класса выде¬
ляются скобками. Все объявления данных выделяются фигурны¬
ми скобками. Например
=Array:Object {short capacity;}
объявляет Array как подкласс класса Object с переменной capacity
(емкость), определенной как короткое целое. Как и в Смолтоке
имеются два типа методов: методы классов и методы экземпляров.
Для их выделения служат знаки плюс (+) и минус (-) соответст¬
венно. Как и в Смолтоке, для создания экземпляра следует послать
сообщедае (new) соответствующему классу. В Objective-C имеются
3-857
66 Объекгнооривнтированные расширения Си: языки и инструментальные средства
два типа выражений-сообщений: унарные выражения и ключевые
выражения. Бинарных выражений, подобных имеющимся в Смол-
токе, нет. Выражение
id myarray = [ByteArray new:80];
создает новый экземпляр класса ByteArray размером 80 байт.
Определение метода (new) для класса Object имеет вид:
+new {return (*_alloc)(self,0);}
При этом для размещения объекта в памяти вызывается встроен¬
ный примитив. Более ничего не требуется, поскольку Object — это
самый простой абстрактный класс. Сообщение new: (new с двоето¬
чием) класса Array требует следующего определения средствами
Objective-C:
+new:(mt)nElements {
self = (*_alloc)(self,nElements*[self ndxVarSize]);
capacity = nElements; return self;}
В Objective-C все сообщения помещаются внутри квадратных
скобок. Таким образом, выражение [self ndxVarSize] — это сооб¬
щение. Сообщение ndxVarSize — это метод классаАггау, который
переопределен следующим образом:
+(int)ndxVarSize {return (int)[self
subclassResponsibil..ty] ; }
Метод subclassResponsibility просто печатает текст: ”Подкласс
должен переопределить сообщение”. Выражение
capacity=nElements просто устанавливает емкость массива равной
передаваемому с ’new:’ параметру.
■ ИЕРАРХИЯ КЛАССОВ
Ниже приведена иерархия базисных классов Objective-C в том
виде, в котором ее мог бы отобразить System Browser, если бы он
существовал в системе Objective-C:
Object
Array
BytArray
IdArray
IntArray
Assoc
Cltn
OrdCltn
Set
Bag
Dictionary
Stack
AsciiFiler
BalNode
Иерархия классов
67
SortCltn
IPSequence
Sequence
ObjGraph
Point
Rectangle
String
Unknown
B Objective-C классы называют объектами-фабриками. Этим спо¬
собом подчеркивается то, что класс представляет собой объект,
основная функция которого создавать экземпляры и подклассы.
Однако, как вы могли заметить, отсутствуют классы Class и
Metaclass. В Objective-C классы не являются экземплярами мета¬
класса и не создаются при помощи посылки ему сообщения, как
это принято в Смолтоке, Xerox LOOPS и многих объектных рас¬
ширениях Лиспа. В этом нет ничего плохого. Это просто отражение
того факта, что Objective-C — гибридный объектно-ориентирован¬
ный язык.
Библиотека классов Objective-C состоит из четырех разделов: ба¬
зовые классы, коллекции, классы ввода-вывода и классы других
типов данных, например String. Как видно из таблицы, в новой
иерархии отсутствует класс AVL. Вместо него добавлены новые:
абстрактный класс BalNode и его подкласс SortCltn. BalNode
содержит родовой код, поддерживающий реализацию произволь¬
ных бинарных деревьев. Занимающийся упорядоченными набора¬
ми класс SortCltn заменил AVLTree. Другим существенным
изменением стала реализация класса Sequence в качестве подклас¬
са IPSequence. Последний реализует сортировку произвольной
коллекции на месте. Чтобы воспользоваться реализованной в
классе IPSequence техникой, в класс Collection включен метод
contents, возвращающий указатель на экземпляр IdArray, кото¬
рый используется клиентом для запоминания элементов коллек¬
ции. Новый класс AsciiFiler выполняет все свойственные файлам
операции и позволяет обмениваться текстами между подключен¬
ными к сети машинами разных архитектур.
ObjGraph — еще один интересный класс. Он используется для
создания графа иерархии классов, включающей все классы, от
которых он наследует. В руководстве описан пример использова¬
ния этого класса для реализации метода broadcast, который пол¬
учает в качестве аргумента имя метода и рассылает его всем
доступным объектам. Метод asGraph класса Object был переписан
в соответствии с новым способом построения графов.
Ранее я показывал некоторые операции над классом Array. В
Objective-C класс Array реализован иначе, чем в других объектно¬
68 Объектно-ориентированные расширения Си: языки и инструментальные средства
ориентированных языках. В Смолтоке класс Array является под¬
классом класса Collection, хотя и не непосредственным. В
Objective-С Array — это формальный или, иначе говоря, абстрак¬
тный класс, непосредственный потомок класса Object. Так сделано
по соображениям эффективности, поскольку в Си массивы уже
реализованы.
Новым в реализации класса Array будет доступ к переменным
экземпляров не по имени, а по индексу. Число элементов в массиве
фиксировано. В отличие от более хитроумных классов коллекций,
речь о которых пойдет позже, размер массивов не может быть
увеличен, после того как число элементов достигло указанного для
данного массива максимума. Подклассы Array также имеют дело
с массивами элементов разных типов. Как и в большинстве объ¬
ектно-ориентированных языков, класс Array не позволяет опреде¬
лять многомерные массивы. Для этого необходимо заводить
специальные подклассы данного класса.
Как мы видим, емкость классов массивов в Objective-С фиксиро¬
вана. Если создан массив определенного размера, нет способа
изменить его длину. Однако для коллекций это не так. Они
разрабатывались как ’’растущие” классы, которые могут включать
больше элементов, чем было изначально задано.
Предназначенный для этого метод expand записывается так:
expand {contents=[contents capacity:
capacity+=capacity];
return self;}
Таким образом на языке определяется метод, изменяющий значе¬
ние переменной contents и удваивающий размер коллекции при
помощи операции инкремента.
В Objective-С объекты рассчитаны на использование единого ад¬
ресного пространства. Они идентифицируются при помощи своих
адресов в оперативной памяти. Из этого следует, что системы,
содержащие объекты,расположенные на диске или в других узлах
компьютерной сети, не могут быть запрограммированы при помо¬
щи Objective-С. В этом языке все объекты должны располагаться
в основной памяти главного компьютера.
Objective-C — это гибридный язык, допускающий ’’жульничест¬
ва”. Это означает, что в отличие от чистых языков типа Смолтока
на Си можно написать код, непосредственно обращающийся к
защищенной памяти объектов. Следует отметить, что это может
привести к большим неприятностям и в корне противоречит идее
инкапсуляции, одной из самых важных в ООП. Программист
должен четко осознавать последствия таких действий. Системы
типа Objective-C позволяют идти на риск в расчете на повышение
производительности.
Коллекции
69
■ КОЛЛЕКЦИИ
Главную роль при создании структур данных играет группа клас¬
сов Collection. Это справедливо не только для Objective-C, но и для
других объектно-ориентированных языков. К этой части иерархии
принадлежат следующие классы:
Cltn
OrdCltn
Stack
Set
Dictionary
Ba g
BalNode
SortCltn
Cltn — это абстрактный (формальный) класс, его методы и пере¬
менные предназначены для использования наследниками этого
класса, которые и будут работать в программах. Предполагается,
что экземпляры будут только у его подклассов. Методы коллекций
подразделяются приблизительно на дюжину категорий: создание
экземпляров, добавление, удаление, упорядочивание, ’’выполне-
ние элемента”, преобразование, печать, освобождение, копирова¬
ние, опрос, сравнение и приватные методы. Чтобы понять их,
нужно познакомиться с тем, как работает коллекция. Непосред¬
ственно структуры данных коллекции используются для хранения
не элементов, а указателей на элементы класса IdArray, который
в действительности и содержит элементы. Методы опроса исполь¬
зуются в приложениях для запросов и поисков, подобных тем, что
выполняются в базах данных и обычных системах. Так, например,
метод find ищет объекты по имени и, если они содержатся в
коллекции, возвращает их.
Методы ”выполнить элементы” последовательно выполняют опре¬
деленную операцию над каждым из элементов коллекции, для чего
каждому из них коллекции посылается сообщение. Одна из про¬
блем состоит в том, что методы требуют различного числа пара¬
метров, а Objective-C не поддерживает функций с переменным
числом параметров. Решение состоит в реализации нескольких
методов elementsPerform, требующих разного числа аргументов.
Предусмотрены версии, принимающие до трех аргументов. Их
можно достаточно просто использовать для построения методов с
большим числом параметров.
Последовательностями называют коллекции, для элементов кото¬
рых существен порядок. Специализированные подклассы класса
OrdCltn реализуют очереди и стеки. В последовательностях не
допустимы элементы NIL. При удалении элементов из последова¬
70 Объектно-ориентированные расширения Си: языки и инструментальные средства
тельностей происходит упаковка, и место, освобожденное удален¬
ным элементом, заполняется. Методы включейия в последователь¬
ность определяют место, на которое следует поставить элемент. В
число методов входят AddFirst (добавить первым), AddLast (доба¬
вить последним), insert:before (вставить до) и insert:after (вставить
после).
Стек — это последовательность, поддерживающая дисциплину:
’’последним пришел — первым вышел”. Кроме методов push и pop
в Objective-C предусмотрены методы at и removeAt для произволь¬
ного доступа к элементам стека. Методы манипуляции стеком
включают также изменение порядка элементов (метод swap) и
доступ к элементам стека без его модификации topElement и
lastElement.
Множества — это коллекции, которые могут содержать ровно по
одному экземпляру каждого элемента. Повторения не допускают¬
ся. Множества особенно эффективны при построении таблиц сим¬
волов. Реализация класса Set допускает в качестве элементов
объекты любых типов. В одном множестве могут содержаться
объекты разных типов. Это означает, что добавление новых эле¬
ментов в множество требует полного просмотра всех существую¬
щих элементов. Класс Set включает возможность хэширования.
При помощи сообщения ’hash’ множество для ускорения поиска
помещает все содержащиеся в нем элементы в хэш-таблицу.
Невозможность динамического изменения существенно ограничи¬
вает применимость описанных здесь множеств. Изменение объекта
множества нарушит корректность механизма доступа.
Класс Dictionary является потомком класса Set и представляет
собой множество ассоциаций. В этом случае разрешены повторя¬
ющиеся значения, но ключи должны быть различны. Это дости¬
гается благодаря тесному взаимодействию с классом Assoc.
Ассоциации хранят связи между ключами и значениями, допу¬
ская запоминание в словарях пар и организуя доступ к ним по
ключу. Ассоциации выполняют сравнения, передавая сообщения
ключам. Кроме методов, унаследованных у класса Set и его
предков, в классе Dictionary реализованы 6 новых методов: метод
with: для инициализации новых словарей и пять методов индекс¬
ного доступа: atKey, atKey:put, values, includesAssociation: и
includesKey:.
Как было сказано раньше, класс SortCltn заменяет использовав¬
шийся ранее класс AVLTree. Элементы упорядоченной коллекции
всегда отсортированы. При добавлении нового объекта он с самого
начала вставляется на нужное место. Способ упорядочивания
зависит от значения переменной cmpsel. По умолчанию использу¬
ется селектор compare. Переменная может принимать и другие
Графика
71
значения: invertCompare и dictCompare. Это имена методов. Мето¬
ды compare и invertCompare реализованы в корневом классе Object.
Метод dictCompare реализован в классе String. Класс SortCltn
ведет себя, как если бы он был подклассом Cltn, хотя это не так.
Как написано в руководстве, определенные в нем операции '’со¬
вместимы по интерфейсу”. Переменная addDupAction также вли¬
яет на поведение объектов класса SortCltn. Она может принимать
одно из 4 значений: ADD Сцобавить), REJECT (отвергнуть), MERGE
(слить) и REPLACE (заменить). Выбор опции определяет способ
обработки дубликатов. Если выбрано значение ADD, дубликаты
разрешаются, для поддержания порядка они вставляются непос¬
редственно после оригиналов. Опция REJECT запрещает повторы.
Как следует из названия, опция MERGE выполняет слияние,
используя для этого метод merge класса элементов.
■ ГРАФИКА
Objeciive-C содержит всего два рудиментарных графических клас¬
са: Point (точка) и Rectangle (прямоугольник). Покуда это так,
рано говорить об объектно-ориентированной графической системе,
однако конструкция этих классов информативна. Класс Point
включает две переменные xLoc и yLoc. К унаследованным у класса
Object добавлены методы, используемые для установки координат
и доступа к их значениям, изменения координат и для выполнения
над их значениями простейших арифметических действий.
Чтобы использовать элементы класса Point для рисования, в
объектно-ориентированных системах обычно предусматривается
что-то вроде реализованного в Смолтоке и Акторе класса Pen, с
помощью которого выполняются основные функции ’’черепашьей”
графики. В таких системах класс Pen наследует классу BitBlt,
ответственному за пересылку битовых блоков. Эти классы не
включаются в систему Objective-С, поэтому, для того чтобы вос¬
пользоваться классами Point и Rectangle, необходимо реализовать
эквиваленты BitBlt и Pen.
Ниже приведены короткая демонстрационная программа на
Objective-С и сгенерированный компилятором листинг на Си. Для
облегчения чтения текст на Си переформатирован.
Листинг 2. Короткая демонстрационная программа на Objective-C
= DemoPoint: Object (Practice) [ int xLoc,yLoc; }
+ create {return [[super new] initialize];}
- initialize {xLoc=100;yLoc=100;return self;}
- print {print ("Координаты этой точки
(%d6%d)\n",xLoc,yLoc) ; }
72 Объектно-ориентированные расширения Си: языки и инструментальные средства
Листинг 3. Выходной код на Си
#line 2 "demo.c"
typedef struct_PRIVATE *id; id_msg(),_msgSuper();
#line 1 "demo.m"
#line 5 "demo.c"
struct_PRIVATE { struct_SHARED *isa;
int xLoc; int yLoc;
};
extern id DemoPoint,Object;
struct_SHARED {
struct_SHARED *isa, *clsSuper;
cha(r *clsName;
char *clsTypes;
shbrt clsSizInstance;
short clsSizDict;
struct_SLT *clsDispTable;
}
extern struct_SHARED _DemoPoint,_DemoPoint;
extern char *Practice[];
#line 1 "demo.m"
#line 3 "demo.m"
/* create = Practice[0] */
static id_l_DemoPoint(self,_cmd)id self;char *_cmd;
{
return_msg(_msgSuper(_DemoPoint.clsSuper,Practice[1]
/*new*/),Practice[2]/*initialize*/);
}
#line 5 "demo.m"
/* initialize=Practice[2] */
static id_2_DemoPoint(self,_cmd)id self; char *_cmd;
{
self-xLoc=100;self-yLoc=100;return self;
}
#line 7 "demo.m"
/*print=Practice[3]*/
static id_3_DemoPoint(self,_cmd)id self;char *_cmd;
{
printf (’’Координаты этой точки (%d@%d) \n",
self-xLoc;
self-yLoc);
}
#line 16 "demo.c"
extern struct_SHARED _Object, _Object;
struct_SLT
{
char **_cmd;
id(*_imp) () ;
};
static struct_SLT _clsDispatchTbl[l] =
{
&Practice[0],(id(*)())_l_DemoPoint,/*create*/
};
static struct_SLT _nstDispatchTbl[2]=
{
&Practice[2],(id(*)())_2_DemoPoint, /*initialize*/
&Practice[3],(id(*)(>i_3_DemoPoint, /*print*/
};
Обсуждение
73
static char _bufClsName[]="_DemoPoint";
struct _SHARED _DemoPoint=
{
&_Object,
&_Object,&_bufClsName[0],0,sizeof(struct _SHARED),1,
(struct _SLT *)_clsDispatchTbl
};
struct _SHARED _DemoPoint=
{
&_DemoPoint,&_Object,&_BufClsName[1],
"#ii",sizeof (struct _PRIVATE),2,
(struct _SLT *)_nstDispatchTbl
};
line 8 "demo.m"
■ СИМВОЛЬНАЯ ОТЛАДКА
Версия Objective-C для MS-DOS позволяет использовать отладчик
CodeView фирмы Microsoft, способный работать даже с синтакси¬
сом Objective-C. Для этого при компиляции нужно пользоваться
опцией -g; выполнив это, вы можете получить в CodeView исход¬
ный текст программы и сможете установить в нем точки останова,
вычислять выражения и т.п. Однако отсутствует возможность
непосредственного просмотра объектов. Чтобы увидеть их содер¬
жимое, необходимо с помощью средств низкого уровня получить
адреса интересующих объектов. Чтобы разобраться в шестнадца¬
теричном представлении, вам потребуется знать структуру храня¬
щейся в них информации. Применение крманд CodeView к
32-битным указателям позволит вам инспектировать значения. Из
этого следует, что часть отладки, которая может быть выполнена
в терминах исходного текста, существенно ограничена.
■ ОБСУЖДЕНИЕ
При появлении систем типа Objective-C неизбежно встает вопрос,
в чем ее преимущества по сравнению с традиционной объектно¬
ориентированной системой вроде Смолтока. Если вы используете
ее в среде MS-DOS и не имеете ”поводыря”, то несомненно обнару¬
жите, что возможности изучения системы в интерактивном режи¬
ме сильно ограничены. В Смолток-системе кроме броузеров и
других оконно-ориентированных средств вы сможете воспользо¬
ваться различными встроенными методами, помогающими изу¬
чить систему и снабжающими в интерактивном режиме всей
необходимой для написания программы информацией. В версии
Objective-C для MS-DOS вам придется положиться на печатную
документацию. По счастью, она написана очень хорошо и отличие
состоит в основном в удобстве. Как и для любых языков програм¬
74 Объектно-ориентированные расширения Си: языки и инструментальные средства
мирования, можно написать разнообразные утилиты, такие, как
программы построения перекрестных ссылок и другие, которые
могут помочь вам.
Следующий вопрос касается размера базовой библиотеки классов,
поставляемой с системой. По этому параметру Objective-C занима¬
ет промежуточное положение между Си++ и более развитыми
окружениями Смолтока и Актора. В определенной степени это
компенсируется возможностью использования существующих
программ на Си для построения специализированных библиотек
классов. Однако в некоторых случаях эта возможность существует
и для других языков. Кроме этого, следует иметь в виду, что
структуры данных периода выполнения Objective-C могут не всег¬
да сочетаться со всеми системами программирования на Си и не
со всеми библиотеками, написанными для Microsoft С. К несча¬
стью, это относится и к программному окружению MS Windows.
Их требования к среде выполнения противоречат друг другу и их
нельзя использовать вместе. В новой версии, однако, этот недоста¬
ток преодолен.
Отсутствие конструкции блока существенно отличает Objective-C
от других объектноориентированных языков, в частности Смол¬
тока и Актора, В этих языках блок кода реализуется как непои¬
менованный объект с отложенным выполнением. Блоки
представляют собой чрезвычайно мощную конструкцию, внося¬
щую .дополнительную степень модульности в определения мето¬
дов. Ведутся исследования по внесению конструкции блока в
Objective-C, однако это средство еще не анонсировано для будущих
версий.
Мир объектно-ориентированного программирования полон мета¬
фор. Метафоры — ”палки о двух концах”. Без сомнения они
выполняют полезную функцию при объяснении основных понятий
этой парадигмы программирования. Но они же являются источ¬
ником заблуждений. Поэтому время от времени их необходимо
пересматрива ть.
В каком смысле разумно сравнивать классы с фабриками? Един¬
ственный реальный их продукт — экземпляры. Подобно фабрике
классы предназначены для выпуска одного продукта. Но напоми¬
нает ли фабрика свою продукцию? Разрабатывается ли класс с
целью эффективного изготовления большого количества экземп¬
ляров? Это показывает, что классы следует уподоблять скорее
лекалам или штампам, чем фабрикам. Однако они превосходят
штампы, поскольку могут быть использованы для изготовления
других штампов. В этом смысле классы еще более гибкое средство,
чем фабрики или штампы.
Ctalk
75
Как обстоит дело с метафорой программной интегральной схемы?
Подобно тому как некоторые микросхемы могут применяться для
построения различных электронных плат, одни и те же классы
можно использовать в разных программах. Но в чем смысл повтор¬
ного использования классов? Важно понять, что классам присущ
совершенно новый подход к повторному использованию, не свой¬
ственный ни библиотечным функциям, ни интегральным схемам.
Вы не можете использовать стандартную микросхему для конст¬
руирования нового чипа. Однако вы, очевидно, можете строить
новые библиотечные процедуры и классы, используя имеющиеся.
Существенное отличие между классами и библиотечными функ¬
циями состоит в том, что классы больше по размеру, более сложны
и используются разными способами. Обычно класс содержит мно¬
жество библиотечных функций и данных, одновременно включа¬
емых в каждое эксплуатирующее класс приложение. Родовые
возможности интегральных схем и библиотечных функций суще¬
ственно ниже.
Синтаксис Objective-C очень удачен. Он более удобочитаем, чем Си
или Си++. Многие критикуют Си за отсутствие наглядности. В
этом Objective-C имеет явные преимущества по сравнению с поро¬
дившим его языком. Вы получаете большинство преимуществ Си,
но на более высоком уровне, с большим числом структур и функ¬
ций, включенных в стандартный набор возможностей языка. В
принципе, Objective-C позволяет создавать большие высокоуров¬
невые приложения, не опасаясь, что они будут существенно завя¬
заны на машинно-зависимые подпрограммы и
специализйрованный код, способные с течением времени стать
обузой. Наконец, Objective-C реализует в среде Си динамическое
связывание. Это означает, что объекты создаются во время выпол¬
нения, а не при компиляции. В этом, кроме более удобочитаемого
синтаксиса, и состоит отличие между Objective-C и Си++.
Я использовал версию 40, перенесенную на ЮМ PC/AT. Этот язык
реализован также на компьютерах VAX, рабочих станциях Sun и
HP 9000. Распространяемая на дискетах высокой плотности вер¬
сия компилятора для MS-DOS включает компилятор, библиотеки,
а также основные классы в исходных текстах. В настоящее время
система работает только с компилятором Microsoft С 5.1. Предпо¬
лагается поддержка версии 6.0.
■ CTALK
Как и следует из названия, Ctalk — это программный инструмент,
который ведет себя как гибрид Си и Смолтока. Он более эффекти¬
вен по сравнению с другими попытками скрещивания, поскольку
внедряет броузер Смолтока в программное окружение Си. Несмот¬
76 Объектно-ориентированн&е расширения Си: языки и инструментальные средства
ря на то что Си не интерпретируемый, а компилируемый язык,
броузер Ctalk VTEWS работает в интерактивном режиме. Кроме
броузера Ctalk включает и другие важные для настоящего объек¬
тно-ориентированного языка средства: наследование, инкапсуля¬
цию и связывание периода выполнения. В системе Ctalk три
основные программы. Кроме броузера имеется препроцессор и
утилита Make. Чтобы понять преимущества Ctalk, я опишу новый
броузер, служащий настоящим ядром, вокруг которого построена
вся система.
Удивительно, насколько броузер CtaUc похож на послуживший
прототипом соответствующий инструмент Смолтока. Как и послед¬
ний, броузер Ctalk представляет собой комбинацию редактора и
менеджера приложений, позволяющий интерактивно и инкремен¬
тально разрабатывать объектно-ориентированные системы. Это
существенное достижение для основанного на Си объектно-ориен¬
тированного инструмента.
Броузер реализован в виде отдельной программы, которая вызы¬
вается так же, как и любая другая программа. Однако, как будет
видно, эта программа предоставляет исключительно могучие сред¬
ства интеграции практически всей системы Ctalk. Она не только
дает возможность познакомиться в интерактивном режиме с ок¬
ружением, но и может быть использована для выполнения в
диалоге множества действий, включая создание шаке-файлов для
пакетной обработки, компиляции и связывания.
Ctalk броузер состоит из нескольких панелей, похожих на распо¬
ложенные вплотную друг к другу окна, которые не могут изменять
размеры и перекрываться. На левой верхней панели отображается
иерархия классов. Как и в броузере Смолтока, если выбрать класс,
то его методы будут видны на правой верхней панели. Если, в свою
очередь, выбрать один из методов, его текст можно редактировать
в нижней текстовой панели. Кроме того, в броузер можно загрузить
произвольный текстовый файл, который будет доступен для редак¬
тирования в нижней панели. Таким образом, броузер, с одной
стороны, небольшой простой редактор, а с другой — специальное
средство для работы с объектами Ctalk.
При работе с броузером на разных панелях доступны разные
всплывающие меню. Они могут быть активизированы нажатием
клавиши или при помощи мыши. В состав броузера входит обо¬
лочка операционной системы, позволяющая выполнять команды
MS-DOS не выходя из броузера. Этот тип оболочек стал настолько
распространен, что его отсутствие кажется странным.
Между броузерами С-talk и Смолтока имеется существенная разни¬
ца, состоящая в том, что первый представляет собой отдельную
программу в компилирующей среде, а второй является частью
Синтаксис Ctalk
77
интегрированной системы, все части которой сосуществуют одно¬
временно, Когда броузер Ctalk загружается в первый раз, он, не
имея внутри себя описаний классов, просматривает текущий ди¬
ректорий на диске в поиске определений классов, а затем для
обеспечения интерактивного доступа к ним компилирует их в базу
данных.
После того как все загрузилось в броузер, пользователю становит¬
ся доступным множество функций, необходимых для создания
приложений. В их число входят средства описания новых прило¬
жений, загрузки существующих классов, определения новых клас¬
сов и их методов и сохранения результатов работы.
Протестированная мною версия еще далека от совершенства. На¬
пример, размер текстовых файлов, которые можно редактировать,
ограничен. Если загружаемый файл превышает максимальный
размер, загружается только часть, но сообщение об ошибке не
выдается. Для неосторожных пользователей это очень опасно,
поскольку внесение небольшого изменения в файл может повлечь
за собой огромные потери данных. По этой причине я должен
сказать, что броузер удобен в первую очередь для работы со
средствами build. Они позволяют создавать множество небольших
файлов, из которых и будут составлены модули законченной
системы. Для больших файлов следует пользоваться другими
редакторами.
■ СИНТАКСИС CTALK
Синтаксис Ctalk слегка отличается от синтаксиса других объект¬
но-ориентированных диалектов Си. Достаточно усвоить несколько
правил, и всякий знакомый с Си без труда научится читать и
писать на нем. Основное соглашение относится к сообщениям
Ctalk, которые всегда ограничены с двух сторон символами @.
Например, если мы хотим создать новый экземпляр класса
Rectangle, сообщение будет выглядеть так:
@Rectangle new_&rect0
Здесь символ & использован для обозначения указателя, т. e. в
обычном для Си смысле. В Ctalk имеется весьма мощный механизм
отображения одних сообщений на другие. Это означает, что селек¬
тор или имя сообщения может быть передан в качестве аргумента
другому сообщению. Синтаксически это оформляется в виде опе¬
ратора присваивания переменной имени селектора сообщения,
заключенного в обратные кавычки.
Для того чтобы послать сообщение, которое ссылается на другое,
запомненное в переменной, в классе Object предусмотрены специ¬
78 Объектно-ориентированные расширения Си: языки и инструментальные средства
альные методы, которые наследуются всеми другими объектами.
Это perform_, perform_with_ и perform_with_with.
Например, пусть переменная gval определена следующим образом:
id gval;
gval = 'getValue'
а объект
id obj;
int gval;
оператор id — это декларация класса id. В программах на Ctalk
все классы, на которые имеются ссылки, должны быть определены
извне. Отображение ‘getValue* можно записать с помощью слёду-
ющего выражения:
0obj perform_ gval with &val0;
Здесь подразумевается, что val — слот в obj, а сообщение ‘getValue4
использует его в качестве аргумента. Как и в большинстве объек¬
тно-ориентированных языков, для ссылки на объекты внутри со¬
общений используются псевдопеременные ’self’ и ’super’.
Псевдопеременная ’self’ ссылается на объект, которому послано
сообщение. Псевдопеременная ’super’ ссылается на суперкласс
данного класса. Для доступа к переменным экземпляра, которому
послано сообщение, используется нотация
self->width
Предполагается, что width — имя переменной экземпляра. Другое
синтаксическое правило CtaUc касается символа подчеркивания.
Когда селектор сообщения заканчивается подчеркиванием, это
означает, что за ним должен следовать аргумент. С практической
точки зрения одним из наиболее существенных свойств объект¬
но-ориентированной системы является количество встроенных ба¬
зовых классов. Их мы и рассмотрим далее.
■ БАЗОВЫЕ КЛАССЫ CTALK
В табл. 3.4 перечислены базовые классы Ctalk.
Container представляет собой абстрактный класс, используемый
для определения подклассов, поддерживающих динамические
данные. Это значит, что любой подкласс класса Container дает
возможность создавать экземпляры, хранящие данные перемен¬
ной или фиксированной длины, вместе с последовательными или
иными методами доступа к ним. Так, например, если объекту
посылается сообщение expand, то его размер удваивается. Для
более гибкого увеличения размера используется метод expandBy_,
получающий в качестве параметра число дополнительных элемен¬
тов, на которые расширяется объект. Подобным образом метод
Базовые классы Ctalk
79
Таблица 3.4. Базовые классы Ctalk
Objact
Assoc
Container
Buffer
Stream
ByteArray
Collection
OrdCollect
Stack
Set
Dictionary
bitArray
String
PutRecSize_ устанавливает размер в байтах, занимаемый каждым
содержащимся в объекте элементом данных. Для доступа к индек¬
сированному объекту воспользуйтесь методом at_get_nRecs, кото¬
рый возвращает указанное число записей, начиная с заданного
номера. Почти все базовые классы — это подклассы класса
Container, реализующие различные типы структур данных. Обыч¬
но в объектно-ориентированных системах динамические структуры
определяются в более глубоких членах иерархии классов. Предло¬
женный способ нов, и о нем следует рассказать поподробнее.
Класс Buffer предназначен для простого хранения больших блоков
данных в байтовом или словном форматах. Непосредственное
расширение этого класса включает переменную экземпляра, ука¬
зывающую, состоит ли буфер из байтовых или словных объектов.
В результате могут быть написаны общие процедуры чтения и
записи, которые вначале посылают буферу сообщение, чтобы уз¬
нать, какой формат он имеет: байтовый или словный. В этом
случае программа не должна специфицировать тип буфера.
Класс Stream (поток) реализован в виде подкласса класса Buffer
и включает переменную position, содержащую текущую позицию
потока. Класс Collection предоставляет объекты, в которых можно
группировать другие объекты. Подклассы Collection различаются
между собой порядком доступа к объектам (последовательный или
произвольный) и наличием ограничения размера. Например,
класс OrdCollect позволяет добавлять к извлекать объекты после¬
довательно, а размер самой коллекции автоматически увеличива¬
ется при добавлении новых элементов.
80 Объектно-ориентированные расширения Си: языки и инструментальные средства
Классы текстовых окон
Фирма CNS разработала несколько дополнительных классов, под¬
держивающих создание текстовых окон в среде Ctalk. Сгруппиро¬
ванные иерархически эти дополнительные классы приведены в
табл. 3.5.
Опишем кратко назначение некоторых из них. Класс Browser
содержит основные методы для создания броузеров, подобных
включенному в систему. Этот класс содержит более 12 методов.
Приведен пример тестирующей броузер программы. Этот код
реализует текстовый броузер, в котором отсутствуют многие эле¬
менты реального инструмента, но из которого ясно, как устроен
настоящий броузер. Те, кто хотел бы сделать собственный броузер,
наращиваемый по желанию, могут в качестве отправной точки
воспользоваться этим кодом.
Подпрограмма инициализации вначале посылает сообщение ме¬
неджеру экрана и передает тип создаваемого экрана. Затем объек¬
ту Mouse сообщают, что нужно создать курсор определенного типа.
Для обслуживания списка окон создается объект Ordered
Collection. Затем строится сам объект Windows. Задаются его
размер и атрибуты. И наконец, создается Event Manager (менеджер
событий). Перед нами законченный пример того, как в Ctalk
используется одна из черт объектно-ориентированной парадигмы:
использование одного объекта для управления другими. Недоста-
Таблица 3.5. Дополнительные классы текстовых окон
Browser
File
Menu
Mouse
Notifier
ScreenMgr
TxPoint
Window
ButtonWin
ItemWin
Scrollbar
StdWindow
ListWindow
HorListWin
PopUp
Response
TxtWindow
TxEditor
WinManager
Заключительные замечания о Ctalk
81
ток этого подхода состоит в тенденции к ’’увязанию в объектах”
при увеличении их числа.
■ СОЗДАНИЕ ИСПОЛНЯЕМЫХ ПРОГРАММ
В Ctalk создание исполняемой программы производится за 5
шагов. Первый состоит в написании требуемьгх: исходных текстов
на Си и Ctalk. Их нужно записать на диск в файлы с расширениями
.PRE. На втором шаге необходимо написать на Си головную
программу, содержащую обращения к требуемым модулям. Как
только это выполнено, можно приступать к третьему шагу —
запуску препроцессора, который преобразует все файлы CtaUc в
соответствующие файлы на Си. На четвертом шаге все файлы на
Си обрабатываются соответствующим компилятором. Последний
шаг состоит в связывании полученных файлов .OBJ в выполняе¬
мую программу.
Как было сказано ранее, желающие могут сократить некоторые
шаги, воспользовавшись опцией Make броузера. Для этого необхо¬
димо выбрать опцию Make Spec подменю. В результате выполне¬
ния интерактивной процедуры будет создан файл с расширением
.MAK, предназначенный для специальной вспомогательной про¬
граммы, которая выполнит в пакетном режиме препроцессирова¬
ние, компиляцию и связывание. Поскольку броузер поддерживает
выполнение команд и временный выход в DOS, нет нужды поки¬
дать его ни для изготовления законченной исполняемой програм¬
мы, ни для ее последующего тестирования.
■ ЗАКЛЮЧИТЕЛЬНЫЕ ЗАМЕЧАНИЯ О CTALK
Ctalk успешно выполняет свое предназначение, а именно реали¬
зует настоящий гибрид языков Си и Смолток. Среди аналогичных
попыток эту выделяет наличие броузера. Наиболее существенным
отличием между Смолтоком и всеми появляющимися гибридами
Си служит существенное различие в числе базовых классов. Смол¬
ток, в том виде в котором он продается, — это не только язык, но
огромная библиотека родовых и по определению стандартных
функций.
По этой причине описанные мною классы Window служат суще¬
ственным довеском к системе Ctalk. Важность таких библиотек
классов можно понять, если вспомнить, что в объектно-ориенти¬
рованной системе рабочими элементами служат объекты, а не
отдельные функции. В этом смысле в системе с большой библио¬
текой классов существенная часть программирования уже выпол¬
нена. Необходимость изучения этих классов составляет оборотную
сторону медали. Хотя единообразие интерфейсов и входит в число
82 Объектно-ориентированные расширения Си: языки и инструментальные средства
достоинств объектно-ориентированного подхода, обучение все-та-
ки происходит постепенно.
К числу небольших недостатков этого типа систем следует отнести
то, что они в основном рассчитаны на работу с исходным текстом
на Ctalk при помощи броузера. Если разработчик желает продать
библиотеку в виде объектных файлов, то существующая реализа¬
ция не позволит использовать их вместе с броузером. Вероятно,
было бы достаточно загружать в броузер наиболее важную инфор¬
мацию, не требуя при этом разглашения исходных текстов. Это
позволит использовать броузер в сочетании с библиотеками клас¬
сов в объектной форме. Следует особо подчеркнуть, что Ctalk
содержит все существенные черты объектно-ориентированного
языка, а броузер сделан настолько хорошо, что позволяет придать
интерактивный ’’привкус” Смолтока, сохраняя все преимущества
системы, основанной на Си.
■ VIEWS: ОБЪЕКТНО-ОРИЕНТИРОВАННЫЙ
ИНСТРУМЕНТ РАЗРАБОТКИ СИ-ПРОГРАММ
ДЛЯ WINDOWS
В этом разделе я расскажу об инструменте разработки VIEWS для
Си++ версии 2.0. Эта среда программирования, базирующаяся на
Windows, предназначена для любого из описанных выше объект¬
ноориентированных диалектов Си. Имеются версии VIEWS для
Си++ версии 2.0, Objective-C и Ctalk. Вне зависимости от того,
какой диалект вам кажется наиболее подходящим, среда VEEWS
дает вам возможность использовать его для разработки программ
для Windows.
Новые средства разработки для Windows обязаны своим появле¬
нием стремлению найти альтернативу сухому подходу, воплощен¬
ному в Software Development Kit (SDK). Список этих средств
стремительно сужается, если речь заходит об объектно-ориентиро¬
ванной системе, пригодной для создания реальных приложений.
Таким средством является VTEWS, все другие находятся далеко
позади. В качестве языка программирования выбран Ctalk, один
из наиболее удобочитаемых и применимых объектно-ориентиро¬
ванных диалектов Си.
На первый взгляд MS Windows кажется объектно-ориентирован¬
ной системой. Различные типы окон поделены на' классы, и все
действия производятся с помощью обмена сообщениями. Все ли
это, что требуется от объектно-ориентированного окружения? Ра¬
зумеется, нет. Программирующим для Windows доступна лишь
малая часть характерных для ООП преимуществ. Отсутствует
возможность исключения повторяющегося кода. Используя SDK,
Броузер Си++ системы VIEWS
83
программист получает нечто противоположное. Результирующий
код на Си можно считать самим определением избыточности. В
чем состоят другие достоинства ООП? Четкое разграничение час¬
тей программы, ведущее к ясному кодированию реализуемых
модулей. Напротив, программы для Windows стремятся вытянуть¬
ся в бесконечную цепочку операторов case; отсутствуют надежные
способы проверки правильности логики программ. По этим при¬
чинам инструменты типа Ctalk VIEWS должны привлечь внима¬
ние серьезных разработчиков программ для Windows.
■ БРОУЗЕР СИ++ СИСТЕМЫ VIEWS
Достаточно одной причины, чтобы сделать VEEWS достойным
предметом изучения. Этой причиной служит уникальный среди
компилируемых языков броузер, очень похожий на имеющийся в
Смолтоке, самом старом объектно-ориентированном языке j6m,ero
назначения. Броузеры для разных объектно-ориентированных ди¬
алектов Си, поддерживаемых системой VEEWS, различны. В этой
главе я заострю внимание на броузере для Си++.
К числу наиболее важных особенностей этого броузера относится
большое количество средств управления визуализацией и редак¬
тированием. Он полностью использует интерактивные возможно¬
сти Windows для задания всех мыслимых режимов, которые могут
быть использованы при разработке программ на Си++. Когда
броузер начинает работу, вы можете обозреть все классы, которые
могут иметь отношение к вашему приложению. Меню Classes
содержит возможности добавления подкласса к выбранному клас¬
су, удаления подкласса, сохранения и загрузки классов и даже
временной выгрузки классов на диск для освобождения памяти.
Весьма важно и меню Make.
Броузер VffiWS спроектирован для работы с файлами окружения
(,ENV). В этих файлах записана иерархия классов, составляющих
приложение. Опция AppHcation основного меню служит для за¬
грузки и сохранения файлов окружения. Когда при первом запу¬
ске броузера эти файлы отсутствуют, в основном меню доступны
только опции File и AppHcation. Для полного обзора системы
классов следует использовать файл ALL.ENV. Как правило, в
файлы окружения включают только те классы, которые использу¬
ются данным приложением.
Когда VTEWS отображает файлы классов, определенные разделы
замещаются выделенными символами $ тегами. Теги соответству¬
ют разделам, которые удаляются и подклеиваются.
Меню View содержит набор опций, ограничивающих набор отобра¬
жаемых на панели редактора аспектов функций членов и классов,
а также список функций-членов, видимый на предназначенной
84 Объектно-ориентированные расширения Си: языки и инструментальные средства
для них панели. При первой загрузке файла .ENV большинство
панелей броузера ничего не отображают. Чтобы увидеть что-либо,
нужно сначала выбрать класс в иерархии, отображенной в левой
верхней панели. В свою очередь, чтобы броузер показал что-либо
для выбранного класса, нужно воспользоваться одной из опций
Header (Заголовок) или Source ^сходный текст) в меню View. В
первом случае будет загружен файл заголовков данного класса, а
в другом — станет доступным для редактирования его исходный
текст. Оставшиеся функции меню View (Public, Protected, Private,
Variables, Methods, Friends и АИ) определяют, какого вида члены
будут отображаться на панели членов. Например, выбор одной из
опций Variables или Methods ограничит список отображаемых
членов соответственно переменными или функциями-членами.
До того как будет выбран один из пунктов меню Members, доступ¬
ной является лишь опция Add. После этого станут доступными
другие опции. В отличие от опций View эти опции изменяют
природу выбранного члена. Например, опция Virtual делает вы¬
бранный член виртуальным.
Опции меню Make управляют подготовкой различных типов фай¬
лов, нужных для построения приложения. Это меню поделено на
три части. В верхней части задается предмет редактирования,
выполняемого на нижней панели: головной файл, файл Make,
файл Link или файл определений. Средняя часть предназначена
для управления зависимостями и файлами текстовых подстано¬
вок. Их можно просматривать, добавлять и удалять. Третья часть
меню Make управляет объектными файлами библиотек классов.
Опция Show в этом меню открывает прокручивающийся список
библиотечных объектов, указанных в текущем файле окружения.
■ МОДЕЛЬ MVC ПОЛЬЗОВАТЕЛЬСКОГО ИНТЕРФЕЙСА
VTEWS использует основанную на популярной парадигме MVC
(Model-View-Control — Модель-Взгляд-Управление) модель орга¬
низации интерфейса Windows в виде классов объектов. Эта пара¬
дигма состоит из трех компонентов:
модели — уровень приложения,
взгляда — уровень визуализации,
управления — уровень взаимодействия.
MVC основан на стратегии разделения операций программы на
три функционирующих независимо модуля: уровень приложения
(модель), содержащий основные данные и операции, уровень ви¬
зуализации, делающий доступными несколько точек зрения на
уровень приложения, и уровень взаимодействия, который обраба¬
тывает пользовательский ввод и распределяет его между двумя
другими уровнями. Это один из классических подходов к созданию
Класс AppView
85
объектно-ориентированного интерфейса с пользователем. Пример
использования этой модели — класс Browser. Модель объектов
класса Browser содержит иерархию классов и методов, сохранен¬
ную в экземпляре класса Ordered Collection.
■ КЛАСС APPVIEW
Фокальным центром VTEWS MVC служит Notifier. Это класс, не
имеющий экземпляров и обрабатывающий сообщения исключи¬
тельно при помощи методов класса. Notifier занимается оповеще¬
нием. Он обязан сообщать различным частям программы о
произошедших за время работы изменениях. Notifier может обра¬
батывать шесть типов событий:
1. Передвижение и нажатие кнопок мыши.
2. Изменение точки (фокуса) ввода.
3. Ввод с клавиатуры.
4. Открытие окон.
5. Изменение размеров и перемещение окон.
6. Различные изменения окружения.
Для каждого из перечисленных типов событий в классе Window
имеются соответствующие методы. Notifier посылает сообщение
нужного типа окну, в котором произошло событие. Например, если
в результате перемещения одного из окон открывается часть
другого, которое было им прикрыто, Notifier посылает сообщение
этому окну, чтобы оно перерисовало себя в соответствии с возник¬
шей на экране конфигурацией.
Понятие фокуса ввода отражает ситуацию, когда некоторое окно
или подокно временно начинает собирать (фокусировать) все со¬
бытия, приходящие от клавиатуры. Обычно, после того как окно
выбрано, оно подсвечивается и становится фокусом ввода. Окно
можно сделать фокусом ввода, послав ему сообщение takeFocus.
Кроме обработки событий класс Notifier предоставляет определен¬
ный сервис окнам. Если по какой-либо причине следует сменить
форму курсора, это делает Notifier.
Достаточно часто в процессе работы пользователь вносит некоторое
изменение, которое должно бьггь йемедленно отображено в опре¬
деленном окне. В модели MVC эта ситуация обрабатывается при
помощи последовательности сообщений, курсирующих взад и впе¬
ред. Самый верхний элемент изображения обнаруживает измене¬
ние данных и посылает сообщение обновления элементу
изображения, ответственному за эти данные. Все эти сообщения
сводятся к тому, что изображенная картинка не соответствует
действительности. Это приводит к сообщению перерисовки, кото¬
рое, в свою очередь, отправляет сообщение paint требуемому окну.
86 Объектно-ориентированные расширения Си: языки и инструментальные средства
Внутри операции перерисовки элемент изображения посылает
сообщение модели, это приводит к тому, что отображается самая
последняя версия данных.
Элементы изображения имеют семь различных типов классов
объектов, используемых для построения пользовательских интер¬
фейсов программ.
1. Классы контроллеров, таких, как Notifier.
2. Окна.
3. Управляющие окна и подокна.
4. Элементы изображений.
5. Окна приложений.
6. Всшп шающие окна.
7. Диалоговые окна.
Когда в окне происходит событие, оно должно ответить TRUE или
FALSE. Ответ TRUE означает, что окно обработает событие, ответ
FALSE приводит к тому, что нижележащая оконная система
выполнит предопределенное действие. При запуске приложения
вначале создаются модель и элементы изображения, а затем классу
Notifier посылается сообщение start, и он начинает обрабатывать
и диспетчеризовать события. По соображениям переносимости
класс Notifier поддерживает только наиболее общий вид мыши, а
именно мышь с одной кнопкой.
■ НАЗНАЧЕНИЕ ОКОН В СИСТЕМЕ VIEWS
Слова "родительское” и ”дочернее” по отношению к окнам могут
показаться странными, но это просто метафора для обозначения
зависимости одних частей экранного интерфейса пользователя от
других. В MS Windows у каждого окна есть родитель. В системе
VlEWS это отражается следующим образом: каждый элемент
подклассов Window имеет некий Ш, содержащий идентификатор
родительского окна. В родительском окне находится переменная
экземпляра Ш объекта класса OrderedCollection, содержащего
дескрипторы всех дочерних окон.
Все окна VTEWS поделены на две части: область клиента и
системную область.
Дочерние окна отличаются от настоящих детей, они всегда пере¬
мещаются вместе с родительским окном. Это относится как к
движению в плоскости экрана, так и к перемещениям вверх-вниз
между разными слоями оконной системы.
Если вы накроете окно, вместе с ним вы накроете и его дочернее
окно. Если вы выберете дочернее окно, то и непосредственный
родитель этого окна окажется выбранным. Когда окно содержит
Класс View
87
одно или более дочерних окон, ему обычно выделяется максимум
места. При этом когда пользователь изменяет размер окна, то
следует иметь в виду, что окно нельзя сделать слишком маленьким,
иначе дочерние окна исчезнут или станут бесполезными.
На самом деле окна прявляются на экране благодаря методу show,
который, будучи послан родительскому окну? автоматически рас¬
пространяется вниз на его дочерние окна. Классы VTEWS имеют
простые методы для вывода текста. Более сложный вывод текста
и графики реализован в классе Port. Абстрактный класс
ControlWindow предназначен для элементов управления. Объекты
потомков ControlWindow предназначены для совместного исполь¬
зования с другими объектами, обычно принадлежащими одному
из классов: View, AppView, PopupView или Dialog. Например,
класс Button (электронная кнопка), один из наследников класса
ControlWindow, содержит переменную экземпляра feAuto, пред¬
ставляющую собой булевскую переменную. Если этой переменной
присвоено значение TRUE,- то при нажатии кнопки мыши элект¬
ронная кнопка немедленно изменяет состояние.
Класс Button содержит несколько готовых к употреблению под¬
классов: CheckBox, TriState, PushButton и RadioButton. CheckBox
предлагает небольшой квадратик, с помощью которого устанавли¬
вается значение типа ”да-нет”. Когда выбрано значение ”да”, в
квадратике появляется знак X. Tristate — это, по сути, аналогич¬
ный подкласс, но имеющий третье состояние — отключено
(disabled). В этом состоянии кнопка изображается менее интенсив¬
ным цветом. PushButton — это кнопка закругленной формы, на
которой написано ее имя. RadioButton представляет собой малень¬
кий кружок, имя которого располагается поблизости. Такие кноп¬
ки часто используются в группах.
.■ КЛАСС VIEW
Класс View и его наследники предоставляют средства для програм¬
мирования уровня визуализации модели MVC. View — это абст¬
рактный класс, наследник Window, призванный облегчить
построение специализированных элементов изображения. View
содержит переменную экземпляра model, содержащую идентифи¬
катор модельного объекта приложения. Как правило, объекты
класса View посылают сообщения модельным объектам, чтобы
извлечь из них данные, которые должны быть отображены. Как
подкласс класса Window View и его наследники представляют
собой настоящие окна, снабженные линиями прокрутки
(scrollbar).
AppView — непосредственный подкласс класса View, представляет
собой более проработанный базовый класс, предназначенный для
88 Объектно-ориентированные расширения Си: языки и инструментальные средства
обзора программы. Как правило, уровень визуализации приложе¬
ния включает объект одного из подклассов этого класса. Другой
подкласс View PopupWindow в качестве потомка имеет Dialog. Он
представляет собой класс объектов, появляющихся на экране,
используемый для запроса пользователя. Их не следует путать с
выпадающими меню, которые активизируются пользователем и
содержат список возможностей. Имеются несколько подклассов
диалогов, представляющих собой готовые к использованию прото¬
типы для разных диалогов. В их числе классы FileSelect, biput,
Report, YesNo и YesNoCancel.
В среде Windows диалоги отличаются от всех других видов окон
тем, что они приостанавливают доступ ко всему остальному окру¬
жению до тех пор, пока им не уделят внимание. Это делает их
реализацию более тонким делом. Она часто вызывает трудности у
начинающих программировать для Windows.
■ СОЗДАНИЕ ДИАЛОГОВ С ПОМОЩЬЮ
ГЕНЕРАТОРОВ ИНТЕРФЕЙСОВ
VIEWS содержит упрощенный подход к объектно-ориентирован¬
ному кодированию диалогов. Этот подход подразумевает, что в
вашем распоряжении имеется программа DLA.LOG.EXE, входящая
в SDK. VIEWS содержит программу Interface Generator
(CTIG.EXE), транслирующую результаты редактора диалогов в
исходные тексты Ctalk VIEWS. Полученный код требует некоторой
доработки перед употреблением. Сгенерированного достаточно
только для создания диалога. Вам по-прежнему понадобится на¬
писать методы взаимодействия с элементами интерфейса, а также
методы, проверяющие предусловия и постусловия работы с диало¬
гом. К сожалению, CTIG.EXE — не программа Windows, и вам
понадобится вызвать оболочку DOS и выполнить эту программу в
традиционной манере — из командной строки.
ControlView — еще один интересный класс. Он позволяет наслед¬
никам ControlWindow (всевозможным кнопкам, спискам, тексто¬
вым редакторам и т.п.) иметь титулы, изменять размер и
перемещаться. Благодаря этому, текстовый редактор, занимаю¬
щий часть родительского окна, может по требованию пользователя
менять размер.
■ СОЗДАНИЕ МЕНЮ
Система меню VLEWS использует для изготовления пользователь¬
ского выпадающего меню три класса объектов: Menu, PopupMenu
и MemiTtem. Класс Menu обеспечивает коллекцию элементов ме¬
ню, определяющую структуру строки меню верхнего уровня. В
Классы редакторов текстов
89
VIEWS объект Menu может в качестве своих элементов иметь
только объекты класса PopupMenu. Поскольку экземпляры Menu
содержат идентификатор родительского окна, между окном и
меню существует двунаправленная связь. Для добавления и иск¬
лючения пунктов меню служат методы append_ и remove_. Хотя
можно удалить произвольный элемент меню, добавить элемент
можно только в конец» т.е. справа.
В КЛАССЫ РЕДАКТОРОВ ТЕКСТОВ
Одна из наиболее часто используемых ветвей подклассов
ControlWindow имеет следующий вид:
ControlView
EditBox
EditLine
TextEditor
EditBox — это класс управления редактировайием самого верхнего
уровня. Его экземпляр предоставляет базовую область окна для
редактирования текста, снабжая ее вертикальной и горизонталь¬
ной линиями прокрутки. Класс EditLine предлагает объекты,
используемые при редактировании текстов, состоящих всего из
одной строки, например, для ввода имен файлов и директориев.
Средства, обычно требуемые от редактора, реализованы в классе
TextEditor. Объекты класса TextEditor хранят редактируемый
текст в объектах классов String, Stream и FileStream. Экземпляр
класса TextEditor содержит буфера редактирования, которые он
может использовать для замены объектов String и Stream, когда
редактирование завершается. Объекты TextEditor могут также
предоставлять готовые выпадающие меню для редактирования
текста.
Ниже приведена подпрограмма на Ctalk, обрабатывающая файло¬
вый ввод-вывод объектов TextEditor:
id myEditor;
if (0myEditor is Charrged0)
{
if 0YesNo ask_
’’Текст был модифицирован. Сохранить изменения?”
of_ self0)
}
Edit Box и все его наследники содержат методы, управляющие
выравниванием текста в своих окнах. Как и всегда в объектно¬
ориентированных системах, программист использует базовые
классы в качестве отправной точки для разработки более специа¬
лизированных версий, применимых к задачам конкретной про¬
граммы.
90 Объектно-ориентированные расширения Си: языки и инструментальные средства
■ ТАЙМЕР
Класс Timer позволяет объектам всех типов узнавать время воз¬
никновения события. Имеется, разумеется, множество примене¬
ний такого механизма. Наиболее часто он используется для
определения скорости работы программы и в качестве часов в
программах дискретного моделирования. Создаваемые объекты
этого класса могут либо всю свою жизнь через регулярные проме¬
жутки времени посылать сообщения другим объектам, либо ожи¬
дать изменяющих их поведение сообщений от клиентов. Им может
быть приказано на время приостановить извещение других объек¬
тов, а затем восстановить режим извещения.
■ КОM МУН И КАЦИОН H Ы Й КЛАСС
VIEWS управляет коммуникацией с помощью класса ComPort,
содержащего средства как для ввода, так и для вывода в последо¬
вательный порт компьютера. Поддерживается метод опроса и
буферизированный ввод-вывод по прерываниям, включая все не¬
обходимое управление потоком данных, аппаратной синхрониза¬
цией и конфигурацией порта. ComPort использует два буфера:
один для приема, а другой для передачи сообщений. Выбор разме¬
ра буферов предоставлен программисту. Благодаря этому удается
достичь оптимальной для данной пропускной способности произ¬
водительности. Класс ComPort разрабатывался в предположении,
что существует другой объект, который знает, как воспользоваться
предоставленным сервисом для нужд конкретного приложения.
■ ГРАФИКА
Графические возможности VTEWS основаны на объектах класса
Port. Имеются методы рисования простых фигур, линий, разнооб¬
разные шрифты и некоторые общие графические преобразования.
Класс Port предусматривает переносимость. Написанные графиче¬
ские программы не зависят от аппаратуры, не считая некоторых
тонкостей, связанных с соотношениями масштабов по осям X и Y.
Метод openOn_ класса Port содержит параметры, определяющие
битовую карту и объекты Window и Printer, на которые направлен
графический вывод. Каждому сообщению openOn_ должно соот¬
ветствовать сообщение close. Эти два сообщения должны сопутст¬
вовать друг другу как парные скобки. Для лучшего обеспечения
переносимости графики класс Port позволяет для управления
объектами-перьями использовать виртуальные координаты. Име¬
ются шесть типов линий: сплошная, штриховая, две штрихпунк-
тирные, пунктирная и невидимая.
Заключительные замечания о VIEWS
91
Могут использоваться закрашенные фигуры. На выбор предлага¬
ются 10 шаблонов закраски. Объекты Port функционируют в двух
режимах: локальном и виртуальном. В виртуальном режиме все
операции рисования используют виртуальную систему координат,
что делает код машинно-независимым. Его не требуется настраи¬
вать под конкретную аппаратуру. Локальный режим использует
систему координат той аппаратуры, на которой в данный момент
работает VDSWS. Для переключения между режимами служат
методы setVirtOn и setVirtOff.
■ УСКОРИТЕЛИ ПРИЛОЖЕНИЙ
Утилита VTEWS Streamliner находится в файле CTSW.EXE. Она
запускается из командной строки DOS. Это оптимизатор кода,
предназначенный для изготовления меньшего по размеру и более
быстрого кода. Он вычищает ”мертвый”, неиспользуемый код и
выполняет статическое связывание везде, где можно. Методы и
классы, к которым нет обращений во время работы программы,
удаляются из исходного текста, а связывание периода выполнения
оставляется только для тех процедур, для которых оно абсолютно
необходимо. В результате размер кода сокращается до 50%, а
скорость выполнения заметно повышается.
■ ЗАКЛЮЧИТЕЛЬНЫЕ ЗАМЕЧАНИЯ О VIEWS
Как и в большинстве реализаций объектноориентированных язы¬
ков на микрокомпьютерах, в Ctalk VIEWS отсутствует множест¬
венное наследование. В программном окружении VIEWS нет
специальных отладочных средств для работы в терминах объектов.
Приходится вести отладку на уровне Си, используя программы
типа CodeView. Идея работать с использованием объектов в окру¬
жении Си несколько дискредитирована отсутствием отладчика.
Одним из ограничений, связанных со структурой Windows, явля¬
ется невозможность прямого доступа к объектам из языка Си.
Из-за сцецифики работы с указателями в Windows на объекты
типа Ю нельзя непосредственно ссылаться из обычных программ
на Си. Однако, несмотря на этот недостаток, VEEWS представляет
собой одно из наиболее завершенных окружений программирова¬
ния для Windows на объектно-ориентированных диалектах Си.
Глава 4
■ РАЗРАБОТКА ПРОГРАММ
ДЛЯ WINDOWS 3.0
В этой главе среда Windows 3.0 рассматривается с точки зрения
разработчика, затрагивается ряд проблем, ссылки на которые
будут появляться в остальных главах книги. Сюда вошли новые
средства Windows 3.0, основы работы с графическим интерфейсом
пользователя, пакет Software Development Kit (SDK) фирмы
Microsoft, компилятор ресурсов и отладчики. В конце главы
приведен краткий список функций, сообщений и структур, поя¬
вившихся в третьей версии Windows.
Новая версия Windows стала заметным событием для разработчи¬
ков программного обеспечения персональных компьютеров. Появ¬
ление многочисленных новых приложений, работающих на базе
одного интерфейса, служит сильным стимулом для овладения
непростым искусством программирования для Windows. В данной
главе я опишу пакет SDK и, образно говоря, рассмотрю через
увеличительное стекло внутреннюю кухню Windows. Я последова¬
тельно расскажу о различных серьезных вспомогательных про¬
граммах и других средствах, предоставляемых этим пакетом
разработчику. И наконец, я покажу процесс конструирования
Windows-программ с помощью SDK и мы обсудим некоторые
типичные проблемы, с которыми сталкивается программист в
среде Windows.
■ ЧТО НОВОГО ПОЯВИЛОСЬ В SDK WINDOWS 3.0?
Для тех, кто работал с ранними версиями SDK, мы предоставим
обзор изменений и дополнений, вошедших в версию 3.0, остано¬
вимся также и на нововведениях собственно в среде Windows.
В Windows 3.0 появились несколько новых видов объектов. К ним
относятся:
• плавающие меню;
Как работает система Windows
93
• иерархические меню;
• определяемые меню Checkmarks.
Перечислим три новые программы, включенные в SDK:
• Новый графический редактор ресурсов, названный
SDKPaint.
• Профайлер, который измеряет время выполнения всех про¬
грамм в Windows.
• SWAP — анализатор своппинга.
Отметим улучшения, сделанные в реализованных ранее програм¬
мах.
• Новый вариант редактор диалога позволяет включать в
диалоговые окна традиционные средства управления (зада¬
ния параметров), такие, как выбор цвета в палитре или меню
регулировок. Улучшены поддержка файлов-заголовков, а
также поддержка новых комбинированных окошек (combo
boxes).
• Редактор шрифтов теперь позволяет преобразовывать шриф¬
ты с переменной шириной в шрифты с постоянной шириной,
многие команды упрощены в результате улучшения обрат¬
ной связи.
• В новой версии программа HeapWalk позволяет просматри¬
вать память EMS.
• CodeView for Windows теперь поддерживает отладку только
в защищенном режиме. Повышена скорость работы; предо¬
ставляется возможность слежения за сообщениями и унич¬
тожения сообщений, при этом, однако, предпочтительнее
иметь не только обычный монитор, но и дополнительный
монохромный монитор с 25-строчным экраном.
• Включены новые средства для работы с классами окон, в
частности функция UnRegisterClass, которая освобождает
память при уничтожении зарегистрированного класса окон,
и функция GetClassbifo, которая выдает информацию о
заданном классе окон.
■ КАК РАБОТАЕТ СИСТЕМА WINDOWS
Постараемся поближе познакомиться с механизмом работы среды
Windows. Все было просто, пока мы рассуждали об общих прин¬
ципах. Теперь речь пойдет о деталях. Пересылка сообщений — это
то, из чего состоит и на чем основана работа Windows. Но что же
94
Разработка программ для Windows 3. 0
происходит конкретно? Какие сообщения и куда пересылаются?
В Windows имеются общая системная очередь и отдельные вход¬
ные очереди к каждому из приложений. Рабочий цикл каждого
из приложений получает входную информацию из собственной
очереди. Всегда, когда пользователь выполняет какое-нибудь дей¬
ствие: нажимает клавишу, ’’щелкает” или передвигает мышь,
программе посылается некоторое сообщение, которое и поступает
во входную очередь. Исключением являются ’’внеочередные” со¬
общения. Без постановки во входную очередь можно послать те
сообщения, которые воздействуют непосредственно на окно. Такие
сообщения обрабатываются без ожидания.
Хотя обычно сообщения создаются пользователем или средой
Windows, можно описать и приложения, инициирующие сообще¬
ния, которые будут поступать в очереди к другим приложениям.
Можно даже написать приложение, которое в процессе диалога с
пользователем будет конструировать некоторое сообщение и посы¬
лать его.
Ядро любой программы в Windows — это цикл работы с сообще¬
ниями (message loop), который получает входные сообщения из
входной очереди и посылает их в нужное окно. Работа любого
приложения состоит из цикла приема сообщений (GetMessage).
Суть его в следующем: если функция GetMessage не находит
какого-нибудь сообщения в своей очереди, она передает управле¬
ние среде Wiridows. Как только вы выбрали некоторое окно
приложенйя, сообщение посылается и функция получает то, что
искала. Приложение получает входную информацию, а
GetMessage продолжает слежение.
■ ФУНКЦИИ, КОТОРЫЕ СОЗДАЮТ ОБЪЕКТЫ
Вероятно, важнее всего программисту знать о сообщениях, которые
позволяют создавать объекты. Не удивительно, что в Windows
важнейшим из создаваемых объектов является само окно! В
табл. 4.1 приведены список основных действий по созданию, унич¬
тожению и модификации окон и список операций, которые выпол¬
няют эти действия.
■ КЛАССЫ ОКОН
Прежде чем идти дальше, имеет смысл уточнить терминологию.
Будем использовать термин ’’класс окон MS” (MS — MicroSoft) для
обозначения классов окон в системе MS-Windows и отличать его
от термина ’’класс” как такового, который обозначает некоторую
структуру в системе объектно-ориентированного программирова¬
ния (ООП). Так, например, если в некоторой системе ООП имеется
клэсс под названием Window, то, когда я буду говорить ’’класс
Классы окон
95
Таблица 4.1. Функции Windows для создания и модификации окон
Функция
CreateWindow
CreateW indowEx
DestroyWindow
DefWindowsProc
AdjustW indowRect
AdjustW indowsRectEx
GetWindowLong
GetWindowWord
SetWindowLong
SetW indowW ord
DefDlgProc
DefFrameProc
DefMDIChildProc
GetClassInfo
GetClassLong
GetClassName
GetClassWord
RegisterClass
SetClassLong
SetClassWord
UnregisterClass
Назначение
Для создания частично перекрывающихся,
выпадающих и дочерних меню
Для создания окон в расширенном стиле
Удаляет окно из системы
Для обработки сообщений окон, которые
необработаны приложениями
Для задания размеров окна в соответствии
с размерами области, определенной
пользователем
Задает размеры окна по указанию
пользователя в расширенном стиле
Для получения информации об окне
Для получения информации об окне
(альтернативный вариант)
Модифицирует атрибуты окна
Модифицирует атрибуты окна
Для обработки сообщений диалога, ые
обработанных приложениями
Для обработки фреймов MDI
(Multi-Document Interface) сообщений,
не обработанных приложениями
Для обработки дочерних сообщений
диалога, не обработанных приложениями
Для получения информации о классе
Для получения информации о структуре
WNDCLASS
Получает имя класса окна
Для получения информации о структуре
WNDCLASS
Для регистрации класса окон
Модифицирует описатель класса окон
Модифицирует описатель класса окон
Удаляет класс окон из системы
Window”, я никогда не буду иметь в виду низкоуровневый ”мик~
рософтовский” класс окон. Это позволит избавиться от двусмыс¬
ленности, возникающей из-за двух значений, которые может
иметь в контексте книги слово ”класс”.
В настоящее время имеются три типа встроенных Windows-клао-
сов: системные глобальные, глобальные классы приложений и
96
Разработка программ для Windows 3. 0
Таблица 4.2. Структура класса Window в Windows
Структура класса Назначение
Имя класса Однозначно идентифицирует класс окон
Адрес процедуры окна Указатель на функцию, обрабатывающую
все сообщения, получаемые данным
классом
Имя приложения, зарегистрировавшего
данный экземпляр класса
Определяет форму курсора в окнах класса
Пиктограмма, используемая, когда окно
класса закрыто
Определяет цвет и растр фона окна
Меню по умолчанию для класса окон
Определяют распределение памяти,
способ изменения и другие
характеристики для класса
Определяет дополнительную память,
которую необходимо разместить
в структуре класса
Определяет дополнительную память,
которую необходимо разместить
в существующих окнах
локальные классы приложений. Глобальные системные классы
доступны всегда и всем приложениям. Они создаются при старте
Windows. В табл. 4.2 даны сведения об основных составляющих
структуры класса.
■ СООБЩЕНИЯ
Входное сообщение в Windows содержит информацию о: систем¬
ном времени; позиции мыши; нажатии кнопки мыши; состоянии
клавиатуры; skan-кодах нажатых клавишей; об устройстве, кото¬
рое возбудило сообщение.
Различают пять видов сообщений:
1) оконные сообщения;
2) сообщения ’’кнопок управления”;
3) сообщения для комбинированных окон;
4) управляющие сообщения редактора;
5) сообщения списка окон.
Эти виды сообщений предоставляют эффективный внутренний
механизм, который дает возможность реализовать много тонких
деталей пользовательского интерфейса автоматически, без много¬
Handle (дескриптор)
Курсор класса
Пиктограмма (icon)
Кисть фона класса
Меню класса
Стили класса
Дополнение класса
Дополнение окна
Функции окон
97
словного выписывания таких деталей в каждом из приложений.
Обычно эти сообщения переносят как сведения о типе события,
так и количественную информацию, которая характеризует неко¬
торое точное значение. Так же как функции Windows предостав¬
ляют пользователю интерфейс для того, чтобы определить
дальнейшие действия, сообщения Windows аналогичным образом
указывают, какие события должны наступить.
Система Windows предусматривает также механизм предотвраще¬
ния дедлока (взаимной блокировки) сообщений. Чтобы понять,
как работает этот механизм, вначале необходимо понять, из-за чего
возникают дедлоки. Дедлок может возникнуть, если одна из задач
программы воспользовалась функцией SendMessage для передачи
сообщения другой задаче и вынуждена ждать возврата из этой
функции, поскольку управление передано другой задаче. Но это
еще не дедлок. Последний возникнет, если вторая задача должна
передать управление первой. В этом случае каждая из задач
требует, чтобы инициатива исходила от другой, и ни одна из задач
не может продолжить выполнение. На практике дедлок встреча¬
ется и в других случаях, особенно тогда, когда задача, которой
передано управление, не содержит специальнькс средств для пред¬
отвращения подобных ситуаций.
■ ФУНКЦИИ окон
Интерфейс разработки программ program Development biterface
PDQ Windows содержит большое количество функций. С одним
управлением окнами связано не менее 18 различных функцио¬
нальных групп. Оконные функции обычно используются при
определении стилей окна. Последние представляют собой заранее
заготовленные компоненты, которые могут быть собраны в различ¬
ных комбинациях, для получения различных типов окон. Окон¬
ные стили служат для формирования параметров функции
CreateWindow, вызываемой для создания окна. В стандартном
окружении Windows используются 4 основные стиля: 1) перекры¬
вающиеся окна, 2) окна с владельцем, 3) всплывающие окна,
4) дочерние окна (табл. 4.3). В табл. 4.4 перечислены функции
управления клавишами.
Таблица 4.3. Названия стилей окон
Стилъ окна Сообщение
Перекрывающиеся окна WS_OVERLAPPED
Окна с владельцем WS_OVERLAPPED с hWindParent
в качестве владельца
Выпадающие окна WS_POPUP
Дочерние окна WS_CHELD
4-857
98
Разработка программ для Windows 3.0
Таблица 4.4. Группы функций для управления окнами
!Функции сообщений
2.Функции создания окна
3.Функции изображения и движения
4Функции ввода
б.Аппаратные функции
6.Функции рисования
7.Функции организации диалога
Б.Функции прокрутки
9.Функции меню
Ю.Информационные функции
Н.Системные функции
12.Функции стеллажа (clipboard)
13.Функции ошибок
14.Функции вставки
15.Функции курсора
16.Функции-крючки
17.Функции собственности
18.Функции прямоугольника
■ КОНТЕКСТ ДИСПЛЕЯ
Система контекста дисплея — это средство управления дисплеем
компьютера, одновременно разделяемым несколькими приложе¬
ниями Windows. Windows работает с четырьмя типами контек¬
стов: общим, классом, приватным и окном. Последний контекст
поддерживает запись данных в любую точку окна. Остальные
позволяют писать только в область клиента окна. Тип дисплейного
контекста получает значение исходя из спецификации стиля клас¬
са окна при его создании.
■ ЭЛЕМЕНТЫ ОКОННЫХ ПОЛЬЗОВАТЕЛЬСКИХ
ИНТЕРФЕЙСОВ
В готовых интерфейсах пользователя Windows обычно присутст¬
вуют многочисленные средства управления, каждое из которых
обладает уникальным идентификационным номером. Когда поль¬
зователь воздействует на эти средства, сообщения
WM_COMMAND, циркулирующие между окнами и средствами
управления, переносят эти номера. Каждый пользовательский
интерфейс в системе Windows состоит из подобного цикла управ¬
ления, который по желанию пользователя может быть расширен
кодом, определяющим реакцию на каждый поступивший иденти¬
фикационный номер. Например, параметры WS_TABSTOP и
Пиктограммы
99
WS_GROUP устанавливают стиль управления для групп электрон¬
ных кнопок и диалоговых полей таким образом, чтобы фокус
управления перемещался с помощью соответственно клавиш
табуляции или стрелок.
■ УПРАВЛЯЮЩИЕ ЭЛЕМЕНТЫ,
ПЕРЕРИСОВЫВАЕМЫЕ ВЛАДЕЛЬЦЕМ
Обычно система Windows сама отвечает за перерисовку различных
типов управляющих элементов. Однако начиная с версии 3.0
появилась возможность приписать элементу стиль ”риеуемый вла¬
дельцем”. Это означает, что у управляющего элемента может быть
тип, ответственность за перерисовку которого берет на себя владе¬
лец. Основное преимущество этого состоит в том, что адаптирован¬
ные данные могут быть запомнены в структурах, допускающих
определенные вариации в стиле элемента. Вместо стандартной
автоматической перерисовки элемента эта работа поручается ок-
ну-владельцу, имеющему доступ к информации о способе рисова¬
ния управляющего элемента.
■ ПИКТОГРАММЫ
В Windows 3.0 пиктограммы f[cons) играют более заметную роль,
чем в предыдущих версиях системы. Пиктограммы создаются с
помощью специальных файлов битовых карт и операторов в запи¬
сях ресурсов, которые позволяют добраться до этих файлов. Фай¬
лы пиктограмм Windows обычно содержат целый набор различных
пиктограмм, настроенных на различные наборы цветов и степени
разрешения. Выбор наиболее подходящей к имеющемуся дисплею
оставлен системе Windows. Встроенные пиктограммы, поставляе¬
мые с системой Windows, могут быть использованы любой при¬
кладной программой. В число встроенных входят
восклицательный знак, вопросительный знак, звездочка и знак
”стоп”. Обычно они используются при построении диалога. Однако
пиктограммы имеют более широкое применение.
Часто удобно создать пиктограмму класса. Она будет использова¬
на, когда объект этого класса сжимается, ”коллапсирует” на
экране. Если для класса окон определена пиктограмма, Windows
автоматически отображает ее всякий раз, когда соответствующие
окна сжимаются. Программисту, создающему приложение, не
нужно заботиться об этом. Для создания новых пиктограмм можно
воспользоваться утилитой SDKPaint из пакета SDK, а соответст¬
вующие декларации ввести с помощью компилятора ресурсов.
100
Разработка программ для Windows 3. О
Я МЕНЮ
Наиболее популярны текстовые меню. Однако графические интер¬
фейсы пользователя часто содержат и графические меню, исполь¬
зующие пиктограммы, например, в программах рисования.
Подобно другим средствам управления в Windows каждое меню
имеет уникальный идентификационный номер, определяемый
программистом. Когда пользователь выбирает пункт меню, его
идентификационный номер передается вместе с сообщением
Windows, уведомляющим приложение о событии. Набор инструк¬
ций, выполняемых для каждого из пунктов меню, определяется
приложением. Хорошая практика программирования отключает
(и делает ”серыми”) опции меню, использование которых в данный
момент неуместно. Это должно быть сделано тогда, когда опцию
меню невозможно или вредно активизировать. Отключение не
следует использовать в ситуациях, когда имеется хотя бы одна
возможность использовать опцию. Если есть сомнения, пункт
меню не следует отключать. В табл. 4.5 перечислены функции
меню.
Таблица 4.5. Функции меню Windows
SetMenu(hW nd,hMenu)
AppendMeriu
bisert*Menu
GetSubMenu
ModifyMenu(hMenu^iPosition,wFlags,wroNewItem,lpNewItem)
SetMenuItemBitmaps
■ УПРАВЛЯЮЩИЕЭЛЕМЕНТЫ
В Windows управляющие элементы представляют собой дочерние
окна, посылающие в ответ на действия пользователя извещающие
сообщения, которые, в свою очередь, могут быть проверены про¬
граммой и связаны с нужными процедурами. Следует тут же
заметить, что существует исключение: статические управляющие
элементы. Последние просто показывают определенный текст, не
воспринимая пользовательского ввода и не генерируя сообщений.
На уровне Windows управляющие элементы создаются функцией
CreateWindow, которой передаются соответствующие параметры.
В среде Windows для оповещения окон о состоянии управляющих
элементов служит сообщение WM_COMMAND. У этого сообщения
два параметра: wp и lp. Параметр wp сообщает Ш управляющего
элемента, а lp содержит информацию о типе управляющего эле¬
мента: меню, ключ ускорения доступа или какой-либо другой. В
следующих разделах описаны типы управляющих элементов.
Диалоги
101
■ КНОПКИ
Как и другие управляющие элементы, кнопки — это отдельные
дочерние окна, с которыми работают, как с настоящими кнопками
на аппаратуре. В принципе, они могут использоваться для запуска
любой команды или сообщения и иметь произвольный размер.
К стандартным относятся следующие типы кнопок: pushbutton,
checkbox и radio button. В зависимости от стиля кнопки текстовая
метка располагается либо на самой кнопке, либо справа от нее.
Для того чтобы реагировать на мышь, кнопки не обязаны фоку¬
сировать ввод. Это требуется лишь при вводе с клавиатуры.
Кнопки radio button используются, когда требуется выбрать один
вариант из нескольких. Для этого они объединяются в группы,
которые представляют собой прямоугольники, объединяющие два
или более функционально связанных между собой элемента управ¬
ления. В группы могут объединяться и кнопки checkbox. Сами
группы не взаимодействуют с пользователем и не генерируют
никаких извещающих сообщений.
■ SCROLL BARS (ЛИНИИ ПРОКРУТКИ)
Теоретически линии прокрутки могут располагаться в любой части
окна. Они особенно удобны, когда пользователь должен выбирать
значение из непрерывного спектра. При передвижении (с по¬
мощью мыши) индикатора вдоль линии прокрутки посылаются
сообщения, которые изменяют значения в программе и внешний
вид линии прокрутки. Имеются два различных типа линий про¬
крутки: линии, служащие частью границы окна, и независимые
линии, которые могут быть никак не связаны с прокруткой текста.
В случае независимых линий прокрутки приложение самостоя¬
тельно определяет, что должно происходить при перемещении
индикатора. Windows содержит функцию SetScrollRange, задаю¬
щую диапазон значений, выбираемых при помощи линии прокрут¬
ки. За интерпретацию посылаемых значений отвечает прикладная
программа.
■ ДИАЛОГИ
Диалоги и меню — это наиболее важные элементы пользователь¬
ского интерфейса Windows. Типичным результатом выбора опции
меню будет открытие диалогового окна. Диалоги, или диалоговые
окна, — это всплывающие окна, используемые, как правило, для
краткого взаимодействия с пользователем. Они обычно запраши¬
вают у пользователя дополнительную информацию или значения
каких-нибудь режимов. Могут объявляться как модальные, так и
немодальные диалоги. В немодальном в отличие от модального
режима родительское окно не отключается. Пример такого диало¬
102
Разработка программ для Windows 3.0
га имеется в программе Write: выполнение команды Find откры¬
вает диалоговое окно, но пользователю разрешается продолжать
редактирование. Подобное средство применимо для программ про¬
верки орфографии и других сходных вспомогательных инструмен¬
тов.
Немного подумав, вы поймете, что между использованием клавиш
в модальных и немодальных диалогах имеется существенная
разница. В немодальных диалогах все определенные ранее клави¬
ши должны сохранят^ прежний смысл, в то время как в модаль¬
ных назначением этих клавиш можно управлять. Windows
предопределяет назначение нескольких клавиш для диалоговых
окон. Функции Windows для обеспечения диалогов приведены в
табл. 4.6.
Таблица 4.6. Диалоговые функции Windows
DialogBox
CreateDialog
EndDialog
IsDialogMessage
GetDlgItem
SendDlgMessage
SetDlgItemText
CreateDialogParam
■ ОКОШКИ СПИСКОВ
Окошки списков делятся на две категории. Одни позволяют вы¬
брать лишь один элемент, другие — произвольное число элемен¬
тов. Имеется возможность строить списочные окошки, состоящие
из нескольких колонок. В окошках, позволяющих выбрать только
один элемент, выбор нового элемента автоматически подавляет
выбор предшественника. Окошки списков с множественным вы¬
бором позволяют подсветить сразу несколько элементов. В окош¬
ках, состоящих из нескольких колонок, если число элементов
превышает высоту окошка, при прокрутке элементы переносятся
из одной колонки в другую.
■ КОМБИНИРОВАННЫЕ ОКОШКИ
Комбинированные окошки представляют собой расширения око¬
шек списков. Они допускают редактирование. Имеются три основ¬
ных стиля комбинированных окошек: простые, выпадающие и
выпадающие с поиском. В простом комбинированном окошке
ниже поля редактирования всегда находится окошко списка.
Интерфейс с графическим устройством
103
Когда комбинированное окошко содержит фокус» в поле редакти¬
рования можно писать. При использовании стиля "выпадающий”
список первоначально не виден. Визуально можно наблюдать
только поле редактирования и маленькую стрелку справа от него.
Если с помощью мыши выбрать строку, то ”выдадает” списковое
окошко. Стиль "выпадающий с поиском” полностью совпадает с
предыдущим, за исключением того, что пользователь может вы¬
бирать элементы из списка, вводя их первые буквы в поле редак¬
тирования»
■ ВСПЛЫВАЮЩИЕ МЕНЮ
В новой версии Windows появилась возможность создавать меню,
которые не привязаны к определенной позиции верхней строки
меню, а ”всплывают” при нажатии кнопки на многокнопочной
мыши. Это существенно для интерфейса с пользователем, посколь¬
ку помогает ему сберечь время. Более не требуется постоянно
подтаскивать курсор мыши вверх к строке меню. Меню можно
воспользоваться в том месте, где вы оказались в процессе работы.
■ ИНТЕРФЕЙС С ГРАФИЧЕСКИМ УСТРОЙСТВОМ
Интерфейс с графическим устройством (GD1 — graphics device
interface) предназначен для обеспечения кода, не зависящего от
характеристик дисплея, предоставляя осуществлять трансляцию
на каждый тип аппаратуры драйверу устройства. Независимость
достигается за счет вывода на виртуальное логическое простран¬
ство, а затем отображения его на дисплеи, принтеры, плоттеры и
т.п. Для этого может быть установлен один из восьми режимов
отображения. Шесть из восьми режимов ограниченные". Режим
Isotropic — частично ограниченный, а режим Anisotropic — неог¬
раниченный. В ограниченных режимах логические единицы ото¬
бражены на реальные единицы длияы. Например, в режиме Low
English логические единицы соответствуют 0.1 дюйма. В неогра¬
ниченных режимах окно и соответствующий порт вывода самосто¬
ятельно вычисляют коэффициенты растяжения. В табл. 4.7
перечислены режимы отображения.
Windows GDI поддерживает вывод графики, подготовленной при
помощи битовых карт, кистей и перьев. В GDI предопределены 3
вида перьев: Black (черное), Null и White (белое). Они могут
использоваться функцией GetStockObject. Предопределено также
7 кистей (Brush): Black, Dark-Gray, Gray, Hollow, Light-Gray, Null
и White. Имеются шесть шаблонов закраски. GDI поддерживает
12 стилей форматирования текста.
104
Разработка программ для Windows 3. О
Таблица 4.7. Режимы отображения GDI
Режим отпображеяия Единица измерения
Anisotropic Произвольная
High English
1000
дюймов
High Metric
100
миллиметров
Low English
100
дюймов
Low Metric
10
миллиметров
Text
1
пиксел устройства
Twips
1440
дюймов
Контексты устройства и информационные контексты
Контекст устройства Windows — это определенная связь между
прикладной программой, драйвером устройства и самим устройст¬
вом вывода, скажем лазерным принтером. Его можно также
рассматривать как цепочку вывода. Информационный контекст —
это контекст устройства исключая возможность вывода.
Имеются всего 17 различных функций Windows GDI. Они пере¬
числены в табл. 4.8.
Метафайлы — это файлы, которые хранят не графические образы,
а создающие их команды. Функции работы с метафайлами
Windows перечислены в табл. 4.9. Они обладают значительной
Таблица 4.8. Категории функций GDI
1. Функции контекста устройства
2. Функции рисования
3. Функции палитры цветов
4. Функции атрибутов рисования
5. Функции отображения
6. Функции работы с координатами
7. Функции регионов
8. Функции клиппирования
9. Функции вывода линий
10. Функции рисования эллипсов и многоугольников
11. Функции работы с битовыми картами
12. Функции работы с текстом
13. Функции работы со шрифтами
14. Функции работы с метафайлами
15. Функции управления принтером
16. Функции ESC-кодов принтера
17. Функции опроса среды
Интерфейс со многими документами (MDf — Multi-Document Interface)
105
Таблица 4.9. Функции работы с метафайлами
CreateMetaFile
CopyMetaFile
GetMetaFile
DeleteMetaFile
EnumMetaFile
PlayMetaFileRecord
AnimatePalette
SetDlBitsToDevice
гибкостью и не сводятся к простой записи последовательности
действий при создании изображения.
Среда Windows разрабатывалась в расчете на ситуации, когда
число требуемых цветов превышает возможности дисплея. Это
может произойти, когда одну и ту же логическую палитру исполь¬
зуют несколько приложений.
■ ИНТЕРФЕЙС СО МНОГИМИ ДОКУМЕНТАМИ
(MDI - MULTI-DOCUMENT INTERFACE)
Интерфейс MDI присутствовал и в более ранних версиях Windows.
Как следует из названия, это стандарт, позволяющий приложению
манипулировать и управлять более чем одним документом одно¬
временно. Это сделано так: каждый документ располагается в
отдельном дочернем окне, а управление по-прежнему сосредотачи¬
вается в единственном родительском окне.
В многодокументных приложениях основное окно служит в каче¬
стве рамки. Использование этого интерфейса позволяет ограни¬
читься весьма небольшим количеством специализированного кода
и данных. Среда Windows предоставляет стандартные средства
управления.
Первый шаг в создании прикладной программы с использованием
MDI — описание фазы инициализации. Вы должны зарегистриро¬
вать два класса окон: один для рамки, а другой для окна докумен¬
та. По сравнению с обычным дочерним окном у регистрационной
структуры для класса окна документа имеется ряд отличий. Для
него обязательно нужна пиктограмма, чтобы дать пользователю
возможность минимизировать дочернее окно тем же способом, что
и основное. У этого окна должно быть пустое имя, поскольку такие
окна не могут иметь собственных меню. Кроме этого должно
резервироваться дополнительное место в памяти для запоминания
имени файла, ассоциированного с каждым документом.
106
Разработка программ дпя Windows 3. 0
■ ДИНАМИЧЕСКИ ПОДКЛЮЧАЕМЫЕ БИБЛИОТЕКИ
(DLL - DYNAMIC LINK LIBRARIES)
При переходе на версию 3.0 необходимо обработать все ранее
созданные динамически подключаемые библиотеки с помощью
компилятора ресурсов. Это нужно хотя бы для того, чтобы поста¬
вить штамп: ”версия 3.0”. Необходимо предотвратить появление
при начале работы сообщения, предупреждающего пользователя
о возможной несовместимости. Наличие штампа версии означает,
что разработчики учли условия нового окружения Windows. Ди¬
намически подключаемые библиотеки, начиная с версии 3.0,
должны вместо расширения .EXE, характерного для более ранних
версий, иметь расширение .DLL. Так сделано, чтобы пользователи
не путали библиотеки с непосредственно выполнимыми приложе¬
ниями Windows. Для программистов работа с новыми библиотека¬
ми проще, поскольку вместо указания входных точек вручную на
машинном языке можно воспользоваться стандартной функцией
установки точек входа.
DLL требуют процедур инициализации и завершения
Версия 3.0 динамически подгружаемых библиотек требует выпол¬
нения процедуры выхода Windows с именем WEP. Это сообщение
посылается^, чтобы уведомить библиотеки, что они выгружаются.
Можйо проектировать приватные DLL, позволяющие перемещать
себя в область верхних адресов памяти, оставляя пространство
основной памяти для других нужд.
Для совместимости с Windows 3.0 требуется обработка DLL ком¬
пилятором ресурсов. Одной из неудач следует признать опцию -p,
определяющую DDL как приватную. Она противоречит самой идее
библиотек DLL. Каждый, кто помечает свою библиотеку как
приватную, получает избыточный код и теряет память. Кроме
этого те, кто не "приватизирует” свои DLL, имеют существенные
преимущества на рынке.
■ ДИНАМИЧЕСКИЙ ОБМЕН ДАННЫМИ
(DDE - DYNAMIC DATA EXCHANGE)
DDE — один из нескольких способов обмена данными между
приложениями Windows. Основным здесь является протокол со¬
общений, позволяющий пересылать данные, как непосредственно
используя для этого сообщения DDE, так и передавая дескрипторы
блоков разделяемой глобальной памяти. Приведем типичные при-
ложения, в которых может быть использован динамический обмен
данными.
Компилятор ресурсов
107
1. Связь с работающими в реальном режиме времени источ¬
никами данных, такими, как датчики управления процес¬
сами, биржевые сводки и т.д.
2. Использование составных документов, т.е. документов, со¬
стоящих из нескольких файлов.
3. Связь по данным между базой данных и электронной таб¬
лицей.
4. Информационный обмен между разными компьютерами,
работающими под управлением Windows.
■ МАШИННО-НЕЗАВИСИМАЯ ЦВЕТНАЯ ГРАФИКА
В отличие от монохромных дисплеев в цветных битовых картах
отсутствует взаимноюднозначное соответствие между битами па¬
мяти и пикселами. Способ представления цвета в памяти сущест¬
венно зависит от адаптера дисплея и его возможностей. Начиная
с версии Windows 3.0, функции, управляющие побитовыми изо¬
бражениями, делают это способом, не зависящим от используемой
аппаратуры. Новые форматы пиктограмм и курсоров выполнены
в виде наборов аппаратно-независимых битовых карт. Подробнее
об этом будет рассказано при описании инструмента SDKPaint.
■ СИСТЕМА ПОСТРОЕНИЯ СПРАВОЧНОЙ
ИНФОРМАЦИИ (HELP)
Одним из наиболее существенных дополнений к Windows стала
система построения справочной информации. Соответствующий
инструмент SDK состоит из двух основных частей: Help engine и
компилятор Help. Help engine входит в состав поставки Windows
и доступен всем пользователям. Он используется для визуализа¬
ции файлов Help в процессе работы. Изготовление файлов Help
происходит в несколько этапов. Сначала необходимо написать
текст, который должен появиться в системе Help. Для этого можно
воспользоваться любым редактором, который готовит файлы в
формате ASCDL Затем в текст вставляются управляющие символы,
используемые компилятором Help.
■ КОМПИЛЯТОР РЕСУРСОВ
Этот инструмент используется для создания ресурсов Windows, т.е
всевозможных меню, курсоров, пиктограмм и диалоговых окон,
используемых приложением. Компилятор использует описашш
ресурсов из файлов с расширением .RC, которые содержат имена
и описания всех требуемых приложением ресурсов. Он компили¬
рует их в файл .RES, склеивает их с файлом .EXE приложения и
ставит затем штамп версии Windows. Перед тем как воспользо¬
108
Разработка программ для Windows 3.0
ваться компилятором ресурсов, необходимо создать все нужные
для данного приложения файлы пиктограмм, курсоров, шрифтов,
битовых карт и диалогов. После чего они описываются в файле
ресурсов.
В описаниях таких ресурсов, как шрифты, курсоры и диалоги,
которые определяются в своих собственных файлах, содержатся
только имена ресурсов и ссылки на содержащие их файлы. Однако
в случае меню скрипт должен содержать полное описание. Как
только описание составлено, вы компилируете его при помощи
компилятора ресурсов (RC.EXE). Эта программа может быть вы¬
звана несколькими разными способами. В последующих главах
будут даны примеры различных способов использования этого
инструмента.
Вариант Windows для разработчиков
Вариант окружения Windows, предназначенный для отладки про¬
грамм, проверяет корректность дескрипторов окон, передаваемых
функциям Windows. В отладочном режиме доступна символьная
информация, содержащаяся в составляющих Windows динамиче¬
ски подключаемых библиотеках. Это облегчает диагностику оши¬
бок с помощью отладчика CodeView for Windows. Отладочный
вариант Windows позволяет наилучшим образом использовать
программу HeapW alker.
Установка отладочного варианта
Отладочный вариант Windows состоит из альтернативных версий
основных DLL системы Windows: KRNL286.EXE, KRNL386.EXE,
GDLEXE, и USER.EXE, которые продаются вместе с SDK и обычно
располагаются в директории WTNDEV\DEBUG. Чтобы запустить
Windows в отладочном режиме, эти библиотеки и соответствующие
им символьные файлы должны быть скопированы в директорий
WDTOOWS\SYSTEM. Прежде чем сделать это следует переимено¬
вать рабочие версии этих DLL или скопировать их для безопасно¬
сти в другой директорий.
■ РЕДАКТОРЫ РЕСУРСОВ И ИНСТРУМЕНТЫ
При создании программ для Windows должен гарантироваться
контроль за разделяемыми ресурсами со стороны окружения.
Одним из таких ресурсов является память. Дисплей также явля¬
ется разделяемым ресурсом. Не следует путать ресурсы компью¬
тера и ресурсы Windows. Ресурсы Windows — это реальные
программы, видимые на экране. В следующих разделах я рассмот¬
рю различные инструменты, предусмотренные в SDK для помощи
в создании ресурсов Windows.
Утилита SDKPaint
109
■ РЕДАКТОР ДИАЛОГОВ
Этот инструмент позволяет быстро создавать диалоги. Вместо
определения операторов диалога в описаниях ресурсов редактор
диалогов дает возможность проектировать и проверять элементы
управления на экране в интерактивном режиме. Спецификации
диалогов выводятся в виде файлов .DLG и .RES, используемых
компилятором ресурсов. Редактор диалогов создает также файлы
заголовков с операторами #define, определяющими константы,
которые идентифицируют управляющие элементы. Последняя ре¬
ализация редактора позволяет строить и поддерживать каталоги
специализированных управляющих элементов для диалогов.
Редактор диалогов содержит альтернативный интерфейс пользо¬
вателя, называемый Toolbox, работающий по принципу графиче¬
ского редактора и предоставляющий те же возможности, что и
система меню. Над управляющим элементом можно выполнять
следующие действия: движение, изменение размеров, удаление,
редактирование текста, изменение стиля управляющего элемента
и его идентификатора. Вы можете рассматривать группы управля¬
ющих элементов и определять порядок перемещения фокуса ввода.
Порядок,используемый по умолчанию,совпадает с последовательно¬
стью создания элементов и не зависит от их расположения.
■ УТИЛИТА SDKPAINT
Появившаяся в версии 3.0 утилита SDKPaint является очень
гибкой графической программой, способной создавать и модифи¬
цировать объекты трех типов: битовые карты, пиктограммы и
курсоры. Им соответствуют три типа файлов: .BMP, .ICO и .CUR,
которые могут включаться в описание ресурсов и компилироваться
в соответствующий файл .RES. Могут создаваться файлы как
цветных, так и монохромных аппаратно-независимых битовых
карт. Каждая битовая карта содержит уникальный графический
образ. Пиктограммы и курсоры представляют собой наборы кар¬
тинок, каждый из которых разработан для визуализации опреде¬
ленного объекта Windows.
При открытии файлы битовых карт, созданные для версий 2.X,
автоматически преобразуются к формату Windows 3.0. Из-за того,
что файлы пиктограмм и курсоров содержат несколько изображе¬
ний, вы вначале открываете файл, а затем загружаете интересую¬
щее вас изображение. Для этого служит отдельное меню. В
открытые файлы пиктограмм и курсоров можно добавлять новые
изображения. Для их создания служат многочисленные инстру¬
менты, изображенные в нижней части окна SDKPaint.
110
Разработка программ для Windows 3. О
■ РЕДАКТОР ШРИФТОВ FONTEDIT
Программа FontEdit помогает создавать новые шрифты и модифи¬
цировать имеющиеся. Она работает только с растровыми экранны¬
ми шрифтами. Окно редактирования представляет собой
увеличенную сетку, содержащую букву загруженного в редактор
шрифта. Редактирование состоит в закраске и удалении отдельных
пикселов. Единственным способом создания нового шрифта явля¬
ется модификация существующего. С этой целью в SDK имеются
образцы шрифтов. Когда шрифт загружается, вы видите букву А
в двух видах: обычном и разбитом на клетки. При этом каждый
пиксел представлен небольшим прямоугольником.
Можно перемещаться вдоль всего множества символов данной
гарнитуры, содержащего весь алфавит, включая заглавные и
строчные буквы. Кроме ввода и удаления отдельных пикселов
редактор шрифтов позволяет работать с целыми рядами и колон¬
ками, а также выполнять мощные операции для разных видов
штриховки. Есть команды, изменяющие ширину букв. Изменение
высоты букв возможно только при выборе общей высоты всего
шрифта.
■ ЛУПА
Zoomin — это специальное окно, в котором отображается прямо¬
угольный фрагмент экрана ^?uc. 4.1). Масштаб изображения может
изменяться вслед за перемещением индикатора линии прокрутки.
Если вы нажмете кнопку мыши внутри окна и подвинете ее, держа
кнопку нажатой, то сможете переместить рамку прямоугольника
лупы по экрану. Внутри окна будет видна захватываемая лупой
часть экрана. Если в рамку лупы попадет часть границы окна
Zoomin, вы увидите картину бесконечного отражения в зеркалах.
■ ПРОФАЙЛЕР
Профайлер (утилита построения профиля программы) состоит из
трех частей: 1) утилиты случайной выборки, 2) утилиты построе¬
ния отчета и 3) набора вызываемых из приложений функций. При
работе программы утилита случайной выборки запоминает инфор¬
мацию о времени, затраченном между двумя соседними метками,
и адресах кода. Предусмотрены два варианта утилиты случайной
выборки: для реального и для защищенного режимов. В реальном
режиме используется PROF.COM, а в защищенном эта утилита
требует дополнительного драйвера VPROD. 386. В результате
работы этой утилиты создаются два отдельных файла: один содер¬
жит выборку значений регистра DP, а другой содержит информа¬
цию о перемещениях сегментов кода. После того как сбор
Spy (мониторинг сообщений)
111
информации будет завершен, можно для отображения результатов
запустить программу SHOWHrTS.EXE.
Вы можете указать профайлеру, где начинать и где заканчивать
сбор информации, включив в код обращения к функциям
ProfStart и ProfStop. Функцию ProfStop не следует путать с
функцией ProfFinish, которая необходима для сбросов результатов
профайлера на диск без выхода из Windows. Как только информа¬
ция сброшена, может запускаться программа SHOWHTTS для
подготовки отчета.
■ SPY (МОНИТОРИНГ СООБЩЕНИЙ)
Программа Spy (шпион) — удобное средство отладки, позволяющее
разобраться, как работает система сообщений Windows (puc.4.2).
Эта очень простая в работе программа осуществляет мониторинг
сообщений, направленных определенному окну. Она вызывается,
как обычная программа Windows. Войдя в нее, следует выбрать
команду Set Window и указать интересующее окно, нажав клави¬
шу мыши. Начиная с этого момента поток сообщений этому окну
начнет отображаться в окне программы Spy. Даже простое пере¬
движение мыши в выбранном окне сопровождается большим
числом сообщений WM_SETCURSOR и WM_MOUSEMOVE. В ме-
Рис. 4.1. Утилита Zoomin пакета SDK
112
Разработка программ для Windows 3. 0
Рис. 4.2. Показан диалог Options утилиты Spy пакета SDK
ню Options с помощью крестиков в квадратиках cheehbox можно
задать фильтр, выделяющий только интересующие нас сообщения.
Вы можете отобрать тип отслеживаемых сообщений. Кроме этого
можно задать, интересуетесь ли вы только входящими сообщени¬
ями, только исходящими или и теми и другими вместе.
■ HEAPWALKER (МОНИТОРИНГ КУЧИ)
Это очень важное средство (puc. 4.3), поскольку позволяет отсле¬
живать состояние системной памяти Windows — кучи. Она рабо¬
тает как в реальном, так и в защищенном режиме. Если вы
работаете в защищенном режиме, то HeapWalker обходит области
памяти, расположенные выше DOS, программ TSR и загруженных
драйверов. HeapWalker имеет несколько режимов обхода. При
обходе осматривается содержимое определенных мест в памяти и
в окне HeapWalker строится список. Например, обход может
включать список LRU. При этом обходе вся допускающая сброс
память упорядочивается в список, в котором последний элемент
будет использован первым. В этом списке можно выбрать произ¬
вольное число элементов, и с помощью команды ADD меню узнать
в диалоговом окне суммарное число байтов в этих элементах.
Shaker (миксер для кучи)
113
Рис. 4.3. Меню Walk утилиты HeapWalker пакета SDK
■ SWAP (ОБМЕН)
Swap яэляется инструментом, позволяющим обнаружить межсег¬
ментные вызовы процедур. Это нужно для повышения производи¬
тельности программ, поскольку в быстрых программах число,
таких переходов должно быть сведено к минимуму. Использование
программы Swap требует встраивания в приложение функции
SwapRecording. Не обойтись без файлов .SYM, которые создаются,
если при запуске линкера указана опция /map. Кроме этого Swap
требует программы SKKERNAL.EXE, которая в существующей
версии Windows работает только в реальном режиме. Если исполь¬
зовать режим, заданный по умолчанию, то Swap нужно много
времени для обработки всех модулей и имен. Обычно имеет смысл
выделять те модули, которые следует изучать с помощью програм¬
мы Swap. Для этого служит опция -М.
■ SHAKER (МИКСЕР ДЛЯ КУЧИ)
Эта простая программа захватывает псевдослучайным образом в
глобальной куче участки памяти, что приводит к перемещениям
сегментов кода. Таким способом можно убедиться в том, что не
возникает проблем, связанных с изменением расположения сег-
114
Разработка программ для Windows 3. 0
ментов кода. Программа Shaker работает автономно. После того
как ваша программа запущена, вы можете проверить, не прояви¬
лись ли какие-либо ошибки. Перед началом работы Shaker позво¬
ляет установить несколько параметров, включая Ganularity, Time
biterval и Max Objects. Granularity задает минимальный размер
размещаемого объекта. Time Interval устанавливает временные
интервалы, измеряемые в квантах системного времени. Мах
Objects устанавливает максимальное число объектов, размещае¬
мых в памяти. Лучший способ понять работу этой программы —
запустить ее. Она проста и вполне очевидна.
■ CODEVIEW FOR WINDOWS
Как уже говорилось ранее, CodeView for Windows — это символь¬
ный отладчик для Windows. Он предназначен для работы со
вторым экраном и функционирует только в защищенном режиме.
Он также требует не менее двух мегабайтов расширенной памяти.
По умолчанию CodeView отображается на втором экране, а отла¬
живаемая программа — на первом. Эта версия CodeView похожа
на версию той же программы для DOS, но она содержит дополни¬
тельные функции, специально разработанные для отладки прило¬
жений Windows. Например, CodeView for Windows может
отслеживать сегменты кода и данных приложения, перенастраи¬
вая свою таблицу символов, при изменении места расположения
программы в памяти. Отладчик отображает также сообщения
Windows, связанные с перемещением сегментов и созданием новых
объектов. Ниже приведены пять новых команд, специально пред¬
назначенных для программирования в среде Windows:
wdl
wdg
wdm
wwm
wbm
■ ОТЛАДЧИК SYMDEB ДЛЯ РЕАЛЬНОГО РЕЖИМА
Symbolic Debug Utility, или кратко Symdeb, — инструмент, исполь¬
зуемый для отладки программ в реальном режиме. Как и CodeView
for Windows, Symdeb также требует второго экрана, однако в этом
случае имеется возможность использовать один терминал. Про¬
грамма Mapsyn работает совместно с Symdeb, преобразуя содержи¬
мое файлов .MAP к формату .SYM, который использует отладчик.
Структура прикладных программ для Windows
115
■ ОБЕСПЕЧЕНИЕ СОВМЕСТИМОСТИ МЕЖДУ
ВЕРСИЯМИ 2.0 И 3.0
Для того чтобы программой могло воспользоваться максимальное
число пользователей, она должна быть совместимой со всеми
версиями Windows, начиная со второй. При создании такой про¬
граммы нужно следовать специальным правилам. Одна из причин
этого состоит в новой структуре памяти. Кроме ограничений,
связанных с управлением памятью, имеются еще два типа согла¬
шений, касающихся флага разрешения прерываний и пропорци¬
ональных системных шрифтов.
Все приложения, разработанные для Windows 2.0, находясь в
среде версии 3.0, получают для меню системные пропорциональ¬
ные шрифты. Чтобы пользоваться ими в диалогах, управляющих
элементах и области пользователя, приложения должны быть
промаркированы специальным образом. Один из побочных эффек¬
тов появления пропорциональных шрифтов состоит в том, что
программы, предполагающие, что все буквы имеют одинаковую
ширину, перестают правильно работать. Для выравнивания таких
элементов, как коды клавиш ускоренного ввода команд в меню,
вместо последовательности пробелов следует использовать клави¬
шу TAB. Ограничения на работу с флагом разрешения прерываний
относятся только к программам на языке ассемблера, использую¬
щим инструкции CLI и STL Поскольку в этой книге не ведется
речь о языке ассемблера, мы не будем останавливаться на этом
ограничении.
■ СТРУКТУРА ПРИКЛАДНЫХ ПРОГРАММ
ДЛЯ WINDOWS
Как правило, функция WinMain приложения выполняет следую¬
щие действия:
1. Регистрирует классы окон, которые используются прило¬
жением, и выполняет другие действия по инициализации.
2. Создает основное окно приложения и, если требуется, дру¬
гие окна.
3. Запускает цикл обработки сообщений из очереди приложе¬
ния.
4. Ожидает сообщения WM_QUIT, сообщающего о заверше¬
нии программы.
Для каждого приложения Windows передает функции WinMain
четыре параметра. Один (hbistance) служит для дескриптора эк¬
земпляра, второй (hPrevInstance) — для предыдущего экземпляра
приложения, еще один содержит длинный указатель на заканчи¬
116
Разработка программ для Windows 3.0
вающуюся нулем командную строку, и последний (nCmdShow),
который может быть передан функции Show Window, отображаю¬
щей основное окно приложения.
Приложение включает также набор функций СаИЬаск, которые
вызываются сообщениями Windows.
■ СТИЛЬ ПОЛЬЗОВАТЕЛЬСКОГО ИНТЕРФЕЙСА
WINDOWS
Фирма Microsoft составила некоторые рекомендации по стилю
использования средств разработки Windows для построения при-
лрограмм. Приведем два из них.
1. Не включайте в диалоги меню.
2. Используйте рамочную границу для модальных диалого¬
вых окон.
■ ОПИСАНИЯ РЕСУРСОВ
Язык Resource Script фирмы Microsoft дает способ определения
ресурсов, которые должны быть скомпилированы, а затем добав¬
лены к выполняемому файлу прикладной программы. Этот язык
содержит восемь различных типов операторов: 1)одиночные стро¬
ки; 2)ресурсы, определяемые пользователем; З)данные; 4)таблицы
строк; 5)клавиши ускорения; 6)меню; 7)диалоги; 8) директивы.
Для определения таких ресурсов, как пиктограммы, шрифты и
битовые карты, которые содержатся в отдельных файлах, исполь¬
зуются одиночные строки, указывающие идентификатор ресурса
и соответствующее имя файла. Формат позволяет задать одну из
опций, определяющую ресурс как FIXED (фиксируемый),.
MOVEABLE (перемещаемый) или DE3CARDABLE (сбрасываемый).
Ресурсы могут объявляться как заранее загружаемые или как
загружаемые по запросу. Допускаются необрабатываемые и опре¬
деляемые пользователем форматы данных. Могут быть определены
таблицы, состоящие из пронумерованных строк ASCH. Их можно
извлечь по номеру, используя функцию LoadString.
Вслед за метками меню идут номер Ш или имя константы с заранее
определенным целым значением.
В табл. 4.10 перечислен список ключевых слов, используемых в
скриптах ресурсов.
■ ЗАКЛЮЧЕНИЕ
SDK представляет собой внушительный набор средств разработки.
Он весьма сложен. По этой причине в настоящее время проявилось
множество средств создания программ для Windows, изготовлен-
Заключение
117
Таблица 4.10. Список ключевых слов
RCDATA RTEXT
STRINGTABLE CTEXT
ACCELERATORS PUSHBUTTON
MENU LE3TBOX
MENUITEM GROUPBOX
POPUP DEFPUSHBUTTON
DIALOG RADIOBUTTON
STYLE EDITTEXT
CAPTION COMBOBOX
MENU ICON
CLASS SCROLLBAR
FONT CONTROL
LTEXT CHECKBUTTON
ных другими фирмами, стремящимися облегчить жизнь програм¬
мисту. Они, безусловно, заслуживают внимания. Хотя в настоящее
время существует пара продуктов, которые позволяют обходитьс§
при написании программ вовсе без SDK, для серьезных разработчи¬
ков средства, подобные Spy,HeapWalker и Coceview for Windows,
по-прежнему делают использование SDK весьма привлекательным
вне зависимости от выбранного языка программирования.
У архитектуры Windows имеются некоторые преимущества объ-
ектно-ориентированных систем. Программные системы, которые
можно построить под Windows, будут напоминать их. Это проис¬
ходит благодаря обмену сообщениями между независимыми моду¬
лями, которые функционируют независимо и которые не требуется
кодировать от нуля каждый раз, когда они нужны. Имеются
классы объектов-шаблонов, которые могут быть использованы для
штамповки готовых частей программ. Однако сходство кончается
в этом месте. Наиболее привлекательные возможности ООП отсут¬
ствуют. Когда дело доходит до кодирования, обычное программи¬
рование на Си имеет мало общего с объектно-ориентированным
стилем, свойственным архитектуре Windows. Легко заметить, на¬
пример, что,для того чтобы воспользоваться преимуществами, ко¬
торые дают ресурсы Windows, требуется написать много кода. Этот
стандарт требует избыточности кода.
С позиции ООП чувствуется определенная незавершенность
Windows SDK, используемого в паре с ”плоским” Си. В принципе,
вместе с Windows может быть использован любой язык. Однако
использование любого другого языка, отличного от Си, рекомен¬
дованного фирмой Microsoft, сопряжено с определенными трудно¬
стями. На практике этот язык должен выдавать код весьма
118
Разработка программ для Windows 3. 0
специфического вида и работать со всеми соглашениями о моделях
памяти, существующими в Windows. Это, впрочем, касается ско¬
рее не языка, а компилятора. По этой причине вы обнаружите
серьезные достоинства у объектно-ориентированных языков, спе¬
циально перенесенных для использования в среде Windows. Ос¬
новная идея этой книги состоит в том, что наиболее естественный
подход к программированию для Windows состоит в использова¬
нии объектно-ориентированной технологии.
Поскольку даже относительно простая программа для Windows
требует большого объема кода, естественным образом возникает
практика создания родовых приложений, которые обслуживают
множество областей и способны принять код, специфичный для
конкретного приложения. В Windows SDK имеется пример такой
родовой программы.
■ УПРАВЛЯЮЩИЕ СООБЩЕНИЯ РЕДАКТИРОВАНИЯ
EM_CANUNDO
EM_EMPTYUNDOBUFFER
EM_FMTLINES
■ НОВЫЕ ФУНКЦИИ ВЕРСИИ 3.0
AdjustWindowRectEx Вычисляет размер прямоугольника
AppendMenu
AnsiLowerBuff
AnsiToOemBuf
AllocSelector
AnimatePalette
AllocCStoDSAlias
AllocDStoCSAlias
для окна расширенного стиля
По селектору сегмента кода
возвращает селектор сегмента
данных для чтения и записи команд
По селектору сегмента данных
возвращает селектор сегмента
кода, который может быть
использован для выполнения
команд, находящихся в сегменте
данных
Размещает новую копию селектора
Заменяет записи в логической
палитре
Преобразует символы, находящиеся
в буфере, в строчные символы
Преобразует строку символов ANSI
в набор символов, определенный
разработчиком аппаратуры
Добавляет новый элемент в конец
меню
Новые функции версии 3. 0
119
ArrangeIconicW indows
BeginDeferWindowPos
ChangeSelector
CreateCursor
GreateDialogIndirectParam
CreateDialogParam
CreateDIBitmap
CreateDffiPatternBrush
CreateIcon
CreatePalette
CreatePolyPoligonRgn
CreatePopupMenu
CreateRoundRectRegn
CreateWindowEx
DebugBreak
DefDlgProc
DeferWindowPos
DefFrameProc
DefineHandleTable
Располагает подокна, представленные
в виде пиктограмм
Инициализирует память для
многооконной структуры
Генерирует временный селектор кода,
соответствующий селектору данных,
или, наоборот, сегмент данных,
соответствующий сегменту кода
Создает курсор указанной высоты,
ширины и конфигурации битов
Создает немодальный диалог по
шаблону и передает ему данные
Создает немодальный диалог и
передает ему данные
Создает зависящую от устройства
битовую карту по
аппаратно-независимой карте
Создает логическую кисть, имеющую
шаблон, определяемый аппаратно¬
независимой битовой картой
Создает пиктограмму с указанными
высотой, шириной, набором
цветов и конфигурацией битов
Создает логическую палитру
Создает область, представляющую
собой объединение замкнутых
многоугольников
Создает пустое подменю
Создает прямоугольную область с
закругленными углами
Создает окно расширенного типа
Вызывает переход к отладчику
Обеспечивает обработку по
умолчанию любого сообщения
Windows, которое не обрабатывает
сам диалог
Модифицирует структуру,
содержащую положения и размеры
одновременно нескольких окон
Обеспечивает обработку по умолчанию
сообщений, не обрабатываемых
функцией основного окна MDI
Создает индивидуальную таблицу
дескрипторов
120
Разработка программ для Windows 3.0
DefMDIChildProc
DeleteMenu
DestroyCursor
DestroyIcon
DeviceCapabilities
DialogBoxbidirectParam
DialogBoxParam
DlgDirListComboBox
DlgDirSelectComboBox
DOS3Call
DrawFocusRect
EndDeferWindowPos
ExitWindows
ExtDeviceMode
ExtFloodFill
FatalAppExit
FreeModule
FreeSelector
GetClassInfo
Обеспечивает обработку по умолчанию
сообщений, не обрабатываемых
функцией дочернего окна MDI
Удаляет подменю из указанного
меню и освобождает занимаемую
им память
Уничтожает созданный функцией
CreateCursor курсор и освобождает
занимаемую им память
Уничтожает созданную функцией
CreateIcon пиктограмму и освобождает
занимаемую ею память
Выдает характеристики принтера
Создает модальный диалог по
шаблону и передает ему данные
для инициализации
Создает модальный диалог
и передает ему данные
Заполняет комбинированный список с
именами файлов по заданному пути
и расширению
Копирует текущий пункт из
комбинированного списка в строку
Позволяет программам Windows
выполнять прерывание 21h
Рисует прямоугольник, указывающий
текущий фокус ввода
Изменяет расположение и размер
окна при перерисовке экрана
Инициирует стандартную
последовательность выключения
Windows
Получает или модифицирует
информацию для данного драйвера
Заполняет область на экране,
используя текущую кисть
Показывает сообщение о фатальной
ошибке и покидает Windows
Уменьшает на единицу список
ссылок на модуль
Освобождает ранее захваченные
селекторы
Дает информацию о классе окна
Новые функции версии 3.0
121
GetCodebifo
GetCurrentPDB
GetDesktopW indow
GetDialogBaseUnits
GetDlBits
GetDlgCntlID
GetDOSEnvironment
GetDriveType
GetFreeSpace
GetKBCodePage
GetKeyboardType
GetKeyNameText
GetLastActivePopup
GetMenuCheckMarkDimensions
GetNearestPaletteIndex
GetPaletteEntries
GetPriorityClipboardFormat
GetPrivateProfileInt
Дает информацию о сегменте.
Возвращает массив из 4 16-битных
слов
Возвращает сегмент PSP
Возвращает дескриптор фона экрана
Windows
Возвращает базовые единицы диалога
для определения фактических
размеров управляющих элементов
Копирует биты из битовой карты в
заданный буфер
Возвращает ID указанного окна
Возвращает длинный указатель на
строку среды выполнения текущей
задачи
Определяет, является ли диск
переносным, стационарным или
удаленным
Возвращает число свободных байтов
в глобальной куче
Определяет, какая из кодовых
страниц OEM/ANSI загружена
системой Windows
Возвращает тип клавиатуры
Возвращает строку с именем нажатой
клавиши
Определяет, какое из подменю
данного окна было активным
последним
Возвращает размерности
предопределенной битовой карты,
используемой для изображения
отметки элементов меню
Возвращает индекс записи палитры,
наиболее близко соответствующей
цвету
Возвращает диапазон записей
логической палитры
Возвращает первый формат из
списка форматов, для которого
имеются соответствующие ему
данные в clipboard
Возвращает значение целого ключа
из указанного файла
122
Разработка программ для Windows 3.0
GetPrivateProfileString
GetRngBox
GetSystemDirectory
GetSystemPaletteEntries
GetSystemPaletteUse
GetTabbedTextExtent
GetWindowsDirectory
WindowsGetWinFlags
GlobalDosAlloc
GlobalDosFree
GlobalFix
GlobalPageLock
GlobalPageUnlock
GlobalUnfix
bisertMenu
bCharAlpha
IsCharAl phaN um eric
bCharLower
feCharUpper
Копирует строку символов
из файла в указанный буфер
Возвращает координаты
прямоугольника, ограничивающего
указанную область
Возвращает полное имя системного
директория Windows
Позволяет получить записи
системной палитры
Определяет, доступна ли программе
полная системная палитра
Вычисляет высоту и ширину
указанной строки текста
Возвращает полное имя
директория Windows
Возвращает 32-разрядное поле
флагов, описывающих процессор и
конфигурацию памяти
Распределяет глобальную память, к
которой может обращаться DOS,
работающая в реальном режиме
Освобождает блок памяти,
захваченный при помощи функции
GlobalDosAlloc
Предотвращает перемещение или
сброс блока глобальной памяти
Увеличивает счетчик постраничной
блокировки памяти
Уменьшает счетчик постраничной
блокировки памяти
Разблокирует блок глобальной
памяти
Вставляет новый элемент в меню
на определенное место
Проверяет, является ли символ
буквой
Проверяет, является ли символ
буквой или цифрой
Проверяет, является ли буква
строчной
Проверяет, является ли буква
прописной
Новые функции версии 3.0
123
lstrcmp
lstrcmpi
MapVirtualKey
ModifyMenu
MuLDiv
NetBIOSCall
OemKeyScan
OutputDebugString
PALETTEINDEX
PALETTERGB
PolyPolygon
ProfClear
ProfFinish
ProfFlush
ProflnsChk
ProfSampRate
ProfSetup
ProfStart
ProfStop
RealizePalette
Лексикографически сравнивает две
строки. Сравнение чувствительно к
регистру символов
Лексикографически сравнивает две
строки. Сравнение не чувствительно
к регистру символов
Осуществляет преобразование
между ASCn-кодами, скан-кодами
и кодами виртуальных клавиш
Изменяет существующий пункт меню
Умножает два словных числа и
делит их на третье
Позволяет приложениям Windows
использовать функции прерывания
BIOS 5Ch
Отображает OEM ASCE коды на
OEM скан-коды
Посылает сообщение отладчику
Макро, возвращающее индекс
записи палитры
Макро, возвращающее значения
интенсивности RGB
Создает набор многоугольников
Стирает накопленную профайлером
информацию
Заканчивает сбор профайлером
информации и сбрасывает
накопленные данные на диск
Сбрасывает накопленные
профайлером данные на диск
для предотвращения
переполнения буфера
Проверяет, установлен ли профайлер
Устанавливает интервалы сбора
информации профайлером
Задает параметры при работе
профайлера в усовершенствованном
386 режиме
Стартует сбор информации
профайлером
Останавливает сбор информации
Отображает элементы логической
палитры на системную палитру
124
Разработка программ для Windows 3.0
RectInRegion
RemoveMenu
ResizePalette
SelectPalette
SetDIBits
SetDIBitsToDevice
SetHandleCount
SetMenuItemBitmaps
SetPaletteEntries
SetSystemPaletteU se
StretchDIBits
SwapRecording
SwithStackBack
SwitchStackTo
TabbedTextOut
ToAscii
TrackPopupMenu
TranslateMDESysAccel
UnregisterClass
UpdateCoIors
Проверяет, находится ли
прямоугольник целиком
в заданной области
Удаляет из меню элемент вместе
с его подменю
Изменяет размер логической
палитры
Выбирает логическую палитру
Устанавливает значения
определенных битов битовой карты
Отображает аппаратно-независимую
битовую карту на экране дисплея
Изменяет число дескрипторов файлов
Ассоциирует битовую карту с
элементом меню
Устанавливает флаги и значения
цвета для ряда записей
логической палитры
Дает возможность программе
использовать полную системную
палитру
Копирует прямоугольник аппаратно¬
независимой битовой карты
Включает и выключает анализ,
выполняемый программой Swap
Возвращает стек текущей программы
в сегмент данных
Переносит стек текущей программы
в указанный сегмент
Пишет строку в определенные
колонки
Транслирует код клавиши в
соответствующий символ или
символы ANSI
Показывает подменю в определенном
месте и отслеживает выбор
элементов в нем
Обрабатывает акселераторные
комбинации клавиш для команд
системного меню в подокне MDI
Удаляет определенный класс
Windows из таблицы классов
Обновляет рабочую область
указанного контекста устройства
Новые сообщения Windows 3.0
125
ValidateCodeSegments
WinExec
WinPrivateProfileString
Wvsprintf
Выдает отладочную информацию
на терминал
Выполняет приложение с заданной
командной строкой
Копирует символьную строку
в заданный инициализационный
файл sprintf. Форматирует и
запоминает последовательность
символов в буфере. Значения
передаются как параметры функции
Форматирует и запоминает
последовательность символов в
буфере. Значения передаются
через массив
НОВЫЕ СООБЩЕНИЯ WINDOWS 3.0
CB_ADDSTRING
CB_DELETESTRDsTG
CB_Dni
CB_FINDSTRING
CB_GETCOUNT
CB_GETCURSEL
CB_GETEDITSEL
CB_GETITEMDATA
CB_GETLBTEXT
CB_GETLBTEXTLEN
CBJNSERTSTRING
CB_LIMITTEXT
СВ RESETCONTENT
Добавить строку в комбинированное
окошко
Удалить строку из
комбинированного окошка
Занести список файлов текущего
директория в комбинированное
окошко
Найти первую подходящую строку
в комбинированном окошке
Выдать число элементов
в комбинированном окошке
Выдать индекс выделенного
элемента
Выдать начало и конец выделенного
текста в редактирующем элементе
комбинированного окошка
Выдать 32-разрядное значение,
ассоциированное с элементом в
комбинированном окошке
Скопировать строку из
комбинированного окошка в буфер
Выдать длину строки в
комбинированном окошке
Вставить строку в комбинированное
окошко
Ограничить длину вводимого текста
Удалить все строки
комбинированного окошка
126
Разработка программ для Windows 3.0
CB_SETCURSEL
CB_SETEDITSEL
СВ SETITEMDATA
СВ SHOWDROPDOWN
Выбрать строку и сделать ее текущей
Выделить все символы между
первым и последним отмеченными
Ассоциировать 32-разрядное
значение с элементом
комбинированного окошка
Показать или спрятать
выпадающий список
Оповещающие сообщения для комбинированного окошка
CBN_DBLCLK
CBN_DROPDOWN
CBN_EDITCHAN GE
CBN_EDITUPDATE
CBN_ERRSPACE
CBN_KILLFOCUS
CBN_SELCHAN GE
CBN SETFOCUS
Пользователь дважды нажал кнопку
мыши на строке комбинированного
окошка
Сообщить дочернему окну, что
должен выпасть список его
комбинированного подокна
Пользователь изменил текст в
редактирующем элементе
Необходимо отобразить
модифицированный текст
Нехватка памяти
Комбинированное окошко теряет
фокус
Изменился выбранный элемент
Комбинированное окошко получает
фокус
Сообщения, управляющие редактированием
EM_EMPTYUNDOBUFFER
EM_SETPASSWORDCHAR
EM_SETTABSTOPS
ЕМ MAXTEXT
Выключает возможность возврата к
предыдущему состоянию
Изменяет символ пароля
Устанавливает позиции табуляции
при многострочном редактировании
Объявляет, что больше нельзя
вводить символы
Сообщения окошку списка
LB FINDSTRING
mcffimoemsTEALEXEENT
LB GETITEMDATA
Найти первую подходящую строку в
списке
Выдать ширину горизонтального
скроллирования списка
Выдать 32-разрядное значение,
ассоциированное с элементом списка
Новые сообщения Windows 3.0
127
LB_GETITEMRECT
LB_GETSELCOUNT
LB_GETSELITEMS
LB_GETOPENDSTDEX
LB_SETCOLUMNWmTH
LB_SEraOREZONTALEXTENT
LB_SETITEMDATA
LB_SETTABSTOPS
LB_SETOPENDSTDEX
LB_KILLFOCU S
LB_SETFOCUS
WM_CHARTOITEM
WM_COMPACTIN G
WM_COMPAREITEM
WM_DELETEITEM
WM_DRAWrTEM
WM GETFONT
Выдать координаты прямоугольника,
содержащего заданный элемент
окна списка
Выдать число выделенных
элементов списка
Выдать номера выделенных
элементов
Выдать номер первого видимого
элемента списка
В окошке, состоящем из нескольких
колонок, задать ширину колонок
в пикселах
Установить ширину горизонтального
скроллирования списка
Ассоциировать с элементом списка
32-разрядное значение
Установить позиции табуляции
в списке
Установить номер первого видимого
элемента
Посылается, когда окно теряет фокус
Посылается, когда окно получает
фокус
Посылается окошком списка
дочернему окну при получении
сообщения WM_CHAR
Посылается всем окнам первого
уровня, когда Windows обнаруживает,
что тратит более 12.5%
процессорного времени на упаковку
памяти
Определить относительную позицию
элемента в упорядоченном списке
Информирует окно, владеющее
окошком списка или
комбинированным окошком,
что уничтожен элемент
Сообщает окну о необходимости
перерисовать управляющий элемент
Получить от управляющего
элемента шрифт, чтобы использовать
его далее для изображения текста
128
Разработка программ для Windows 3. 0
WM_ICONERASEBKGD
WM_MDIACTIV АТЕ
WM_MDICASCADE
WM_MDICREATE
WM_MDIDESTROY
WM_MDIGETACTrVE
WM_MDHCONARRANGE
WM_MDIMAXIMIZE
WM_MDE4EXT
WM_MDIRESTORE
WM_MDISETMENU
WM_MDITILE
WM_MEASUREITEM
WM_PAINTICON
WM_PALETTECHANGED
WM_PARENTNOTEFY
WM_QUERYDRAGICON
WM_QUERYNEWPALETTE
WM_SETFONT
WM SPOOLERSTATUS
Посылается ужатому окну, когда
требуется заполнение фона перед
перерисовкой пиктограммы
Посылается клиенту MDI, чтобы он
активировал новое дочернее окно
Расположить дочерние окна фрейма
MDI в форме ”каскада”
Посылается основному окну MDI
для создания нового дочернего окна
Посылается основному окну MDI
для закрытия дочернего окна
Выдать активное в данный момент
дочернее окно
Посылается клиенту MDt чтобы
он расставил дочерние окна
Посылается клиенту MDI для
максимизации дочернего окна
Активирует следующее подокно MDI
Восстанавливает размер
максимизированного или
минимизированного подокна
Заменяет меню в основном окне MDI
Располагает подокна MDI
в виде паркета
Требует заполнить структуру данных
MEASUREITEM
Требует перерисовать
минимизированное окно
Сообщает всем окнам, что текущее
окно изменило палитру
Сообщает родительскому окну о
существенных изменениях статуса
Сообщает минимизированному
окну, не имеющему пиктограммы
класса, что *ero передвигают
Сообщает окну, что оно скоро
получит фокус ввода
Определяет шрифт, который следует
использовать в окнах диалога
Посылается менеджером печати при
добавлении или удалении элементов
из очереди
Новые структуры данных Windows 3.0
129
НОВЫЕ СТРУКТУРЫ ДАННЫХ WINDOWS 3.0
BrTMAPCOREHEADER
BrTMAPCORENFO
BrTMAPFBLEHEADER
BrrMAPDNTFO
BrTMAPE4FOHEADER
CLIENTCREATESTRU CT
OMPAREITEMSTRUCT
DELETETTEMSTRUCT
DEVMODE
LOGPALETTE
MDICREATESTRUCT
MEASUREITEMSTRU CT
PALETTEENTRY
RGBQUAD
RGBTRIPLE
Содержит сведения о размерах
и цветах аппаратно-независимой
битовой карты
Полное определение размеров
и цветов аппаратно-независимой
битовой карты
Содержит тип, размер
и расположение аппаратно¬
независимой битовой карты
Полностью определяет размеры
и цвет аппаратно-независимой
битовой карты в Windows 3.0
Содержит информацию о размерах
и формате аппаратно-независимой
битовой карты в Windows 3.0
Содержит информацию о меню и
первом подокне ШЛчжна
Содержит информацию о сравнении
двух записей в нестандартном
списке
Описывает удаляемый элемент
нестандартного списка
Содержит информацию об
инициализации устройства и
конфигурации драйвера принтера
Определяет логическую палитру
Содержит класс, титул, хозяина,
расположение и размер подокна MDI
Содержит размеры нестандартного
управляющего подокна
Задает цвет и способ использования
элемента логической цветовой
палитры
4-байтовая структура, описывающая
интенсивность цветов RGB
3-байтовая структура, описывающая
интенсивность цветов RGB
£—857
Глава 5
■ ВВЕДЕНИЕ В СИСТЕМУ
ПРОГРАММИРОВАНИЯ АКТОР
■ ЯЗЫК АКТОР, ВЕРСИЯ 3.0
В качестве введения в объектноюриентированное программирова¬
ние (ООП) для Windows хорошо познакомиться с языком Актор
группы Whitewater. Актор не только работает под управлением
MS-Windows, он был первым объектно-ориентированным инстру¬
ментом, разработанным специально для написания приложений,
работающих в среде Windows. Использование языка Актор —
самый простой путь к изучению ООП в Windows, в этом случае
вам не потребуется собственного SDK. В этой главе вначале опи¬
сывается среда программирования Актор, затем его синтаксис и,
наконец, после этого даются примеры программирования на Ак¬
торе в Windows и использования различных инструментов, помо¬
гающих в этом.
■ СРЕДА ПРОГРАММИРОВАНИЯ АКТОР
Среда программирования Актор состоит из экрана дисплея, рабо¬
чего окна и некоторых факультативных возможностей, использу¬
емых в той или иной ситуации. Важнейшими среди них являются
Броузер (визуализатор), инспектор, отладчик и редактор файлов.
Актор использует возможности Windows для того, чтобы реализо¬
вывать объекты, обеспечивающие интерактивную оконную среду,
похожую на среду Смолтока. Как и во многих программах MS-
Windows, при использовании Актора вам понадобится "мышь”.
Главными составляющими ’’рабочего стола” Актора являются
рабочие окна, визуализаторы и инспекторы, смоделированные в
духе Смолтока. Когда система начинает первый сеанс, она выдает
рабочее окно с двумя рядами опций команд, расположенных вдоль
верхней полоски рабочего окна Q>uc. 5.1). В число их входят
Броузер Актора
131
Рис. 5.1. Рабочее окно Актора
следующие команды: File, Edit!, Doit!, Browse!, bispect!, Show,
Room! и Templates.
Область редактирования в рабочем окне ведет себя как интерпре¬
татор. Если введете некоторое выражение и нажмете Enter, Актор
попытается скомпилировать и выполнить его. Если некоторый код
(программный текст) уже имеется в окне редактирования, то его
фрагмент можно подсветить, после чего нажать Doit! (Выполни!),
в результате отмеченный фрагмент будет скомпилирован и выпол¬
нен. Опции Browse! и bispect! создают новый экземпляр этих
инструментов и открывают на ’’рабочем столе” Актора окно в стиле
рорир-окон (окон, появляющихся на месте уже имеющихся).
■ БРОУЗЕР АКТОРА
Броузер ipuc. 5.2) выполняет ту же функцию, что и инспектор, но
открывает интерактивное окно не на каждый единичный объект,
а сразу на всю систему классов, загруженных в данный момент.
Окно прокручивания списка (окно скроллинга) содержит список
всех классов иерархии классов Актора. Правое списковое окно
содержит методы текущего класса. Выбрав класс, а потом какой-
132
Введение в систему программирования Актор
Рис. 5.2. Окно Броузера классов в Акторе
нибудь из его методов, вы можете получить доступ к коду в
нижнем окне и редактировать его.
Выбор нужной опции в строке-меню Броузера позволит вам ука¬
зать, как просматривать классы — в соответствии с иерархией или
с алфавитным порядком. Еще более простой способ найти класс —
использовать опцию Goto Class в меню Options. Кроме того, в этом
меню располагаются средства для определения переменных класса
и программ его инициализации. К вопросу о переменных класса
мы вернемся несколько позже.
Отдельное меню Броузера предназначено для поиска и замены
операций в текстовом окошке (pane). Для удобства при помощи
опции Templates можно выбрать различные шаблоны, которые
вставляются в окошко редактирования — это ускоряет процесс
разработки и ограничивает возможность появления опечатки или
синтаксической ошибки.
Меню Utility Броузера содержит несколько мощных возможно¬
стей:
bnplementors, Senders, Window Routine Senders,
Global References и References.
Инспекторы
133
Опция Implementors (реализаторы) находит целиком среду разра¬
ботки для всех классов, которые реализуют методы с данным
именем. Опция Senders (отправители) делает примерно то же
самое, но она строит список методов, которые используют данный
метод в посылаемых сообщениях. Опция Window Routine Senders
(отправители оконной программы) особенно интересна. С ее по¬
мощью можно найти все методы в среде разработки, которые
вызывают некоторую функцию Windows. Global References (гло¬
бальные ссылки) строит список всех методов, которые ссылаются
на выбранную глобальную переменную. И наконец, References
возвращает список всех предварительно связанных ссылок на
некоторый метод. Предварительное связывание — это специаль¬
ный механизм Актора, его можно использовать для написания
более эффективного кода, когда вы уверены в том, каков будет тип
аргументов при обращении к данному методу.
Для любого класса Броузер показывает только те методы, которые
реализованы именно для этого класса. Наследуемые методы не
попадают в список, который просматривается через окошко G?ane),
однако наследуемые переменные попадают в список окошка пере¬
менных. Броузер — один из важнейших инструментов ООП.
Однажды попробовав поработать с ним, вы никогда не станете
заниматься ООП без аналогичного средства. Отметим, что Броузер
Актора в отличие от других, где можно работать как с мышью,
так и без нее, ориентирован исключительно на использование
мыши.
■ ИНСПЕКТОРЫ
Инспектор — это рорир-окно, которое можно использовать в любой
момент времени для просмотра содержимого каждого из объектов
как экземпляров, так и классов. Пользоваться им очень просто.
Если вы хотите просмотреть некоторый экземпляр объекта, сна¬
чала подсветите его имя в рабочем окне и затем обратитесь к опции
bispect! из окна Актора в среде разработки. Если объект является
экземпляром одного из классов CoUection, то в верхнем правом
окошке вы найдете список номеров (индексов), соответствующих
объектам в коллекции. Указав на любой из них, вы выберите
объект, и его имя будет высвечено в главном окне.
Инспекторы используются тогда, когда требуется сконцентриро¬
ваться на одном частном объекте. Вы обращаетесь к ним тогда,
когда нужно изучать содержимое объектов в деталях или модифи¬
цировать их. Верхнее левое окошко окна инспектора содержит
список окон, в которых выдаются все экземплярные переменные
некоторого объекта. Если указать мышью на какую-либо перемен¬
ную в скроллируемом списке, то в нижнем окошке инспектора,
134
Вввдвнив в систему программирования Актор
его окне редактирования, выдается ее значение. При использова¬
нии инспектора доступны переменные как класса, так и экземп¬
ляров.
■ КЛАССЫ АКТОРА
Актор оснащен удивительно богатой библиотекой классов, гото¬
вых программ, которые можно использовать при разработке соб¬
ственных приложений. Эти библиотеки являются мостиком,
связывающим изучение и использование системы. Здесь приво¬
дится неполный список классов, которые используются для струк¬
тур данных и графических возможностей, связанных с ними.
Object
Collection
IndexedCollection
Аггау
Function
Ordered-Collection
Sorted-Collection
Text-Collection
Byte-Collection
String
Symbol
Struct
DosStruct
Graphics-Object
WinPolygon
Rect
WinEllipse
RndRect
Proc
Interval
CharInterval
KeyedCollection
Dictionary
Method-Dictionary
Identity-Dictionary
System-классы — это особые конструкции Актора. Только для
методов этих классов разрешается посылать сообщения, не имея
”приемников” этих сообщений.
■ КЛАССЫ MS-WINDOWS
Одной из наиболее привлекательных особенностей системы Актор
является то, что она предоставляет встроенные классы для облег¬
Классы MS-Windows
135
чения использования пользовательского интерфейса MS-Windows.
Таких классов три: Window, Control и Dialog. Сначала рассмотрим
ветвь иерархии, которая содержит все классы Windows. Приведем
схему этой ветви.
Object
Windows-Object
Control
Button
Edit
ListBox
ClassList
MethodList
VarList
ScrollBar
Dialog
ClVarDialog
ClassDialog
DebugDialog
DialogDesign
DirtyCLD
FileDialog
InputDialog
ReplaceDialog
PrintDialog
SealDialog
Window
AboutWindow
TextWindow
EditWindow
WorkEdit
BrowEdit
DBBrowEdit
DebugEdit
FileWindow
PrintFileWindow
Workspace
ToolWindow
Browser
DebugWindow
Inspector
Для упрощения мы выделим три главных ветви: Window, Control
и Dialog. Может показаться, что класс Window гораздо больше
других, но это лишь формальный или абстрактный класс. Это
означает, что он реализует методы, которые будут использоваться
136
Введение в систему программирования Актор
подклассами, каждый из которых реализует специальные окна, в
действительности существующие и используемые. В частности,
Window реализует программы для взаимодействия с MS-Windows.
Для того чтобы понимать, как работает Актор, надо помнить о
разнице между сообщениями Windows и Актора. Windows не
понимает никаких сообщений уровня Актора, просто игнорирует
их существование. Актор не понимает большую часть сообщений
Windows, поскольку для него нет необходимости отвечать на них.
Однако, если необходимо, можно сделать так, что Актор будет
отвечать на любое сообщение Windows (любым желаемым спосо¬
бом). Все, что необходимо сделать для этого, это определить метод
некоторого класса в Акторе с таким же именем, как и сообщение
Windows, и в определении метода описать, как объект Актора,
принимающий сообщение, будет отвечать на него.
Всегда, когда в Акторе создается оконный объект, в Windows
посылается сообщение СаИ CreateWindow с общим числом пара¬
метров не менее одиннадцати. Когда в такой ситуации создается
оконный объект, Windows посылает дескриптор (handle) или имя
созданного окна и Актор должен заполнить этот дескриптор. В
терминах ООП на Акторе можно сказать, в классе Window и у его
потомков имеется метод create, результатом работы которого яв¬
ляется посылка MS-Windows сообщения CreateWindow. Когда
окно Актора закрывается, оконный объект в действительности
по-прежнему будет существовать до тех пор, пока каким-либо
явным dnoco6oM не будет удален из системы. Дескриптор оконного
меню запоминается в экземплярной переменной объекта hMenu.
Метод loadMenu занимается загрузкой ресурсов меню и задает
значение hMenu.
Один из простейших потомков Window — класс TextWindow. Он
предоставляет вам некоторые реальные услуги. Этот класс позво¬
ляет создавать tiled (паркетные) окна. Они могут печатать текст и
делают это при помощи методов printString и printChar, которые
вызывают в MS-Windows функцию Textout GDI (Graphics Display
biterface). Часто вам требуется окно, которое может не только
показывать текст, но и передвигаться по нему и редактировать его.
Такие средства редактирования предоставляет программа класса
EditWindow, являющегося подклассом TextWindow. Класс
WorkEdit — еще один шаг вперед: он позволяет создавать окна, в
которых можно не только редактировать, но и вводить и выпол¬
нять операторы языка Актор. Три подкласса WorkEdit представ¬
ляют окна традиционного назначения: броузеры, броузеры файлов
и рабочие окна общего назначения. Их главное отличие от тради¬
ционных в том, что они, так же как и их предок TextWindow,
являются паркетными окнами. Чаще всего в Акторе используются
окна из другой ветви дерева.
Элементы управлений
137
Окна, которые вы получаете из PopupWindow, напоминают слои,
накладываемые друг на друга. Но в отличие от паркетных окон
их нельзя распахнуть или сжать в пиктограмму. Это обусловлено
MS-Windows, рорир-окна требуют наличия родительского тексто¬
вого окна. Когда родительское окно сжимается в пиктограмму,
рорир-окна, ассоциированные с ним, временно становятся невиди¬
мыми.
■ ЭЛЕМЕНТЫ УПРАВЛЕНИЯ
Как и в MS-Windows, в Акторе элементы управления (Controls) —
это специальный вид окон, который используется для программ
ввода и вывода в пользовательском интерфейсе. В качестве при¬
меров элементов управления можно привести кнопку, список
окошек, полосу скроллирования. Ветвь дерева классов, связанная
с элементами управления, выглядит следующим образом:
Object
Control
Button
Edit
ListBox
ClassList
MethodList
VarList
Scrollbar
Подобно классу Window класс Актора Control тоже формальный,
его подклассы — это подклассы, которые реализуются в приложе¬
ниях. С элементами управления в Акторе работают практически
так же, как и в Windows. Новые экземпляры создаются при
помощи посылки сообщения new, а показываются они при посыл¬
ке сообщения show.
Класс ListBox является подклассом Control, который создает ма¬
ленькое рорирчжно, ассоциированное с родительским окном, и
выдает список айтемов (item — элементов некоторого перечисле¬
ния). Обычно списковое окно используется, чтобы представить
айтемы, которые пользователь может выбирать при помощи мы¬
ши. Класс Scrollbar используется не для всех полосок скроллиро¬
вания, как можно было бы предположить. Стиль определений для
окон обычно обеспечивает это автоматически. Этот класс исполь¬
зуется только тогда, когда расположение полосок скроллирования
должно отличаться от стандартного, которое обычно обеспечива¬
ется автоматически. Таким образом, используя этот класс, вы
сможете создать набор горизонтальных и вертикальных полосок
скроллирования, которые находятся в части экрана, отдельной от
138
Введение в систему программирования Актор
родительского окна, которым они управляют. Аналогично, класс
Button предназначен для создания ”кнопок” в манере, отличной
от обьгчной. Кнопками оснащают встроенные диалоговые окошки
(boxes), но, возможно, вы хотите, чтобы при помощи кнопок
выполнялось и еще кое-что. Вы можете, например, пожелать
разместить их в обычных окнах.
Теперь переходим к классу Dialog и его подклассам:
Object
Dialog
ClVarDialog
ClassDialog
DebugDialog
DialogDesign
DirtyCLD
FileDialog
biputDialog
ReplaceDialog
PrintDialog
SealDialog
Диалоговые окна похожи на рорир-окна тем, что они накладыва¬
ются одно поверх другого, и тем, что им необходимо иметь
родительское окно, с которым они связываются. Так же как
Window и Control, Dialog, почзуществу, это абстрактный класс,
который предоставляет программы, предназначенные для исполь¬
зования в его потомках. Класс FileDialog в Акторе используется
для создания диалоговых окон, которые обычно появляются в
MS-Windows, когда вы загружаете файл, используя pull-down-ме-
ню (ниспадающее меню). Класс для диалоговых объектов
ClassDialog используется тогда, когда при помощи броузера редак¬
тируется или создается какой-нибудь класс.
■ ДИНАМИЧЕСКИЕ МЕНЮ И ДИАЛОГИ
Еще одно преимущество Актора проявляется в том, что он помимо
варианта со статщческой компиляцией ресурсов предоставляет
некоторый альтернативный вариант создания меню и диалогов.
Хотя статические меню и диалоги более эффективны, динамиче¬
ские ресурсы в силу того, что создаются динамически, и проще в
использовании. Для создания динамических меню используется
класс Menu, который не был представлен в иерархии классов, но
который легко загрузить при помощи оператора:
load ("classes\menu.cls");
Списковые структуры
139
С другой стороны, если вы не хотите набирать такой длинный
текст, вы можете перейти в меню File окна Актора Display и
выполнить Load, а затем выбрать menu.cls из директория классов.
■ CALLBACKS
Нередко вам требуется повторить некоторое действие над объекта¬
ми определенного ряда: над всеми окнами определенного типа,
всеми экземплярами определенного класса и т.п. В Акторе для
этой цели небходимо послать в Windows адрес функции, при
помощи которой вы собираетесь выполнять итерации с таким
расчетом, что Windows сможет, обращаясь к ней последовательно,
получить все оконные объекты. Для упрощения этого процесса
предоставляется класс CaUback, который дает все виды конструк¬
ций, опирающихся на са11ЬасЫ£ункции, — функции, последова¬
тельно реализующие серии вызовов. В табл. 5.1 приведен список
перечисляющих функций Windows, которые можно использовать
таким образом.
Таблица 5.1. Перечисляющие функции Windows
EnumChilWindows
EnumClipboardFormats
EnumFonts
EnmnMetaFile
EnumObjects
EnumProps
EnumTaskWindows
EnumWindows
Для того чтобы использовать в Акторе класс Callback, предвари¬
тельно надо написать:
Def enumWindows(self cb, set)
{ set := new(Set, 4);
сЬ := create(callback, #(0, 1), {using(hnd,IVal);
add(set,hnd); 1});
Call EnumWindows(lock(cd) , lL);
free(cd);
^set;
■ СПИСКОВЫЕ СТРУКТУРЫ
Для задач искусственного интеллекта и во многих других прило¬
жениях важны механизмы для обработки связанных списков. Как
сделать это в Акторе? В соответствии с одним вариантом можно
было бы работать с классом OrderedCollection, дополнив его под¬
классы необходимыми методами для обработки списков. Однако
листинг одной из демонстрационных программ, которая поставле¬
на вместе с предыдущей версией Актора, предлагает другой под¬
ход. Новый класс ListNode определяется как подкласс класса
Collection. Для этого нового класса определяются методы append,
140
Введение в систему программирования Актор
do, isAtom, printOn и rPrintOn. Для Object как корневого класса
также определяются новые методы. isAtom, rPrintOn и cons. Если
вы посмотрите в текст метода cons, то увидите, что это программа
для посылки сообщения new классу ListNode для создания его
нового экземпляра. Очевидно, это всего лишь элементарный пере¬
ходник, обеспечивающий сервис для начинающих пользоваться
классом для обработки списков.
Структуры, используемые классом OrderedCollection, существенно
отличаются от динамически модифицируемых списков. В терми¬
нологии ООП упорядоченная коллекция — это коллекция фикси¬
рованной длины, она никогда не увеличивается. Это означает, что,
когда вы создаете некоторую упорядоченную коллекцию
(OrderedCollection), то должны ориентироваться на некоторое мак¬
симальное число элементов. Если в частично заполненной коллек¬
ции максимум еще не достигнут, то можно добавлять элементы
как в начало, так и в конец списка. Когда максимум достигнут, а
вам требуется еще, необходимо послать в коллекцию сообщение
grow, при этом будет создан новый массив необходимого размера,
а элементы старого — скопированы в него.
■ ПРОГРАММИРОВАНИЕ НА АКТОРЕ
Из сказанного можно заключить, что язык Актор — это некоторый
гибрид. Он очень похож на Смолток, но его синтаксис сильно
тяготеет к Си. С точки зрения программистов, Актор является
хорошим воссозданием среды типа Смолтока, но без его специфи¬
ческого синтаксиса. Программисты с опытом работы на Си навер¬
няка одобрят синтаксис Актора, и я думаю, большинство
согласится, что этот синтаксис делает объектно-ориентированную
программу более понятной. Каковы же его основные черты? Я
предполагаю, что вы зададите такой вопрос, и поэтому перехожу
к обсуждению именно этой темы.
■ ОСНОВЫ СИНТАКСИСА АКТОРА
Название ”Актор” начинается со слова ”акт” — действие. Имена
метода или процедуры, ”глагол” в программном тексте в сообще¬
нии Актора стоят на первом месте. Сначала имя действия, затем
скобки и список элементов внутри них. Это общий формат для всех
операторов Актора. Самый первый элемент списка после открыва¬
ющей скобки — это всегда имя объекта, которому посылается
сообщение. Так, в некотором гипотетическом сообщении
verify(program77, rapidModel);
сообщение veryfi посылается некоторому объекту, называемому
program77. ’’rapidModel” — это аргумент, который уточняет на¬
значение сообщения. В конце любого оператора Актора, передаю¬
Основы синтаксиса Актора
141
щего сообщение, если хотите ’’терминатор” или ’’завершитель”,
точка с запятой, так же как в Си. Кроме того, с Си сближает и
использование фигурных скобок ”{}”, которые заключают в себе
блок программы.
Важно иметь в виду, что Актор чувствителен к тому, какими
буквами — прописными или строчными — вводится текст. Он не
распознает термин, если все прописные и строчные буквы не будут
написаны так, как надо. Одна маленькая ошибка, и Актор начи¬
нает жаловаться на то, что не понимает вас.
Так же как и в языках Си и Паскаль, в Акторе аргументы,
заключенные в скобки следуют сразу за именем метода или
селектором, и подобно Паскалю и Смолтоку присваивание пере¬
менной задается операцией ”?=”. Аналогично, Смолтоку, для того
чтобы создать новый экземпляр, вы обычно посылаете сообщение
’’new” в некоторый класс и присваиваете этот экземпляр некото¬
рому имени. Так, например, вы можете создать экземпляр из
класса Turtle (’’черепашка” в стиле Лого), указав:
Barney:=new(Turtle) ;
В общем случае посылка сообщения в Акторе подобна передаче
аргументов. Теперь черепашка будет разбирать посланные нами
сообщения. Как и любая нормальная черепашка, Barney знает, что
сообщение ”г” означает ’’повернись направо”, ”1” — ’’налево, ”f” —
двигайся вперед, ”b” — назад. Она также знает, что ”down” —
означает ’’опусти хвост” ОДО? того, чтобы рисовать), а ”up” —
’’подними хвост”. Так, если бы мы хотели, чтобы черепашка
Barney совершила прогулку и нарисовала квадрат, мы должны
были бы сообщить ей следующее:
down(Barney);
f(Barney, 10);
r(Barney, 90);
f(Barney, 10);
l(Barney, 90);
b(Barney, 10);
r (Barney, 90);
b(Barney, 10);
up (Barney);
Для определения методов в Акторе используется ключевое слово
Def. Так, если мы хотим обучить не только Barney, но и всех
других черепашек новому сообщению walkSquare (прогуляйся по
квадрату), мы можем воспользоваться таким методом:
Def walkSquare(self, size)
{ down(self);
f (self, size);
r(self, 90);
f (self, size);
1 (self, 90);
b(self, size);
142
Введение в систему программирования Актор
r(self, 90);
f(self, size);
up(self);
};
Теперь, для того чтобы попросить Barney или другую черепашку
произвести подобный маневр, все,что от нас требуется, это указать:
walkSquare(Barney, 10);
Возможно, вы заметили, что движение этой черепашки по квадрату
происходит в одном направлении — по часовой стрелке. Иногда,
правда довольно редко, требуется другой вид черепашек, который
двигается по квадрату против часовой стрелки. К счастью, объек¬
тно-ориентированные системы типа Актора позволяют отразить и
такую причуду естественной истории. Как мы можем наделить
черепашку этой новой особенностью? Нам следует определить
класс CounterTurtle (контр-черепашка) и в нем метод walkSquare
для черепашек этого вида, метод, который задает вариант стан¬
дартной прогулки по квадрату против часовой стрелки. Это заодно
дает нам возможность проиллюстрировать определение новых
классов в Акторе.
Обычно в Акторе новый класс создается на основе какого-нибудь
уже имеющегося класса, доступного вам в броузере, поэтому я с
этого и начну. Прежде всего вы находите в списке классов в
верхнем левом окне класс Turtle. Затем переходите в ниспадающее
меню Options и выбираете в нем опцию ’’MakeDescendant” (создать
потомка). В результате откроется рорир-окно, которое будет ис¬
пользоваться как шаблон для создания нового класса. Теперь
вводите имя нового класса — CounterTurtle, нажимаете на клави¬
шу Accept, и система создает новый класс, а его имя добавляется
к списку классов и становится частью иерархии классов Актора.
Второй способ создать новый класс — это написать его программу
непосредственно, заново, броузер при этом отодвигается на второй
план. Для этих целей используется инструкция ’*inherit”. Так, мы
можем написать:
inherit(Turtle, #CounterTurtle)
Все версии Актора, начиная с версии 2.0 и далее, поддерживают
ведение переменных класса. Переменные класса — это такие
переменные, которые определяются для класса и могут хранить
некоторое значение даже тогда, когда нет ни одного экземпляра
класса. В действительности переменная класса принадлежит клас¬
су и, в общем, не зависит от его экземпляров. Один из возможных
способов использования переменной класса — это запоминание
списка всех его экземпляров. Другой важный вариант использо¬
вания — хранение любой информации, запоминание которой
требуется для экземпляров данного класса.
Тренировки в языке Актор
143
■ НЕМНОГО МУЗЫКИ?
Если вы не прочь немного послушать Баха, то выполнение сооб¬
щений включит воспроизведение:
А := defaultNew(AboutWindow, "New");
bach(А);
■ ПРОГРАММНЫЕ БЛОКИ
Использование блоков в Акторе — это некоторая вариация блоч¬
ной структуры Смолтока. Блок — это ’’кусочек” программы,
которому не присвоено никакого имени. Это способ, при помощи
которого программа становится чуть более модульной. Каждый
блок сам по себе — объект, являющийся экземпляром класса
BlockContext. Единственный метод этого класса, который вам в
действительности нужен, это метод eval (выполнить). Что он
делает? Вы угадали. Он выполняет блоки. Каждый раз, когда
какой-либо блок получает сообщение eval, он выполняется. Блоки
существенно упрощают специализацию алгоритмов. Всегда, когда
имеется набор алгоритмов, решающих примерно одну и ту же
задачу, вы можете выделить различия в них и определить их как
блоки. Пользуясь этим способом, единственное, что надо сде¬
лать,— это выполнить переключение с одного блока на другой.
■ ФОРМАТЫ ФАЙЛОВ
Исходная программа на Акторе может храниться в файлах одного
из двух типов. Бывают файлы с расширениями .ACT и .CLS.
Последний из них предназначен для определений классов и имеет
более сложную структуру. Файлы ACT в основном используются
для хранения инструкций Актора и программ, базирующихся на
существующих классах. Актор похож на Смолток в отношении
использования файлов-образов, сохраняющих полное состоянйё
среды разработки, так, что при последующем входе в систему
восстанавливается в точности то состояние среды, которое было на
момент последнего запоминания образа. Запоминание образа —
это ’’мгновенная фотография”, которую можно сделать в любой
момент в течение сеанса с Актором.
■ ТРЕНИРОВКИ В ЯЗЫКЕ АКТОР
Специально для тех, кто не знаком с Актором, я разработал серию
упражнений, которые быстро, в интерактивном сеансе познакомят
вас с наиболее важными аспектами языка. Те, кто знаком с Си и
Смолтоком, легко увидят, что находятся в привычном окружении.
Прежде всего, есть ряд моментов, которые вам следует знать о
функционировании интерактивной среды Актора. Вы можете на¬
144
Введение в систему программирования Актор
печатать (ввести) имя любого объекта Актора и точку с запятой
после него, в ответ на это Актор выдаст общее описание объекта.
Для получения более детальной информации надо обратиться к
инспектору. Так, например:
ThePort;
а WorkSpace
OutPorts;
Set( а WorkWindow )
■ СЕАНС С КЛАССОМ NUMBER
Как и в большинстве объектно-ориентированных языков, выпол¬
нение арифметических действий связано с посылкой сообщений к
объектам класса Number, в которых указывается, какие операции
следует над ними произвести. Актор использует инфиксную форму
записи, примеры которой приводятся ниже:
2 + 2;
4
4 * 5;
20
7 * (4 + (3 * 2) ) ;
70
sqrt(144);
12
exp(2);
7.389056099
4**3;
64
random(666);
47
Следующее определение метода для возведения числа в квадрат
для класса Number некорректно. Почему?
Def square(self)
{ self * self;
}
А вот корректное определение:
Def square(self | ans)
{
ans := self * self;
^ans
}
Теперь метод square можно использовать для определения других
новых методов, например для вычисления суммы квадратов:
Def sumsquare(self, other ans) {
ans := square(self) + square(other);
^ans
}
Циклы
145
sumsquare(3, 4)
25
■ СТРОКИ
Bs := fillWith("b", 22);
"bbbbbbbbbbbbbbbbbbbbbb”
insetr(Bs, "x", 10);
"bbbbbbbbbbxbbbbbbbbbbbb"
hash (Bs);
3004
■ СТРУКТУРЫ УПРАВЛЕНИЯ
Актор предоставляет полный набор структур управления, тради¬
ционный для современных языков программирования. Имеются
условные и итеративные циклы, перечисление, конструкции вы¬
бора (case) и рекурсия. Шаблоны, которые выдаются в меню
Templates, помогут вам, если вы забыли синтаксис одной из
управляющих структур. Большинство программистов отлично
знакомы с конструкцией итеративного цикла. Следующий раздел
продемонстрирует их использование в Акторе.
■ ЦИКЛЫ
i : = 0 ;
loop while i 10
print (i);
i := i + 1;
endLoop;
As := fillWith("a", 22);
"aaaaaaaaaaaaaaaaaaaaaa"
i := 0;
loop while i 10
As := insert(As, "x", i);
i := i + 1;
endLoop;
^As; "xxxxxxxxxxaaaaaaaaaaaaaaaaaaaaaa"
delete(As, 0, 10);
"aaaaaaaaaaaaa"
i : = 0 ;
loop while i 22
As := insert(As, " ", i);
i := i + 1;
endLoop;
^As;
" aaaaaaaaaaaaaaaaaaaaaa"
leadingBlanks(As);
22
leftJustify(As);
"aaaaaaaaaaaaaaaaaaaaaa"
146
Введение в систему программирования Актор
print(As);
aaaaaaaaaaaaaaaaaaaaaa"
delete(As, 0, 10);
"aaaaaaaaaaaaa"
i : = 1;
loop while i 45
As := insert(As, "x", i);
i : = i + 2 ;
endLoop;
^As;
"axaxaxaxaxaxaxaxaxaxaxaxaxaxaxaxaxaxaxaxaxax"
i := 2;
loop while i 67
As := insert(As, "e", i);
i : = i + 3 ;
endLoop;
^As;
"axeaxeaxeaxeaxeaxeaxeaxeaxeaxeaxeaxeaxeaxeaxeaxeaxeaxeaxe
axeaxeaxe"
i := 3;
loop while i 89
As := insert(As, " ", i);
i := i + 4;
endLoop;
^As;
"axe axe axe axe axe axe axe axe axe axe axe axe axe
axe axe axe axe axe axe axe axe axe "
words(As);
OrderedCollection("axe" "axe" "axe" "axe" "axe" "axe"
"axe" "axe" "axe" "axe" "axe" "axe" "axe" "axe" "axe"
"axe" "axe" "axe" "axe" "axe" "axe" "axe")
■ КОЛЛЕКЦИИ
В большинстве объектно-ориентированных систем классы
Collection и Container представляют собой ядро набора встроенных,
готовых структур данных — ’’снимай с полки и пользуйся”. Актор
не является исключением. Ниже приводится ветвь иерархии клас¬
сов Актора, связанная с объектами класса CoUection.
Object
CoUection
Bag
bidexedCollection
Array
Function
OrderedCoUection
CircularQueue
TextCoUection
SortedCollection
SortedSet
Массивы
147
ByteCollection
String
Symbol
Struct
Interval
Charbiterval
KeyedCollection
Dictionary
Frame
FrameList
Slot
Сам по себе класс Collection — это абстрактный или формальный
класс. Вы ничего не сможете сделать с его экземплярами. Он
служит базой для многих классов-наследников коллекций. Когда
при помсщи ссобщения new создается член одного нз класса-кол-
лекции, вы должны указать аргумент сообщающий максимальное
количество элементов в данной коллекции. Однако метод size
вернет вам только то количество элементов коллекции, которое в
действительности включено в нее, а совсем не максимальный
размер, фигурировавший в сообщении new.
■ МАССИВЫ
Для работы с массивами в Акторе используют класс Array. Для
их создания используют две основные конструкции:
A4 := new(Array, 4);
AR4 := #(1, 2, 3, 4);
Вот так строятся двумерные массивы:
AA :=#(#(l,0), #(1,1), #(0,1), #(0,0));
Теперь построим трехмерны:: массив:
AAA := #(#(#(1,1,1), #(1,1,0), #(l,0,0)), #(#(0,1,1),
#(0,1,0), #(0,0,0)), #(#(1,0,1), #(0,0,1), #(0,0,0)));
Массив в Акторе может быть образован из смеси различных типов
данных:
Mix := #("String", 77, #(а, b, c));
AA2 := copyFrom(AA, 0, 3);
Кроме того, вы можете собрать массив из массивов, предваритель¬
но создав массив и затем отправляя сообщение put для вставки
одного массива в другой, так как это делается в следующей
программе:
A1 := #(1, 2, 3, 4);
A2 := #(5, б, 7, 8);
АЗ := #(9, 10, 11, 12);
A4 := #(13, 14, 15, 16);
148
Введение в систему программирования Актор
AAAA := new(Array, 4);
put(AAAA, A1, 0);
put(AAAA, A2, 1);
put(AAAA, АЗ, 2);
put(AAAA, A4, 3);
Для проверки, содержит ли массив в действительности все, что
туда было помещено, вы можете использовать сообщение print,
чтобы выдать содержимое этого массива. Вот как отправляется
это сообщение и вот что возвращается в ответ:
print(AAAA);
Array (Array ( 1 2 3 4 ) Array(5 6 7 8) Array(9 10 11 12)
■ ДРУГИЕКОЛЛЕКЦИИ
У потомков класса Array добавляются свойства, которые не при¬
сущи простым массивам. Так, объекты OrderedCollection (упоря¬
доченная коллекция) имеют две экземплярные переменные
firstElement и lastElement (первый и последний элемент). Объекты
класса SortedCoUection (отсортированная коллекция) наследуют
две эти переменные и имеют еще одну compareBlock. Значение
compareBlock определяет то, как будут упорядочены объекты в
SortedCollection. Здесь приводится простой интерактивный сеанс
работы с SortedCollection:
S := new(SortedCollection, 7);
add(S, "f");
add(S, ''d") ;
add(S, "y");
add(S, "a");
print(S);
SortedCollection("а", "d", "f", "у".
add(S, "k");
add(S, "c");
add(S, "x");
print(S);
SortedCollection("а", "с", "d", "f", "k", ”х", "у")
size (S)*;
7
add(S, "q");
size (S);
8
"а" in S;
1
"r" in S;
nil
findItemIndex(S, "X");
Array(l 6)
Словари
149
remove(S, "x");
"x"
size (S);
7
Отметим один важный момент. Coo6njeHnesetCompareBlock позво¬
ляет нам переупорядочить SortedCollection, используя новый кри¬
терий для сравнения двух элементов этой коллекции. Я забежал
немного вперед для того, чтобы подчеркнуть, какими мощными
возможностями уже обладают эти классы. Теперь вернемся назад
и рассмотрим, как отсортированные коллекции (SortedCollection)
наследуют некоторые из своих возможностей.
■ УПОРЯДОЧЕННЫЕ КОЛЛЕКЦИИ
Класс SortedCollection, который я только что представил в преды¬
дущем разделе, является прямым подклассом OrderedCollection.
Это не абстрактные классы. Вы можете использовать их для
создания работающих объектов. Рассмотрим в качестве примера
какой-нибудь объект, который можно построить из класса
OrderedCollection. Ниже следуют две вариации на одну и ту же
тему. В каждом из примеров сначала объект инициализируется.
Затем к нему добавляются члены. И наконец, используется пере¬
числительный цикл, который обрабатывает все члены и выводит
их на экран. Для того чтобы посмотреть эти и другие, созданные
вами объекты коллекции, вы можете использовать Инспектор.
0C := new(OrderedCollection, 9);
add(OC, "One");
add(OC, "Two");
add(OC, "Three");
do (ОС, { using(i) printLine(i) });
Assassins := new(OrderedCollection, 3);
add(Assassins, "Ruby");
add(Assassins, "Ray");
add(Assassins, "Bremer");
do(Assassins, { using(a) printLine(a) });'
■ СЛОВАРИ
Здесь приводится простой пример того, как вы можете использо¬
вать объекты Dictionary:
Capitals := new(Dictionary, 10);
add(Capitals, "USA", "Washington");
add(Capitals, "USSR", "Moscow");
add(Capitals, "Italy", "Rome");
add(Capitals, "France", "Paris");
add(Capitals, "Germany", "Berlin");
add(Capitals, "Japan", "Tokyo");
add(Capitals, "China", "Peking");
add(Capitals, "Sweden", "Stockholm");
add(Capitals, "England", "London");
150
Введение в систему программирования Актор
add(Capitals, "Greece", "Athens");
at (Capitals, "USA");
"Washington"
MethodDictionary хранит пары ’’ключ/значение”, поиск которых
основывается на принципе эквивалентности, а не равенства. Этот
подход по сравнению с Dictionary более эффективный как в смысле
времени, так и пространства, хотя и является менее общим.
Поскольку поиск ключей ведется с учетом эквивалентности, а не
равенства, это ограничивает применимость MethodDictionary кру¬
гом объектов, для которых эквивалентность имеет смысл, для
таких, как Char, bit и Symbol.
■ ТЕКСТОВЫЕ КОЛЛЕКЦИИ
Экземпляры класса TextCollection являются отсортированными
коллекциями строк. Ниже приведен пример, в котором показано,
как можно использовать объекты TextCollection для того, чтобы
собирать и разбирать различные слова и предложения из коллек¬
ции строк.
Тс := new(TextCollection, 12);
add(Tc, "What");
add(Tc, "goes");
add(Tc, "on");
add(Tc, "in");
add(Tc, "your");
add(Tc, "heart");
makeString(Tc);
"What goes on in your heart"
add(Tc, "mind");
makeString(copyFrom(Tc, 0, 6));
"What goes on in your heart"
■ ОЧЕРЕДИ
В комплекте поставки Актора распространяется класс
CircularQueue. Объекты CircularQueue можно использовать там,
где обычно применяются структуры данных с круговыми (цикли¬
ческими) очередями. Следующий пример показывает, как можно
использовать объект CircularQueue для представления повторяю¬
щегося недельного цикла:
WeekDays := new(CircularQueue, 7);
add(WeekDays, "Monday");
add(WeekDays, "Tuesday");
add(WeekDays, "Wednesday");
add(WeekDays, "Thursday");
add(WeekDays, "Friday");
add(WeekDays, "Saturday");
add(WeekDays, "Sunday");
do(WeekDays, {using(i) printLine(i)});
Переменные класса
151
Последнее сообщение — это перечислительный цикл, который
охватывает все сущности, входящие в CircularQueue, и выдает их
на экран. Ниже приводится интерактивный сеанс, в котором
используется объект WeekDays для демонстрации некоторых фун¬
кций и свойств объектов класса CircularQueue:
size(WeekDays) ;
7
next (WeekDays) ;
"Monday"
next (WeekDays) ;
"Tuesday"
next (WeekDays) ;
"Wednesday"
next(WeekDays) ;
"Thursday"
next(WeekDays) ;
"Friday"
next (WeekDays) ;
"Saturday"
next(WeekDays) ;
"Sunday"
WeekDays[0];
"Monday"
WeekDays[6];
"Sunday"
■ ПЕРЕМЕННЫЕ КЛАССА
В системе Актор переменные класса используются весьма редко.
К небольшому числу использующих их классов принадлежат:
ErrorHandler, File и его потомки, ActorAnalyzer, System, VarList,
Dialog и его потомки, AboutWindow, ClockWindow и ToolWindow
и его потомки. Для чего обычно используются переменные класса?
Во-первых, для хранения того, что требуется для всех экземпляров
класса и никогда не изменяется. Во-вторых, для тех значений,
которые могут изменяться, но изменяются одновременно для всех
экземпляров класса. Таким образом, переменные класса — это
совсем не те величины, которые пользователь или программист
может менять, не особенно задумываясь, следуя своей прихоти. По
этой причине в Акторе используют соглашение о том, что их имена
начинаются с символа $, как, например, переменная класса
$Dialogs в классе Dialog.
Один из интересных вариантов использования переменной класса
переменная $Clocks в классе ClockWindow. Это переменная хранит
множество всех экземпляров данного класса. Несколько позже мы
рассмотрим механизм, который используется ClockWindow.
152
Введение в систему программирования Актор
■ ВЫЗОВ БИБЛИОТЕК С ДИНАМИЧЕСКИМ
СВЯЗЫВАНИЕМ
Для доступа к библиотекам с динамическим вызовом системы
Windows — DLL fDynamic Link Libraries) в Акторе имеется класс
Library, который в свою очередь для доступа к возможностям DLL
использует класс Proc. Proc является потомком класса Struct. При
использовании Library для доступа к DLL-библиотекам вам при¬
дется создать отдельный экземпляр класса для каждого библио¬
течного файла, который вам нужен. Экземпляры Library
связывают некоторое имя с файлами DDL и определяют отдельные
СЛовари (каталоги) процедур. Для записи имени в экземплярную
переменную используется метод setName класса Library.
Например, для того чтобы инициализировать экземпляр Library
для использования PBRUSH.DDL, модуля, который поставляется
вместе с Windows, вы должны были бы сделать что-нибудь подо-
бйое тому, что написано ниже, при этом, вероятно, изменив
н&звание драйвера и маршрут (pathname):
pbLib := new(Library);
setName(pbLib, "e:\windows\pbrush.dll");
После этого надо создать экземпляры Proc для каждой функции,
которую вы собираетесь использовать. Это делается при помощи
сообщения add, которое создает новый экземпляр Proc и вводит
имя процедуры и дескриптор аргументов в объект Library. Library
имеет свое собственное сообщение load, которое используется для
загрузки модулей из DLL файлов. В действительности вызов
процедур DLL состоит в посылке сообщений pcall к нужным
экземплярам Proc. Перед выходом из Актора или из приложения,
построенного при помощи Актора, необходимо явно закрыть DLL-
модуль при помощи сообщения free. Используя DLL-библиотеки,
вы должны каждый раз загружать и освобождать нужные вам
модули, что несколько неудобно, однако это не слишком большая
цена за то разнообразие программ, которое становится вам доступно.
■ ПРЕДСТАВЛЕНИЕ ЗНАНИЙ
Актор включает в себя набор классов, которые определяют язык
представления фреймов (Frame Representation Language — FRL),
весьма полезный для представления знаний. Эта возможность
обеспечивается классами Frame, FrameList и Slot. Frame,
FrameList и Slot являются потомками класса Dictionary. Их на¬
значение — это представление структур знаний, организованных
в форме фреймов, слотов и граней. Экземпляры класса FrameList
предоставляют словари всех фреймов, которые будут использо¬
ваться в данном приложении. Объекты FrameList — это словари
Представление знаний
153
фреймов, объекты Frame — это словари слотов и объекты Slot —
это словари граней. Грани не представлены в Акторе отдельным
классом, они могут быть любым классом или объектом.
Первое, что нужно сделать для создания базы знаний на FRL, —
создать новый экземпляр класса FrameList. В примере, который
мы будем рассматривать, используется FRL для хранения цепочки
фактов истории. Инструкция для создания экземпляра класса
FrameList выглядит так:
History := new(FrameList, 12);
set(History, #event, #date, #location, #principal,);
set(History, #event, #time, #value, nil);
setValue(History, #event, #value, #ElectionofChairman);
^Z
setValue (History, #ElectionofChairman, #date,
date(new(Date), 11, 7, 1992));
В последней инструкции значение слота #date задается путем
создания экземпляра класса Актора Date, который предоставляет
свой собственный формат для хранения дат и программы для
манипулирования ими.
getValue(History, #ElectionofChairman, #date);
11-7-1992
setValue (History, #ElectionofChairman, #location, "San
Francisco);
setValue(History, #ElectionofChairman, #time,
time(new(Time), 13,
00, 0)) ;
13:00:00
setValue(History, #event, #value, #SigningofContract);
setValue(History, #SigningofContract, #date,
date(new(Date), 1, 1, 1993));
dayOfWeek(getValue(History, #ElectionofChairman,
#date));
"Tuesday"
dayOfWeek(getValue(History, #SigningofContract,
#date));
"Wednesday"
asDayString (getValue(History, #SigningofContract,
#date));
"January 1, 1993"
asLongString(getValue(History, #SigningofContract,
#date));
"January 1, 1993"
asDayString(getValue(History, #SigningofContract,
#date));
"Friday, January 1, 1993"
154
Введение в систему программирования Актор
Я ОТЛАДКА
В результате ошибки в программе, выполняющейся в системе
Актор, открывается диалоговое окошко, содержащее состояние
стека истории (вызовов) на момент обнаружения ошибки. Кроме
того, диалоговое окошко обычно будет содержать сообщение, ди¬
агностирующее тип ошибки. Если хотите, вы можете при появле¬
нии диалогового окошка, вызванного ошибкой, нажать на
клавишу ’’debug” и тем самым открыть окно Debug. Это весьма
мощный инструмент отладки, сочетающий в себе некоторые воз¬
можности броузера, инспектора, а также средств для изменения
любых значений, связанных с некоторым методом. При помощи
этого инструмента вы можете продолжать работу, на ходу попра¬
вив обнаруженную ошибку.
■ ПРОФАЙЛЕР
Работая в Акторе, вы в любое время можете провести измерения
вашего приложения, построить его ’’профиль”, выполнив сообщение:
profile(System);
Начиная с этого момента Актор будет собирать информацию о
том, сколько раз вызывался каждый из методов. Такое же сооб¬
щение profile(System) используется и для того, чтобы остановить
сбор информации о профиле. Когда сообщение будет послано во
второй раз, собранные данные будут автоматически записаны в
файл PROFTLE.DAT.
■ ВЫПОЛНЕНИЕ ВНЕШНИХ ПРОГРАММ
Внешние программы можно выполнять при помощи метода exec
класса String, например:
exec ("D:\execl\execl\.exe");
Метод exec получает аргументы командной строки, так что можно
выполнить и такую команду:
exec("D:\execl\execl d:\execl\library\goalseek.xlm");
Результат метода exec состоит в том, что управление передается
указанному приложению. Оно сначала загружается, а потом вы¬
полняется. Для того чтобы вернуться назад, пользователь должен
выйти из внешнего приложения.
Класс Behavior в Акторе является корневым классом для мета¬
классов, так же как Object — для обычных классов.
Класс Application
155
■ "РАСПЕЧАТЫВАНИЕ” ПРИЛОЖЕНИЙ
Постольку поскольку Актор компилируется инкрементально, тре¬
буется, чтобы программы Актора были обеспечены соответствую¬
щим окружением для своего выполнения. Вместо "линкования”
федактирования связей) скомпилированных программ с библио¬
теками поддержки времени исполнения предоставляется другой
метод, обеспечивающий предварительную обработку нужных при¬
ложений. В Акторе он называется ’’распечатыванием” (seaUng-
off). Для облегчения этого процесса в Акторе имеется меню
Seal-off. Выбранное при помощи мыши, это меню открывает
диалоговое окно, которое содержит шаблон для ввода информа¬
ции, необходимой для процесса распечатывания. Вы должны
ввести имя класса приложений, имя образа, который вы собирае¬
тесь использовать, и параметры для его статической и динамиче¬
ской памяти.
■ КЛАСС APPLICATION
В предыдущем параграфе я упоминал, что средство распечатыва¬
ния требует введения имени класса приложения. Нужно сделать
некоторые пояснения, поскольку отнюдь не каждый класс позво¬
ляет использовать эту возможность корректно. Обычно приложе¬
ние включает в себя несколько классов. Но для того чтобы
приложение можно было распечатать, оно должно быть подклас¬
сом специального класса, называемого AppHcation. Рассмотрим
некоторые из возможностей этого класса. Класс Application имеет
две экземплярные переменные ’mainWindow’ и ’conunandLine’.
Значения этих экземплярных переменных сообщают средствам
распечатывания имя главного окна приложения и аргумент ко¬
мандной строки, которая будет посылаться в метод init данного
класса. Этот и другие методы класса не касаются того, что делает
приложение, однако они связаны с процессом распечатывания и
обеспечением функционирования в среде выполнения.
Вызывающие функции Windows
Ниже приводится пример вызова функции Windows из метода
createMenu класса Windows:
Def createMenu(self)
{ if setMenu(self, Call CreateMenu())
then Call DrawMenuBar(getHWnd(self))
else ^nil
endif;
}
Еще более простой пример — широко используемый метод класса
Window Object:
156
Введение в систему программирования Актор
Def show(self, val)
{ Call ShowWindow(getHWnd(self), val);
update(self);
}
В методе appendItem класса Menu имеется еще один вариант
использования вызовов Windows-функций:
Def appendItem(self, menuItem, hM pC hPopup)
{ if pC := popupCall(menuItem)
then hPopup := Call CreateMenu();
do(pC,
{using(mi) appendItem(self, mi, hPopup);
});
Call AppendMenu(hM, MF_POPUP, hPopup,
asciiz(text(menuItem)));
else addToMenu(menuItem, hM);
setAction(self, id(menuItem), actionSym(menuItem));
endif;
}
Сначала при помощи вызова функции CreateMenu создается пози¬
ция меню, ей дается название hPopup. Затем, используя hPopup
как аргумент, вызывается функция AppendMenu. Позиция меню,
связанная с временной переменной hPopup, создается и подклю¬
чается к меню, которое посылало сообщение.
■ ДОБАВЛЕНИЕ ПРИМИТИВОВ В АКТОР
Primitive — это встроенный метод, эффективность которого объ¬
ясняется тем, что он написан непосредственно в машинных кодах,
а не компилируется из программ Актора. Актор допускает добав¬
ление примитивов, написанных на языке ассемблера или на Си.
Группа Whitewater предоставляет также следующие пакеты под¬
держки:
The Actor Extension — расширение Актора;
The Resource TooMt — пакет работы с ресурсами;
Object Graphics — объектная графика;
The Wintrieve Database Toolkit — база данных Wintrieve;
Actor vs SmalltaDc — Актор против Смолтока.
Между Смолтоком и Актором так много общего , что у каждого
может возникнуть вопрос, зачем тратить так много усилий на
создание нового языка, когда реализации Смолтока уже сущест¬
вуют. Имеются две основные: причины. Во-первых, Актор проек¬
тировался совместимым с коммерческой операционной средой на
ШМ PC, следовательно, как внутренне совместимый с Microsoft
Windows. Во-вторых, синтаксис Актора разработан с таким расче¬
том, чтобы он был ближе тем программистам, кто работал на Си
и на Паскале. Короче говоря, Актор предназначен для того, чтобы
Добавление примитивов в Актор
157
дать систему, включающую в себя все хорошее, что есть в Смол-
токе, переоформленное таким образом, чтобы отвечать специфи¬
ческим потребностям сегодняшних пользователей IBM PC. И
последнее, но отнюдь не маловажное обстоятельство состоит в том,
что Актор работает под управлением MS-Windows. Смолток же
обычно пользуется своим собственным пользовательским интер¬
фейсом и своей оконной системой.
До сих пор я избегал обсуждения одного из аспектов языка Актор,
а именно темы множественного наследования. В системах, рабо¬
тающих с реальными проблемами, множественное наследование
во все большей степени будет становиться стандартной возможно¬
стью. Причина здесь проста. В реальном мире многие предметы
наделены множеством ролей, выполняют несколько функций.
Множественное наследование указывает прямой путь для реали¬
зации этого свойства. Поставщики объектноюриентированных
инструментов, которые отказываются от включения средств мно¬
жественного наследования, обычно ссылаются на то, что не хотят
делать систему слишком сложной для пользователей, или на то,
что никто из потребителей не заказывал таких возможностей. Я
не нахожу эти ответы особенно убедительными. Существует еще
одна точка зрения: я не встречал особых трудностей в использова¬
нии множественного наследования в тех системах, где оно есть, и
с трудом представляю себе серьезную объектно-ориентированную
программу без него. В ответ на утверждения такого рода можно
сказать, что в системе без множественного наследования по-преж¬
нему есть возможность создавать классы с одинаковыми опреде¬
лениями; это тяжелый путь, но он позволяет выразить то, что вы
хотите. Мое отношение к этому таково: что возможно в теории,
никогда не делается на практике. За 5 лет работы с объектноюри-
ентированными системами мне пришлось лишь раз или два рабо¬
тать в системах без множественного наследования, в то время как
работа с системами, имеющими его, — самое обычное дело.
Причина этому та же, что делает важными интерактивные систе¬
мы типа Актор, а не только простое удобство. Чем проще можно
сделать базовые вещи, тем больше возникает стремление творче¬
ски подойти к более сложным областям, пытаясь сделать то, за
что в другом случае вы, возможно, и не взялись бы. Если вы
пользователь объектно-ориентированных средств, которые лише¬
ны множественного наследования, я настоятельно рекомендую
попытаться уговорить продавца включить его первым номером в
список новых качеств системы. Я уверен, что вы не пожалеете о
том, что воспользуетесь здесь своим правом покупателя.
158
Введение в систему программирования Актор
Л ИЗМЕРЕНИЕ ВРЕМЕНИ
Важность возможностей работы со временем особенно проявляется
в операционной среде типа Windows. Кроме того, это область, где
преимущества объектно-ориентированкого программирования
прямо бросаются в глаза. Во многих операциях системного уровня
объектно-ориентированный подход применим лишь отчасти. В
качестве иллюстрации можно привести класс Актора System, в
котором имеется целый ряд системных функций, которые плохо
выразимы в терминах посылки сообщений объектам. По этой
причине все методы из System являются методами класса, а не
методами экземпляров класса. С другой стороны, оформление
временных записей в виде объектов, которые заключают в себе все
необходимое для их работы с ними, в действительности представ¬
ляет собой классический пример, демонстрирующий преимущест¬
ва объектно-ориентированного подхода.
Пакет Windows API предоставляет в качестве ограниченного ре¬
сурса Timer. Timer используют несколько инструментов Windows
для обеспечения "рабочего стола”, средл них такие, как Clock,
Calendar, Terminal и Control Panel. Эти средства всегда доступны
для непосредственного вызова из функций Windows. Вызов функ¬
ции GetCurrentTime() не требует аргументов и возвращает время
Windows — число миллисекунд, прошедших с момента загрузки
системы.
GetTickCount ()
Функция Windows SetTimer создает события системного таймера.
Windows использует сообщение WM_TMER для ответа на вызов
функции SetTimer, сделанный любым приложением. Формат ар¬
гументов для SetTimer таков:
SetTimer(hWnd, nIDEvent, wElapse, lpTimerFunc)
Аргумент hWnd может опускаться. Он связывает таймер со спе¬
циальным окном путем указания его дескриптора. Если hWnd не
NULI то для указания временного интервала модификации тай¬
мера используется nffiEvent. Аргумент lpTimerFunc служит для
передачи в Windows имени callback функции, которой будут
посылаться сообщения WM_TMER. Функция SetTimer возвраща¬
ет значение, показывающее, создавался ли ранее таймерный ре¬
сурс. Если возвращается ноль, то ресурс не создавался.
Когда таймерный ресурс больше не нужен, для его завершения
можно использовать функцию KillTimer. Формат KillTimer таков:
KiIlTimer(hWnd, nIDEvent)
Когда параметр hWnd, используемый при вызове SetTimer, равен
Null, значением аргумента nK)Event, используемого для вызова
KiUTimer, служит величина, возвращаемая из SetTimer.
Работа с датами и временем
159
■ РАБОТА С ДАТАМИ И ВРЕМЕНЕМ
Классы Time и Date предоставляют множество услуг для работы с
форматами, которые используются в записях, хранящих время.
Новый объект Date можно создать вручную следующим образом:
dayl := date(new(Date), 7, 27, 90);
7-27-90
Класс Date содержит программы, которые позволяют узнать, на
какой день недели приходилась или будет приходиться некоторая
дата в прошлом, настоящем или будущем. Для того чтобы это
сделать, надо послать сообщение dayOfWeek любому объекту-дате
примерно таким образом:
dayOfWeek(dayl);
"Wednesday"
Сообщение dayOfWeekNumber можно послать для того, чтобы
получить число, порядковый номер дня в неделе вместо строки с
текстом. В данном примере вместо "Wednesday” (среда) возвраща¬
ется число 3.
dayOfWeekNumber(dayl);
3
Аналогичным образом, в той же манере, в какой класс Date
производит вычисления, необходимые для корректной работы с
датами, класс Timer предоставляет средства для работы с форма¬
тами данных, используемых для записи времени дня и ночи.
Вручную создать объекты Time можно при помощи посылки
сообщения такого типа:
Т1 := time(new(Time), 12, 30, 0);
12:30:00
Обычный временной формат можно преобразовать в формат вре¬
менного массива, использовав сообщение asTimeArray:
asTimeArray(T1);
Array(12 30 0 nil)
Как видно из ветви иерархии классов, приведенной ниже, Date и
Time являются классами одного уровня, и оба происходят из
класса Long.
Object
Magnitude
Number
Long
Date
Time
160
Введение в систему программирования Актор
Внутреннее представление дат — это ’’длинное число”, которое
соответствует числу дней, прошедших со времени введения Гри¬
горианского календаря (вероятно, имеется в виду число дней,
прошедших с начала нашего летоисчисления. — Прим. nepee.).
Интересцо сравнить реализацию классов Time и Date, поскольку
они похожи друг на друга, хотя используются иногда совершенно
по-разному. Наибольшее различие, конечно, состоит в том, что
классу Time приходится иметь дело с временными интервалами
вплоть до секунды, а классу Date — только до дня. Эти два класса
так близки по замыслу и реализации, что набор их методов
практически идентичен. Методами, которые в точности совпада¬
ют, являются: asLong, set, printOn и методы объекта sysPrintOn и
метод класса new. Даже текущие методы класса так схожи, что
различаются лишь значениями данных. Альтернативным вариан¬
том, способным извлечь преимущества такого близкого сходства
этих классов, могло бы стать создание формального класса
TemporalObject (временной объект), включающего в себя все мето¬
ды, идентичные в обоих классах. Тогда Date и Time были бы
потомками TemporalObject и дополнительно включали бы только
специфические для каждого из них методы. Имеющийся метод
класса можно было бы реализовать в формальном классе, посколь¬
ку изменяемое значение могло бы реализовываться как перемен¬
ная класса. Но особого практического интереса в данном частном
случае эта возможность не представляет, так как все равно потре¬
бовались бы различные дополнительные методы для инициализа¬
ции переменных класса.
Класс объектов Temporal (временной)
inherit(Long, #TemporalObject, nil, 1, nil)!!
now(class(TemporalObject))!!
/* Создать новый Data4>6beKT и инициализировать*/
Def new(self)
{ ^init(new(self:Behavior));
}!!
now(TemporalObject)!!
/* Отправить (положить) данные в указанный поток */
Def sysPrintOn(self, aStrm)
{ printOn(asString(self), aStrm)
}!!
/* Присваивает seLf полученные значения в форме
индексированной величины */
Def set(self, value temp)
{ temp := new(Struct, 4);
putLong(temp, value, 0);
setClass(temp, class(self));
swapProperties(self, temp);
swap(self, temp);
}!!
Работа с датами и временем
161
/* Отправляет данные в указанный поток */
Def printOn(self, aStm)
{ printOn(asString(self), aStrm)
} ! !
/* Возвращает seLf как kmg-число для использования
в арифметических операциях */
Def asLong(self temp)
{ ^setClass(temp := copy(self), Long)
} ! !
Класс таймер
inherit(TemporalObject, #Timer, nil, 1, nil)!!
now(class(Timer))!!
/* Возвращает текущее время */
Def current(self ds)
{ ds := new(Struct, 18);
putMSB(ds, 0x2c, 4);
call(ds);
^time(new(self),
atMSB(ds, 8), /* Часы */
atLSB(ds, 8), /* Минуты */
atMSB(ds, 10) /* Секунды */
}!!
now(Timer)!!
/* Нормализовать время, свести его к моменту времени
в единичном 24-х часовом периоде */
Def- normalize(self)
{ set(self, asLong(self) mod 86400)
} ! !
/* Определить временной объект на основании переданного
параметра */
Def time(self, hr, min, sec)
{ set(self, (hr*3600)+(min*60)+sec)
}N
/* Инициализировать время (момент 0:00:00) */
Def init (self)
{ set (self, 0L);
}!!
/* Возвратить массив из 4-х элементов — часы, минуты, секунды,
превышение. Превышение указывает, сколько в указанном времени
содержится полных 24 часа. Время, выходящее за границы полных
суток, должно быть нормализовано */
Def asTimeArray(self hour, min, sec, over, time)
{ if (time := (asLong(self) mod 86400) ) self /* больше,
чем 24 часа */
then over := asLong(self) / 86400;
endif;
hour := time / 3600;
min := time " (hour * 3600);
sec := min mod 60;
min := min / 60;
^tuple(asInt(hour), asInt(min), asInt(sec), over)
} ! !
6-857
162
Введение в систему программирования Актор
/* Преобразует self во временной объект */
Def asTime(self)
{ ^self
} ! !
/* Преобразует Timer-объект в строку */
Def asString(self ta)
{ ta := asTimeArray(self);
^asString(ta[0]) + ":" +
asPaddedString(ta[l], 2) + ":" +
asPaddedString(ta[2], 2)
} ! !
Класс Dating (для представления дат)
inherit(TemporalObject, #Dating, nil, 1, nil)!!
now(class(Dating))!!
/* Возвращает текущую дату */
Def current(self ds)
{ ds := new(Struct, 18);
putMSD(ds, 0x2a, 4);
call(ds);
^date(new(self),
atMSB(ds, 10), /* Месяц */
atLSB(ds, 10), /* День */
wordAt(ds, 8) /* Год */;
} ! !
now (Dating) ! !
/* Для данной даты возвращает предыдущую дату, не изменяя seLf */
Def Previous(self)
{ ^asDate(self " 1)
}!!
/* Для данной даты возвращает предыдущую дату, не изменяя seLf */
Def Previous(self)
{ ^asDate(self + 1)
} ! !
/* Пределом объекта-дата является 2 */
Def limit(self)
{ ^2
}!!
/* Инициализирует все новые даты как l/l/l980 */
Def init(self)
{ ^date(self, 1, 1, 1980);
}!!
/* Возвращает номер дня в неделе (0 — воскресенье, ...6 — суббота) */
Def dayOfWeekNumber(self)
{ ^asInt(asLong(self) + 4L) mod 7)
} ! !
/* Возвращает день недели */
Def dayOfWeek(self)
{ ^#("Sunday" "Monday" "Tuesday" "Wednesday" "Thursday"
"Friday" "Saturday")[dayOfWeekNurnber(self)3
Работа с датами и временем
163
/* Возвращает новый объект-дату с переданными значениями
месяца, дня и года. Дата вычисляется, путем прибавления к первому
дню года month месяцев и day дней, т.е. date(newflDate), 11, 31,
1990) возвращает 12-01-1990. За основу здесь взят алгоритм 199 из
фонда алгоритмов ACM */
Def date(self^ month, day, year centure)
{ if month 2
then month := month ^ 3;
else month := rtionth + 9;
year := year " 1;
endif;
centure := year / 100;
year := year ^ (centure * 100);
^set(self, ((asLong(centure) * 146097L) / 4L)
+ ((asLong(year) * 1461L) / 4L)
+ ((asLong(month) * 153L) + 2L) / 5L) '
+ (asLong(day) ^ 578041L));
}! !
/* Возвращает дату в сокращенном строковом формате */
Def asString(self da)
{ da := asDateArray(self);
^asString(do[0^) + "-" +
asString(do[l]) + "-" +
asString(do[2])
} ! !
/* Возвращает дату в длинном строковом формате */
Def asLongString(self da)
{ da := asDateArray(self);
^#("January" "February" "March" "April" "May" "June"
"July"
"August" "September" "October" "November"
"December")[da[0]-l]+
" " + asString(da[l]) + "," + asString(da[2])
}!!
/* Возвращает дату в формате ”день недели” */
Def asDayString(self)
{ ^dayOfWeek(self) + "," + asLongString(self)
} ! !
/* Возвращает массив с тремя элементами — месяц, день, год
[алгоритм 199 из фонда алгоритмов ACM] */
def asDateArray(self gregorian, month, day, year)
{ gregorian := asLong(self) + 578041L;
year := ((gregorian * 4) ^ 1) / 146097L;
gregorian := ((gregorian * 4) ~ 1) / 146097L;
day := (gregorian / 4);
gregorian := ((day * 4) + 3 ) / 1461;
day := ((day * 4) + 3) ^ (gregorian * 1461);
day := (day + 4) / 4;
month := ((day * 5) ^ 3) / 153;
day := ((day * 5) ^ 3) ^ (month * 153);
day := (day + 5) / 5;
year := (year * 100) + gregorian;
if month 10
then month := month + 3;
else month := month ^ 9;
164
Введение в систему программирования Актор
year := year + 1;
endif;
^tuple(asInt(month), asInt(day), asInt(year))
}!!
/* Преобразует полученное число в дату */
Def asDate(self)
{ ^self
} ! !
dayl := date(new(Dating), 3, 15, 90);
8-15-90
T1 := time(new(Timer), 12, 30, 0);
12:30:00
■ ЧТО ТАКОЕ ВРЕМЯ?
В последующих разделах главы вы познакомитесь с примерами,
которые демонстрируют, как легко иметь дело со временем в
объектно-ориентированных системах типа Актор и насколько удо¬
бен объектно-ориентированный подход к такого рода сущностям.
Прежде всего — маленькие цифровые часы, которые вы можете
включить в свою среду разработки, если уверены, что класс
ClockWindow загружен, выполнив сообщение:
CW :=.defaultNew(ClockWindow, "Current Time");
show(CW, 1);
Пример класса ClockWindow показывает, как легко реализовать
цифровые часы в Windows при использовании Актора. Как потом¬
ки класса TextWindow объекты класса ClockWindow предоставля¬
ют выдачу текущего времени в символьном виде. Одной из
специфических особенностей ClockWindow является возможность
синхронизации нескольких различных экземпляров класса при
помощи отслеживания интервалов изменения времени на некото¬
рой регулярной основе. Для того чтобы обеспечить это,
ClockWindow использует переменные класса. Всю необходимую
для простой модифицирующей процедуры информацию дают пе¬
ременные класса $biterval и $Clocks. В переменной $biterval
хранится интервал времени между модификациями объектов
ClockWindow. Переменная $Clocks хранит имена всех имеющихся
в данный момент экземпляров ClockWindow.
■ ЦИФРОВЫЕ ЧАСЫ
У класса ClockWindow есть несколько интересных возможностей
для выдачи правильного времени. Инициализация класса состоит
из следующих строк:
$Interval := 5;
$Clocks := new(Set, 4);
Цифровые часы
165
Это означает, что показания часов будут обновляться каждые пять
секунд и что переменная класса $Clocks инициализируется как
пустой экземпляр класса Set и имеет размер, позволяющий запо¬
минать до четырех объектов.
Каждые пять секунд — это не достаточно часто для многих
применений, в частности, если имеются несколько часов.
CW1 := new(ClockWindow, nil, n.il, "First Clock",
&(50,50,175,125));
show(CW1,1);
CW2 := new(ClockWindow, nil, nil, "Second Clock",
& (5 0, 110, 17 5,17 5) ) ;
show(CW2,1);
CW3 := new(ClockWindow, nil, nil,
&(50, 170, 175, 125)) ;
show(CW3,1);
"Third Clock"
CW4 := new(ClockWindow, nil, nil, "Fourth Clock",
& (50, 230,175,2 95));'
show(CW4,1) ;
Если вас беспокоит, что не всегда на часах одно и то же время,
выполните сообщение:
setInterval(ClockWindow, 0) ;
Далее показывается, как описан метод init для ClockWindow:
Def init (self)
{ init(self:ancestor);
add($Clocks, self);
tick(self);
setTimer(self, $Interval);
}
Def close(self)
{ killTimer(self);
remove($Clocks, self);
close (self:ancestor);
}
Как видите, этот метод задает значения для обеих переменных
класса ClockWindow. Можно сделать простое расширение
ClockWindow, если добавить метод класса new, который позволяет
описывать часовой пояс. Таким образом можно будет обеспечить
синхронизацию для нескольких часов в различных часовых поя¬
сах. Новый метод класса можно описать так:
Def new(self, parent, menu, windowName, rect,
timeZone aClock)
{ aClock := new(self:WindowClass, parent, menu,
windowName, rect);
aClock[timeZone] := ((timeZone mod 24) + 24) mod 24;
^aClock;
}
166
Введение в систему программирования Актор
Функция .SetTimer MS Windows реализует таймер в среде Windows,
являющийся глобальным ограниченным ресурсом.
Функции Windows для доступа к системному времени и дате очень
просты, они совсем не требуют никаких аргументов.
Call GetCurrentTime();
Call GetTickCount();
ОПИСАНИЯ КЛАССОВ ДЛЯ РАБОТЫ
СО ВРЕМЕНЕМ
ClockWindow
Исходный файл:
Наследует из:
Переменные класса:
$biterval
$Clocks
Методы класса:
setbiterval
sizeRect
Методы объектов:
close
drawTime
init
killTimer
setTimer
tic
CLOCKWIN. CLS
TextWindow
Устанавливает интервал часов (число
секунд). Интервал указывает, как часто
следует обновлять выдачу для каждого из
объектов, реализующих окно-часы. Когда
интервал завершается, каждый экземпляр
часов информируется, с тем, чтобы он мог
модифицировать свой временной интервал
Возвращает калиброванный прямоугольник.
Изменяет размеры калиброванного окна
так, что оно становится просто четвертью
от окна с размерами, задаваемыми
по умолчанию
Выключает таймер и затем закрывает окно
Рисует время в окне TextWindow,
перезаписывая строку, нарисованную ранее
Уничтожает таймер
Запускает таймер. Он будет посылать
сообщения WM_TMER каждые n секунд,
n — аргумент метода. Если нужны
миллисекунды, то проявите
изобретательность
Обрабатывает такт (tick) таймера.
Берет текущее время и выдает его
Описания классов для работы со временем
167
timeStr Возвращает текущую строку со временем.
Вызывает функцию DOS GetTime.
Прерывание 21H функция 2CH
update Модифицирует интервал таймера.
Переустанавливает таймер получателя на
такт каждые n секунд в соответствии со
значением переменной класса $brterval
STOPWATC. CLS
Object
экземпляров:
Время начала (старта) выполнения блока
Время конца выполнения блока
StopW а^Ь(секундомер)
Исходный файл:
Наследует из:
Переменные
startTime
stopTime
Методы объектов:
lap
Возвращает время между временем старта
и текущим моментом
start Запускает секундомер и записывает
время старта
stop Останавливает секундомер и возвращает
общее прошедшее время
С1оск(часы)
Примечание. Этот материал заимствован из приложений,
написанных Джимом Кауторном. Описание класса
приводится здесь с целью обучения.
Исходный файл: CLOCK.CLS
Наследует из: Window
Переменные экземпляра:
brForeground
penBackground
penForeground
bIconic
aspectD
aspectN
clockRect
clockRadius
clockCenter
VertRes
HorzRes
MapSize
TimerID
oTime
n Time
’’Кисть” для переднего плана
”Перо” для заднего плана
”Перо” для переднего плана
Не равно nil, если приложение свернуто
в пиктограмму
Масштабный множитель
Номер Timer-ID
Последнее считанное время
Самое новое считанное время
168
Вввдвнив в систему программирования Актор
timeZone
IconDrawMode
dayligth
Методы объектов:
clockPaint
clockSize
clockTimer
compClockDim
create
createTools
deleteTools
drawFace
drawFatHand
drawHand
drawHandsIcon
drawHandsWnd
killTimer
paint
setTimer
show
Число часов, которое надо прибавить к
времени (для получения поясного
времени — Прим. nepee.)
Рисовать изображение часов
непосредственно на экран или сначала
в bitmap-буфер, а потом на экран?
Не равно nil, если дневное время, и
nil, если ночное
Главный метод для рисования и
перерисовки часов
Дает информацию, в каком состоянии
находятся часы, в виде пиктограммы и
в виде обычного окна
Управляет сообщениями таймера,
посланными к команде часов
Вычисляет размерности часов
Решает создавать POPUP или TILED
(паркетное) окно
Строит перья и кисти для рисования часов
Уничтожает перья и кисти
Рисует циферблат в форме окружности
из черточек (рисок) и точек
Рисует часовую и минутную стрелки,
когда часы представлены в виде окна
(не пиктограммы)
Рисует секундную стрелку или минутную
и часовую стрелки, когда часы
представлены пиктограммой
Рисует минутную и часовую стрелки
в пиктограмме
Рисует минутную и часовую стрелки в окне
Уничтожает системный таймер
Перекрашивает экран
Устанавливает системный таймер
Выдает показание часов в соответствии
со значением val. Важен порядок событий.
Часы должны иметь свое уже "показанное”
окно до того, как к нему будет
присоединяться окно ошибок. Перед
исполнением ’show’ инструменты
должны быть уже созданы
Дополнительный пример программ
169
■ ДОПОЛНИТЕЛЬНЫЙ ПРИМЕР ПРОГРАММ
В листинги 1 — 4 включены дополнительные примеры программ,
три из которых по типу близки к эталонным программам для
проведения измерений производительности: простые числа, числа
Фиббоначчи и Ханойские башни; последняя программа содержит
некоторые расширения для обработки списков. Поскольку эти
алгоритмы реализовывались так много раз и на таком множестве
широко распространенных языков программирования, то для
опытного программиста будет нетрудно быстро разобраться, как
же использовать Актор там, где он пользуется своим любимым
языком. Когда вы изучите Актор достаточно основательно, то
придете к заключению, что он поддерживает все известные вам
конструкции, а также и некоторые другие.
Листинг 1. Эталонная программа ”Решето Эратосфена”
inherit(Object, #Sieve, nil, nil, nil));
now(Sieve);
/* Возвращает число простых чисел между 0 и cnt,
включительно */
Def sieve(self, cnt flags, count, с)
{ с := cnt + 1;
flags := new(Array, с);
fill(flags, true);
count := 1;
do( over(2, с),
{ using(i triple)
if flags[i]
then triple := i*3;
if triple cnt
then do( overBy(triple-1, с, i+i-1),
{ using(j) flags[j] := nil });
endif;
count := count + 1 ;
endif;
}) ;
Acount;
}
Acto.r[#Sam] := new(Sieve)
/* Для запуска надо напечатать: sieve(Sam, 100) */
Листинг 2. Программа Фиббоначчи
now(Int)
/* Рекурсивный метод поиска n-ro числа Фиббоначчи.
Преднамеренно выбран крайне неэффективный способ
поиска чисел Фиббоначчи, каждое сообщение
"размножается”, порождает рекурсивно два других */
Def fib(self)
{ if self 3
then ^1
endif; Afib(self " 1) + fib(self ^ 2);
}
170
Введение в систему программирования Актор
/* Итеративный метод поиска n-го числа Фиббоначчи */
Def fib2(self term termlBefore, term2Before)
{ if self 3
then ^1
else term := 2; termlBefore := 1; trem2Before := 1;
do(new(Interval, 3, self + 1, 1),
{using(i) term:= termlBefore + term2Before;
term2Before := termlBefore;
termlBefore := term;
});
endif;
}
Листинг 3. Ханойские башни
/* См. Byte, август 1986, с. 146, cbd 13.8.86 */
inherit(Object, #TowerOfHanoi, nil, nil, nil);
now(NowerOfHanoi);
Def moveTower(self, height, from, to, use)
{ if height 0
then
moveTower(self, height ^ 1, from, use, to);
moveTower(self, height ^ 1, use, to, from);
endif;
}
Def moveTower2(self, height, from, to, use)
{ if ’height 0
then
moveTower(self : TowerOfHanoi, height ^ 1, from,
use, to);
moveTower(self : TowerOfHanoi, height ^ 1, use, to,
from);
endif;
}
Actor[#Hanoi] := new(TowerOfHanoi);
/* пример запуска программы для решения задачи о Ханойских
башнях */
moveTower(Hanoi, 3, 1, 3, 2);
Листинг 4. Поддержка обработки списков в Акторе
/* С.В. Duff 7.13.86 (с) Copyright, 1986 */
now(NilClass);
/* подсоединить niI к узлу */
Def append(self, aNode)
{ ^aNode }
Def cons(self, aNode)
{ ^aNode }
Def rPrintOn(self., aStrm)
{ printOn('[', aStrm);
}
Дополнительный пример программ
171
inherit(Collection, #ListNode, #(left, right), nil,
nil) ;
now(ListNode);
Def append(self, aNode)
{ ^cons( left, append(right, aNode));
}
Def do(self, aBlock)
{ if isAtom(left)
then eval(aBlock, left);
else do(left, aBlock);
endif;
if right
then do(right, aBlock);
endif;
}
Def isAtom(self)
{ ^nil }
Def printOn(self, aStrm)
{ printOn('[', aStrm);
printOn(left, aStrm);
rPrintOn(right, aStrm);
}
Def rPrintOn(self, aStrm)
{ printOn(' ', aStrm); printOn(left, aStrm);
rPrintOn(right, aStrm);
printOn(']', aStrm);
}
now(Object) ;
Def isAtom(self)
{}!!
Def rPrintOn(self, aStrm)
{ printOn('.', aStrmf);
printOn(' ', aStrm));
printOn(self, aStrm));
printOn(']', aStrm));
}
Def cons (self, aNode newNode)
{ if isAtom(aNode)
then newNode := new(listNode)/
else newNode := copy(aNode);
endif;
newNode.left := self;
newNode.right := aNode
^newNode;
Глава 6
Я ОБЪЕКТНО-ОРИЕНТИРОВАННЫЙ
ПОЛЬЗОВАТЕЛЬСКИЙ
ИНТЕРФЕЙС ДЛЯ WINDOWS
Центральным моментом во всей разработке MS-Windows является
использование его возможностей для построения пользовательско¬
го интерфейса для различных приложений. Вопросы пользова¬
тельских интерфейсов привлекают к себе так много внимания
практически во всех применениях Windows, что даже требуются
определенные усилия для того, чтобы отделить их от внутренних
аспектов работы прикладных программ. К счастью, объектно-ори¬
ентированные системы кое в чем выгодно отличаются от традици¬
онных, они несут в себе именно тот тип модульности, который
здесь и требуется. Мы, по-прежнему, будем использовать язык
Актор, описанный в предыдущей главе. Однако основное внима¬
ние будем уделять вопросам, которые касаются всех объектно-ори¬
ентированных систем в Windows.
■ СОЗДАНИЕОКОН
Вначале процесс создания окна в Акторе может показаться не¬
сколько сложным. Это потому, что необходимо запомнить несколь¬
ко важных правил. Для создания окон часто используются методы
классов new и defaultNew. Метод defaultNew — это вариант new,
аргументы которого определяются по умолчанию, поэтому его
проще и легче использовать. Метод класса new обращается к
методу объекта create, который в свою очередь вызывает функцию
CreateWindow в среде MS-Windows.
Элементарное окно можно создать и открыть, послав сообщение:
W := defaultNew(Window, "Basic Window”);
show(W,1);
Создание окон
173
Таким образом создается и выводится базисное пустое окно, кото¬
рое само по себе делать что-либо существенное не может. Этим
должны заниматься подклассы Windows, использующиеся для тех
или иных целей. Другим общецелевым методом для создания окон
практически любого типа служит метод newStyle. Цена, которую
приходится платить за общность, — это необходимость полностью
задавать большой набор аргументов. Фактически их семь. Первый
определяет имя класса типа окна, которое вы создаете. Следующий
дает имя родительского окна, если вы хотите создать дочернее
окно. В большинстве случаев создаваемые окна являются дочер¬
ними того или иного вида. Для этих целей обычно используется
метод newChild.
Ниже приводится ветвь иерархии классов Актора, в которой
собраны различные специализированные классы окон.
Object
W indowsObject
Window
TextWindow
EditWindow
WorkEdit
BrowEdit
FileWindow
WorkSpace
ToolWindow
Browser
bispector
Класс WindowsObject — это формальный корневой класс, который
дает основу для создания любого из основных объектов, использу¬
емых при создании окна, таких как элементы управления, диалоги
и окна сами по себе. Впервые на уровне класса Window обеспечи¬
вается поддержка меню. По умолчанию выбирается стиль popup
окон, а в качестве указателя — обобщенный курсор мыши, Хотя
Window в большинстве случаев это лишь формальный класс, его
экземпляры можно использовать для выдачи графики.
FW := newMain(FileWindow, "editmenu","Editor", null);
show(FW,1);
Каждый раз, когда в Акторе создается оконный объект, в Windows
посылается сообщение CallCreateWindow, содержащее не менее
чем одиннадцать параметров. Когда в такой ситуации создается
оконный объект, Windows посылает дескриптор или имя создан¬
ного окна и Актор должен запомнить этот дескриптор. В терминах
ООП на Акторе можно сказать, что в классе Window и его потомков
имеется метод create, результатом работы которого является по¬
Объектно-оривнтированный пользовательский интерфейс для Windows
сылка MS-Windows сообщения CreateWindow. Дескриптор окон¬
ного меню запоминается в экземплярной переменной hMenu. Ме¬
тод IoadMenu занимается загрузкой ресурсов меню и задает
значение hMenu.
Вызов метода defaultNew равносилен вызову new с некоторым
набором параметров, задаваемых по умолчанию. Простейшее оп¬
ределение метода выглядит так:
Def defaultNew(self, паше)
{ ^new(self, mainWindow(TheApp:late), nil, паше, nil);
}
Аналогичным образом метод new легко определяется в терминах
метода newStyle:
Def new(self, par, menuName,wName, rect)
{ ^newStyle(self, par, menuName,wName, rect, nil, nil)
}
В методе newStyle, который уже выполняет некоторую реальную
работу, главными являются обращения к методам create и init. Вот
как определяется метод newStyle:
Def newStyle(self,par,menuName,wName,rect,id,style |
theWnd)
{ theWnd := new(self:Behavior);
if menuName
then loadMenu(theWnd, menuName);
else setHMenu(theWnd, setContl'D(theWnd, id));
endif;
setPaintStruct(theWnd, new(Struct, 32));
create(theWnd, par, wName car lllf, rect car
sizeRect(self),
style cor style(self));
setLocRect- (theWnd) ;
^init(theWnd);
}
И наконец, когда мы доберемся до настоящего работающего мето¬
да, увидим, где происходят обращения к функциям базисного
уровня Windows типа CreateWindow. Например, в определении
метода create, которое приводится ниже, функция CreateWindow
вызывается одним из двух способов в зависимости от того, предо¬
ставлено ли реальное значение для аргумента rect. Когда оно равно
nil, размер задается по умолчанию:
Def create(self, par, wName, rect, style | wndCls,
parHWnd)
{ parHWnd :=
if parent := par
then handle(par)
else 0
endif;
wndCls := wndClass(class(self));
if Call GetClassInfo(HInstance, asciiz(wndCls),
new(Struct, 26)) = 0
Текстовые окна
175
then register(class(self));
endif;
caption := wName;
wStyle := style;
setCurWindow(System, self);
hWnd :=
if rect
then Call
CreateWindow(asciiz(wndCls,asciiz(caption),style,
left(rect),top(rect),width(rect),height(rect),
parHWnd, hMenu cor 0, HInstance, 0L) ;
else Call CreateWindow(asciiz(wndCls),
asciiz(caption),style,
0x80 * 0x100, 0, 0x80 * 0x100, 0,
parHWnd, hMenu cor 0, HInstance, 0L) ;
endif;
if hWnd = 0
then alert(System, sell, #windCreateError);
endif;
add(Windows, self);
if not(parent)
then add(OpenWindows, self);
endif;
}
Важным видом сообщений являются сообщения newMain. Они
используются для того, чтобы создать и вернуть новое окно,
пригодное как главное окно некоторого приложения. Определение
newMain таково:
Def newMain(self, menu, caption, rect)
{ ^newStyle(self, nil, menu, caption, rect, nil,
WS_OVERLAPPEDWINDOW)
}
Для аргумента menu требуется строка, соответствующая имени
меню, определенному в ресурсном сценарии (script), или nil, если
такого меню нет. Аргумент caption — другая строка, если это не
nil, то это будет заголовок окна. И наконец, для аргумента rect
ожидается либо структура, задающая координаты окна на экране,
либо nil, тогда координаты задаются по умолчанию.
■ ТЕКСТОВЫЕ ОКНА
Класс TextWindows — это один из простейших потомков Window,
который в действительности может предоставить что-то полезное.
Этот класс позволяет создавать паркетные окна, которые могут
распечатывать текст. Он делает это при помощи методов
printString и printChar, которые вызывают функцию MS-Windows
Textout GDI (Graphics Display Interface). Часто хочется иметь
текстовое окно, которое позволяет не только выводить текст, но и
передвигаться по нему и редактировать его. Подкласс
TextWindow, вызывающий EditWindow, предоставляет программу
для обеспечения таких возможностей. Класс WorkEdit делает еще
176
Объектно-ориентированный пользовательский интерфейс для Windows
один шаг вперед, он позволяет создавать окна, в которых можно
не только редактировать, но и вводить и выполнять операторы
языка Актор. Три подкласса WorkEdit предоставляют окна тради¬
ционного назначения: броузеры, броузеры файлов и рабочие окна
общего назначения. Их главное отличие от традиционных в том,
что они так же,как и их предок TextWindow, являются паркетны¬
ми. Чаще всего в Акторе используются окна из другой ветви
дерева, они реализуются на основе класса PopupWindow.
Объект TextWindow можно создать и открыть при помощи сооб¬
щения:
TW := defaultNew(TextWindow, "Characters, Strings, &
Things”);
Show(TW, 1 );
Окно, созданное таким образом, не позволяет вводить текст непос¬
редственно. Однако вы можете переслать в него текст. Например,
следующие сообщения дают окну команду выдать различные
текстовые строки:
eol(TW);
printString(TW,"There must be an easier way.”);
eol(TW);
eol(TW);
printString(TW,"Oh, come on. You're enjoyirig this and
you know it") ;
Другой неплохой возможностью TextWindow является то, что он
может сохранять отметку о месте вставки, положение этой пози¬
ции запоминается в экземплярных переменных xPos и yPos.
Позицию вставки можно изменить при помощи сообщения
moveCaret, например:
setXPos(TW, 50);
moveCaret(TW);
Кроме того, объекты TextWindow восстанавливают свое изображе¬
ние после того, как их на некоторое время заслоняет другое окно.
У TextWindow имеется метод setXPos, который позволяет устанав¬
ливать новое значение переменной xPos, и, хотя нет метода
setYPos, его легко определить:
Def setYPos(self, yp)
{ ^yPos := yp;
}
Окна, которые мы получаем в popup-стиле, напоминают слои,
накладываемые друг на друга. В отличие от паркетных окон их
нельзя распахнуть или сжать в пиктограмму. Это обусловлено
MS-Windows, рорир-окна требуют наличия ’’родительского” тек¬
стового окна. Когда родительское окно сжимается в пиктограмму,
рорир-окна, ассоциированные с ним,временно становятся невиди¬
мыми. Окнами стиля popup обычно бывают экземпляры классов
EditWindows
177
Control, Dialog или их потомков. Позже я уделю внимание окон¬
ным объектам этого типа.
■ EDITWINDOWS
В качестве примера полноценного окна для редактирования можно
использовать окно, которое получится., если в рабочем окне вы¬
полнить сообщение:
E := new(EditWindow, ThePort, "editmenu","Bas.ic
Editor",nil);
show(E,1);
Созданный редактор имеет два меню команд, размещенных на
главной полоске. Первое меню содержит операции редактирова¬
ния для Бейсика, второе служит для поиска и замены текстов.
Нам предоставлен элементарный редактор, правда в нем нет
файлов ввода-вывода и других удобств, необходимых для функци¬
онально полной программы подобного назначения. А как можно
было бы превратить базисный объект редактирования во что-ни¬
будь более функциональное и полезное? Для того, чтобы ответить
на этот вопрос, вам придется узнать кое-что о том, как работают
объекты EditWindow. Нам также будут полезны два других фай¬
ловых класса FiIeWindow (табл. 6.1) и FileEditor.
Методы для управления меню, используемые в EditWindow, в
действительности наследуются из классов Window и
WindowObject. В классе Window методы объектов createMenu,
drawMenu, loadMenu, setHMenu и setMenu определяются для
управления меню, представленных в форме статических ресурсов.
Методы классов new, newMain и newStyle получают параметры
меню или имена как аргументьь Экземплярные переменные
hMenu и menu также наследуются из класса Window. Экземпляр-
ная переменная menu предназначена для использования в случае
динамических меню. В отличие от о5ъектоп EditWindow у
WorkSpaces имеются опции для работы с файловыми операциями.
Для того чтобы создать новое функционально полное рабочее
окно, используя уже скомпилированные ресурсы меню Actor.EXE,
выполним:
WS := new(Classes[#WorkSpace], mainWindow,
loadString(32), loadString{321), nil);
show(WS, 1);
Таблица 6.1. Экземплярные переменные FileWindow
fileDlg Диалог для загрузки файла
file Имя редактируемого файла
inputDelimiter Разделитель во входном файле
outputDelimiter Разделитель в выходном файле
178
Объектно-ориентированный пользовательский интерфейс для Windows
Сообщение loadstring используется для того, чтобы активировать
строки, которые уже были определены как статические ресурсы и
скомпилированы как часть файла ACTOR.EXE.
Сообщение getClipText из EditWindow возвращает текстовую стро¬
ку из Clipboard.
Def getClipText(self | hStr, aStr)
{ if Call OpenClipboard(hWnd) 0
then hStr := Call GetClipboardData(CF_TEXT);
aStr := getText(hStr);
Call CloseClipboard();
AremoveNulls(aStr);
endif;
Call CloseClipboard();
Anil
■ FILEEDITOR
Другим важным классом для редактирования окон является
FileEditor. Как подсказывает само название, этот класс предостав¬
ляет окно редактирования с меню для выполнения различных
операций над файлами. Сообщение open реализовано как метод
класса таким образом, что одного сообщения достаточно для
создания и открытия объекта FileEditor, например:
FE := ppen(FileEditor, "New");
■ ОПИСАНИЯ КЛАССА
FileEdit
Файл с исходным текстом:
Наследует из:
Переменные экземпляра:
fileDlg
inputDelimiter
outputDelimiter
Методы класса:
open
style
wndClass
wndIcon
FILEEDIT.CLS
EditWindow
Диалог для загрузки файла
Разделитель во входном файле
Разделитель в выходном файле
Открывает новый редактор файла
для указанного файла
Возвращает стиль окна по умолчанию
Возвращает имя оконного класса
MS-Windows для объектов этого
класса
Возвращает имя оконного класса
MS-Window указанного класса для
регистрации или для создания
нового окна
Описания класса
179
Методы объектов:
charIn
CheekDirty
command
create
init
maxFileSize
openFile
openSaveAs
readText
recreate
save
setFileDlg
shoudClose
showTitle
writeText
ActorApp
Файл с исходным текстом:
Управляет автоидентификацией
Спрашивает у пользователя,
в действительностили он хочет
покинуть редактор (закончить
редактирование). Возвращает true
(self), если это так. Текст в окне
диагностики Определялся как
ресурсные строки
Управляет событиями меню.
Создает Filewindow, используя все
параметры по умолчанию.
Подключает FileDialog
Инициализирует переменные
для хранения разделителей
Возвращает максимальный размер
считываемого файла
Открывает новый файл и заполняет
workText его содержимым
Запрашивает у пользователя имя
файла и пытается открыть его.
Возвращает имя файла в DOS или nil
Заменяет диапазон выборки на
содержимое файла. fName — строка, в
которой находится имя считываемого
файла DOS, например, ”new.doc”
Выполняет "пересоздание” в
соответствии со значением
экземплярных переменных
Сохраняет текст в текущем файле
Устанавливает и возвращает
файловый диалог
Подтверждает уверенность, что
пользователь в действительности
хочет закрыть окно
Выдает текущий заголовок
редактируемого файла или "Untitled”
если его имя еще не определено
Записывает содержимое workText
в файл. fName — строка, в которой
находится имя файла в DOS,
например, "new.doc”
ACTORAPP.CLS
180
Объектно-ориентированный пользовательский ингерфейсдля Windows
Наследует из: Application
Экземплярные переменные:
workspace
imageName
dynamic
static
Методы класса:
Методы объектов:
abort Очищает mainWindow и
перерисовывает, если необходимо,
текущее окно. Этот метод возвращает
true (self) для того,чтобы показать,
что отправитель может продолжать
выброшенный процесс
■ КЛАСС ACTORAPP
Разобраться, как строятся пользовательские интерфейсы в Акторе,
можно, тщательно изучив класс ActorApp. Этот класс предостав¬
ляет большую часть того, что имеется в пользовательском интер¬
фейсе в среде разработки Актора. Единственный экземпляр
ActorApp хранится в переменной с именем TheApp, этому экзем¬
пляру посылаются сообщения init в начале работы. Для любой
корректной прикладной программы необходимо, чтобы был неко¬
торый, хранящийся в TheApp объект, который мог бы отвечать на
сообщения init, abort и shouldClass.
Метод init в ActorApp начинает запуск Актора с выдачи паркет¬
ного окна, которое необходимо создать первым. Затем он создает
рабочее окно, если это возможно, и пересоздает все окна, которые
отвечают на сообщение recreate. Метод ActorApp всегда выполня¬
ется в начале работы для того, чтобы инициализировать среду
разработки Актора.
Def init(self, str)
{ init(self:ancestor, str);
DeadWindows := new(Set, 4);
if mainWindow
then recreate (mamWindow) ;
forceOnScreen(mainWindow);
else startMainWindow(self);
add(OutPorts:late,- mainWindow);
endif;
addAbout(mainWindow) ;
removeUsing(OpenWindows, mainWindow, {});
if workspace
then recreate(workspace);
else
if Classes[#WorkSpace];
then startWorkSpace(self);
Класс ActorApp
181
endif;
endif;
removeUsing(OpenWindows, workspace,{});
do(OpenWindows:late, {using(window) recreate(window);});
if size(DeadWindows:late) 0
then do(DeadWindows:late,
{ using(window)
remove(OpenWindows:late, window);
}) ;
beep();
printLine(loadString(382));
beep();
endif;
initDirty(self);
if workspace
then forceOnScreen(workspace);
changeLog(workspace),
"/*Started image: " + imageInfo(self) + "*/");
setFocus(workspace);
Call PostMessage(handle(workspace),
messageID(#WM_SYSCOMMAND),
IDSABOUT,0)?
endif;
initMemory();
AmessageLoop(self);
}
В первую очередь сообщение startMainWindow инициализирует
окно. Когда такое окно создано, метод теоретически можно было
бы удалять из системы, он требуется лишь один раз.
Def startMainWindow(self)
{
display:=mainWindow:=newMain(WorkWindow,nil,loadString(1 6
0), nil);
setBuffer(mainWindow, new(String, 120));
fill(buffer(mainWindow), ' ');
home(mainWindow);
show(mainWindow, CmdShow);
}
Метод startWorkSpace класса ActorApp точно так же запускает в
первый раз рабочую область workspace. Поскольку объект
workspace принадлежит OpenWindows, так же как и startMain,
теоретически этот метод можно удалить из системы.
def startWorkSpace(self)
{ workspace := new(Classes[#WorkSpace], mainWindow,
loadString(320), loadString(321), nil)?
show(workSpace, 1);
}
Сообщение reset класса ActorApp вновь вызывает целиком всю
среду Actor Application. Выполняя это, он очищает mainWindow
и окно workspace.
182
Объектно-ориентированный пользовательский интерфейс для Windows
Def reset(self)
{ mainWindow cand
cls(initWorkText(resetLocRect(mainWindow) ) ) ;
workspace cand
cls(initWorkText(resetLocRect(workspace)));
}
■ КЛАВИШИ, ДИАЛОГИ И ДРУГИЕ ЭЛЕМЕНТЫ
УПРАВЛЕНИЯ
Ветвь Актора, которая связана с элементами управления, имеет
следующую структуру:
Object
DlgItem
WindowsObject
Control
Button
Edit
ScrollBar
ListBox
ComboDox
Dialog
DialogDesign
FileDialog
biputDialog
■ СОЗДАНИЕ КНОПОК
Класс Button (кнопка) происходит из класса Control, который в
свою очередь является потомком класса WindowsObject. Кноп¬
ки — это в действительности маленькие дочерние окна со специ¬
фическими свойствами, связанными с управлением и с их
визуализацией Q)uc. 6.1). Один из примеров различных типов
кнопок можно увидеть, выполнив в workspace окне следующую
программу:
WB := new(Window,ThePort,nil,"Window For
Buttons",&(215,60,500,200));
show(WB,1);
B1 := newPush(Button, 202, WB, "Go");
setCRect(B1,&(10,20, 70, 40) ) ;
moveWindow(Bl) ;
show(Bl,T);
B2 := newDefPush(Button, 203, WB, "Stop");
setCRect(B2, &(90,20, 150, 40) ) ;
moveWindow(B2) ;
show(B2,1);
B3 := newDefRadio(Button, 204, WB, "Radio");
setCRect(B3,&(10, 60, 70, 80)) ;
moveWindow(B3);
Создание кнопок
183
Рис. 6.1. Четыре базисных стиля кнопок
show (B3,1);
B4 := newCheck(But'ton, 205, WB, "Check");
setCRect(B4,&(90,60,150,80));
moveWindow(B4) ;
show(B4,1);
Если у вас имеются некоторые сомнения или колебания в отноше¬
нии свободы выбора размеров кнопок, следующий пример должен
их рассеять:
GBW := new(Window, ThePort, nil, "Giant Button Window",
&(275, 60, 500, 300) ) ;
show(GBW, 1);
Huge := newPush(Button, 202, GBW, "Anything to Prave а
Point") ;
setCRect(Huge, &(15, 10, 205, 200));
moveWindow(Huge) ;
show(Huge,1);
Windows обеспечивает поддержку до 255 кнопок для одного окна или
диалога. Здесь приводится пример окна со множеством кнопок:
WB := new(Window,ThePort,nil,"Say it with
Buttons",&(175,90,600,300));
show(WB,1);
at := newPush(Button, 202, WB, "At");
setCRect(at,&(10, 60, 70, 80) ) ;
moveWindow(at) ;
Объектно-ориентированный пользовательский интерфейс для Windows
show(at,1);
this := newPush(Button, 203, WB, "this");
setCRect(this,&(90,60,150,80) );
moveWindow(this);
show(this,1);
rate := newPush(Button, 204, WB, "rate");
setCRect(rate,&(1 70,60,230,80) ) ;
moveWindow(rate);
show(rate,1);
comma := newPush(Button, 205, WB, ",");
setCRect(comma,&(250,60,265,80));
moveWindow(comma);
show(comma,1);
well := newPush(Button, 205, WB, "we'll");
setCRect(well,&(275,60,320,80) ) ;
moveWindow(well);
show(well,1);
surely := newPush(Button, 205, WB, "surely");
setCRect(surely,&(340,60,400,80));
moveWindow(surely);
show(surely,1);
run := newPush(Button, 202, WB, "run");
setCRect(run,&(10,120,70,140));
moveWindow(run);
show(run,1);
out := newPush(Button, 203, WB, "out");
setCRect(out,&(90,120,150,140));
moveWindow(out);
show(out,1);
of := newPush(Button, 204, WB, "of");
setCRect(of,&(170,120,230,1 40) ) ;
moveWindow(of);
show(of,1);
buttons := newPush(Button, 205, WB, "buttons");
setCRect(buttons,&(250,120,320,140));
moveWindow(buttons);
show(buttons, 1) ;
soon := newPush(Button, 205, WB, "soon");
setCRect(soon,&(340,120,400,1 40) ) ;
moveWindow(soon);
show(soon,1);
■ СОЗДАНИЕ ФАЙЛОВЫХ ДИАЛОГОВ
Файловые диалоги отличаются от обычных большим числом со¬
глашений, встроенных (подразумевающихся) в них. Обычно они
автоматически предоставляют возможности расширения файлов,
компиляции и выдачи списка файлов с окне для списков, в
котором можно скроллировать и выполнять селекцию. Кроме того,
традиционно обеспечивается дополнительное окно, где можно
выполнять селекцию для другого директория или другого диска.
Для создания базисного файлового диалога достаточно следующих
сообщений:
Класс TextFile
185
F := new(FileDialog, "*.*") ;
runModal(F, FILE_BOX, ThePort);
Как вы можете увидеть, этот тип файлового диалога устанавлива¬
ется для загрузки файлов из спискового окна.
■ РАБОЧИЕОБЛАСТИ
Отличный способ увидеть, как в Акторе строится полный пользо¬
вательский интерфейс, состоит в изучении класса WorkSpace
(рабочая область). Его экземпляры используются для общения с
рабочими окнами, опыт использования которых у вас уже имеется.
Рабочие области задуманы как самые первые дочерние окна глав¬
ного окна. Другие дочерние окна главного окна используют метод
sizeRect класса WorkSpace.
Вот как определяется метод loadFile класса WorkSpace:
def loadFile(self)
{ if not(fileDlg)
then fileDlg := new(FileDialog, "*.*");
endif;
if runModal(fileDlg, FILE_BOX, self) == IDOK
then load(loadFile(fileDlg));
endif;
repaint(self);
setFocus(self);
}
Стратегия этой программы состоит в использовании одного и того
же файлового диалога каждый раз, когда она запоминает строку
с самой последней path-спецификацией.
WorkSpace имеет те же тридцать девять экземплярных перемен¬
ных, как и его предок EditWindow.
WorkSpace использует экземплярную переменную fileDlg для за¬
поминания оконного объекта файлового диалога.
■ КЛАСС TEXTFILE
TextFile — это подкласс класса File, который имеет средства для
чтения и записи текстовых буферов. Для того чтобы инициали¬
зировать новый TextFile, вам обычно требуется что-нибудь в этом
роде:
TF := new(TextFile);
setName(TF, "newfile.txt");
create(TF);
open(TF, 1);
Не забывайте, что объекты TextFile — это не окна. Для того чтобы
визуализировать объект TextFile, необходимо использовать окно
какого-нибудь класса, оснащенного средствами для загрузки ви¬
зуализируемых текстовых файлов типа FileEdit. Объекты TextFile
186
Объектно-ориентированный пользовательский интерфейс для Windows
имеют буфер на 512K и могут распознавать симвожл-разделители.
Это позволяет объектам TextFile отслеживать конец текстовой
строки. Все объекты данного вида для этой цели имеют экземп-
лярную переменную, называемую delimiter, в которой хранится
символ-разделитель. Этим обстоятельством пользуется и метод
readLine для того, чтобы определять, как разделяются строки в
тексте.
■ СОЗДАНИЕ ДИНАМИЧЕСКИХ МЕНЮ
Как упоминалось ранее, динамические меню-это такие меню,
которые можно создавать без предварительной компиляции файла
ресурсов. С ними связаны два главных класса Menu и MenuItem.
В меню могут появиться два типа айтемов, те, которые выполняют
некоторые действия, и те, которые открывают popup-меню с новым
списком выбора. Динамические меню запоминаются в экземпляр-
ной переменной menu класса Window.
Класс MenuWindow подходит к созданию приложения с каскад¬
ным меню как к созданию некоторого класса, который будет
заниматься только этой задачей. Это, конечно, не идеальный
способ решения вопроса. Вы можете захотеть использовать эти
специальные типы меню в разнообразных ситуациях. Таким обра¬
зом, придется разбить задачу на несколько функций общего на¬
значения и либо поместить их в отдельный подкласс, либо
вставить их прямо в класс Window. Здесь мы будем использовать
подход с созданием нового класса. Таким образом описание класса
Window остается в целости, и все новые описания будут сосредо¬
точены в документации нового класса.
Рассмотрим простое динамическое меню файлов, которое активи¬
зирует файловый диалог. Построение такого меню состоит из двух
основных этапов. Сначала вы создаете командную строку для
меню, а затем должны создать действие-событие для того, чтобы
активизировать ваш файловый диалог. Для этого вы можете
создать новый класс, называемый FileMenuWindow, который яв¬
ляется подклассом Window. Для нового класса необходимо опре¬
делить новый метод command. Этот метод просто интерпретирует
команды меню, которые должны возбуждать события, и выполня¬
ет действия, ассоциированные с ними. Программа будет очень
простой, если ваш подход будет заключаться только в передаче
сообщения perform для обработки некоторому методу, который
специфицирован, например, так:
Def command(self, wp, lp msg)
{ if msg := action(menu, wp)
then ^perform(self, wp, msg)
endif;
}
Создание динамическихдиалогов
187
Это базис, позволяющий при появлении событий, которые возбуж¬
даются пользователем, работающим с меню, пересылать сообще¬
ния объектам, выполняющим указанные действия. Помимо
метода command для построения динамических меню требуется
метод init. Ниже описан типовой метод init для динамических
меню, который можно использовать как образец, для вашего
приложения его можно скорректировать:
Def init(self pMenu)
{ menu := create(new(Menu), self);
addItem(menu, appPopup(self));
pMenu := new(Popup(MenuItem, "&Menu1");
addItem(pMenu, new(MenuItem,
"&0ption1"+asString(Tab)+"Ctrl+W",
C0MMAND1, #message1)) ;
addItem(pMenu, new(MenuItem,
"&0ption2"+asString(Tab)+"Ctrl+S",
C0MMAND2, #message2) ) ;
addItem(menu, pMenu);
pMenu := new(Popup(MenuItem, "&Menu2");
addItem(pMenu, new(MenuItem, "&0ption1", C0MMAND1,
#message1));
addItem(pMenu, new(MenuItem, "&0ption2", C0MMAND2,
#message2));
addItem(menu, pMenu);
addItem(menu, new(MenuItem, "&Help1", HELP, #help));
do(over(C0MMAND1, C0MMAND2 + 1),
{ using(item) grayMenuItem(menu, item);
}) ; '
drawMenu(self);
}
Естественно, что количество меню и подменю, описанных в данном
образце, может быть произвольным. Строки этого текста можно
удалять или копировать так, как вам требуется.
■ СОЗДАНИЕ ДИНАМИЧЕСКИХ ДИАЛОГОВ
В той же манере, как и динамические меню, можно создавать
динамические диалоги. Для этого используются два главных клас¬
са DlgItem и DialogDesign. Если в вашей системе нет таких
классов, следует их загрузить. Динамические диалоги полезны не
только для интерактивной разработки, но и тогда, когда вы
заранее не можете скомпилировать ресурсы статического диалога.
Для того чтобы создать динамический диалог, выполните данные
сообщения в рабочем окне:
D := new(DialogDesign);
setText(D, "Hard Dialog");
addItem(D, newButton(DlgItem, "Shut", IDOK, 40@40,
32016, 0));
addItem(D, newStatic(DlgItem,"Make Your
Move",100,25020,60@16,0)) ?
setSize(D, 0@0, 110@70);
188
Объектно-ориентированный пользовательский интерфейс для Windows
runModal(D, nil, ThePort);
Теперь попробуем разобраться, что же реадьно можно делать с
динамическими диалогами. Главная экземплярная переменная
класса DialogDesign - это itemColl, которая является коллекцией,
содержащей все дочерние элементы управления в некотором диа¬
логе. Метод-приглашение (prompter) класса DialogDesign исполь¬
зуется для создания динамических диалогов, которые дают
возможность пользователю интерактивно вводить текст в соответ¬
ствующие поля. Элементы управления добавляются к диалогу при
помощи метода addItem, способов добавления к меню много. Метод
addItem обычно используется в сочетании с различными методами
инициализации класса Design. У всех этих методов одинаковый
базисный формат, они запрашивают шесть аргументов:
1) назначение сообщения;
2) текст заголовка, который выдается для данного диалога;
3) целое число Ш;
4) координаты элементов управления в диалоге;
5) размеры элементов управления;
6) типы элементов управления.
Создание очень простого диалога из предыдущего примера можно
усовершенствовать, если использовать класс ErrorBox Q?uc. 6.2).
Загрузите этот класс, если вы еще не сделали этого, и выполните:
E := new(ErrorBox, ThePort, "Make Your Move",
"HardDialog", MB_ICONHAND);
■ ВНЕРЕЖИМНЫЕ ДИАЛОГИ
Внережимный диалог, не зависящий от текущего контекста, —
это диалог, в котором пользователя не заставляют отвечать за
действия других задач, выполнявшихся перед этим. Такой диалог
возможен всегда, когда нужно. Все, что для этого необходимо, —
это использовать runModeless вместо runModeL Для того, чтобы
запустить ’’Hard Dialog” внережимно, выполните:
D := new(DialogDesign);
setText(D, "Hard Dialog");
addItem(D, newButton(DlgItem, "Shut", IDOK, 4040, 3216,
0)) ;
addItem(D, newStatic(DlgItem,"Make Your
Move",100,2520,6016,0));
setSize(D, 00, 11070);
unModeless(D, nil, ThePort);
Внережимные диалоги важны в части приложений, где вы хотели
бы дать пользователю возможность перехватить инициативу и
выполнить какую-либо подзадачу, не принуждая его следовать
жесткой схеме. Они также важны для того, чтобы предохранить
Создание списковьь окон
189
Рис. 6.2. Динамический диалог с пиктограммой, основанный на классе ErrorBox
пользователя от попадания в режим, когда возникает ситуация, в
которой ничего нельзя ответить на запрос и приходится перезаг¬
ружать компьютер.
■ ОБЫЧНЫЙ ДИНАМИЧЕСКИЙ ДИАЛОГ
Способов использовать диалоги бесконечное множество, и поэтому
нет конца многообразию типов диалогов, пригодньгх для той или
иной цели. Например, одно общепринятое использование диалогов
с приглашениями и другими элементами управления должно
позволить пользователю ввести аргументы сообщений. Один из
неплохих вариантов решения этой задачи — определить специаль¬
ный командный метод, который опишет, что нужно сделать с
текстом, введенным пользователем»
■ СОЗДАНИЕ СПИСКОВЫХ ОКОН
Списковые окна — важный тип элементов управления в среде
Windows. Он содержит бесконечное многообразие видов использо¬
вания и может принести важные преимущества, когда проектиро¬
вание выполнено продуманно и качественно. В Акторе класс
ListBox является потомком класса Control, который служит одно¬
190
Объектно-ориентированный пользовательский интерфейс для Windows
временно и образцом для инициализации работающих объектов и
классом, чьи свойства наследуются усовершенствованными эле¬
ментами управления, такими, как комбинированные окошки.
Простое списковое окно можно создать и визуализировать при
помощи сообщения:
WW := new(Window, ThePort, nil, "Window For List Box",
&(275, 60, 500, 200));
show(WW, 1);
L := new(ListBox, 200, WW);
setCRect(L, &(100, 0,200, 80));
moveWindow(L);
addString(L, "Alpha");
show(L, 1);
addString(L, "Beta");
addString(L, "Gamma");
■ СОЗДАНИЕ КОМБИНИРОВАННЫХ ОКОШЕК
Специальный класс ComboBox Q?uc. 6.3) предназначен для создания
комбинированных окошек Актора. Он является подклассом
ListBox, поскольку комбинированные окошки в действительности
являются особым типом списковых окон. Комбинированное окош¬
ко — это особый диалог, который может включать различные
списки и опции выбора. Имеются три различных вида: простой,
ниспадающий и списковый ниспадающий.
WC := (Window, ThwPort, nil, "Window For Combo Box",
&(275, 60, 500, 200));
show(WC, 1);
С := newSimple(ComboBox, IDOK, WC, clientRect(WC));
addString(C, "command.com");
addString(C, "config.sys");
addString(C, "autoexec.bat");
Show (С, 1);
WC := (Window, ThwPort, nil, "Window For Combo Box",
&(275, 60, 500, 200)) ;
show(WC, 1);
С := newDropDownList(ComboBox, IDOK, WC,
clientRect(WC));
addString(C, "happiness");
addString(C, "freedom");
addString(C, "prosperity");
Show(С, 1);
Нажав на стрелку, вы увидите список отсортированных ниспада¬
ющих текстовых фрагментов-айтемов. Хотя этот пример, по-види¬
мому, отработает нормально, для комбинированных окошек,
созданных данным способом, имеется ряд проблем. Вы это заме¬
тите, если будете передвигать окно по экрану. Окно двигается, а
комбинированное окошко стоит на месте. В приложениях, где у
пользователя нет возможности передвигать окно, проблем не воз¬
никает. В случае же приложений, где пользователь может переме-
Создание полосок меню главного окна
191
Рис. 6.3. Простое комбинированное окошко
щать списковые окна, необходимо предусмотреть простой меха¬
низм, который будет автоматически и корректным образом пере¬
мещать списковое окно всегда, когда перемещается его владелец.
■ СОЗДАНИЕ ПОЛОСОК МЕНЮ ГЛАВНОГО ОКНА
В технологии программирования уже давно практикуется метод
прототипирования, когда вместо программы, которую можно до¬
бавить позднее, используется заглушка или пустуя функция. Эта
техника подходит и для разработки главной полоски в пользова¬
тельском интерфейсе Windows. В следующих двух разделах я
приведу два примера, где все меню и их айтемы создаются, но
действия айтемов представляются лишь заглушками, которые не
делают ничего, кроме открытия диалога для того, чтобы показать,
куда будут направлены следующие сообщения. Ниже приводится
пример главного инициирующего сообщения для некоторого ги¬
потетического приложения, связанного с деловой графикой.
Def init(self pMenu)
{ menu := create(new(Menu(, self);
pMenu := newPopup(MenuItem, "&File");
addItem(pMenu, new(MenuItem, "&Load" + asString(Tab) +
"Ctrl+W", 100, #load) ) ;
192
Объектно-ориентированный пользовательский интерфейс для Windows
additem(pMenu, new(MenuItem, "&Save" + asString(Tab) +
"Ctrl+S",100, #save));
addItem(menu, pMenu);
pMenu := newPopup(MenuItem, &Charts");
addItem(pMenu, new(MenuItem, "Vertical", 102,
#drawChart));
addItem(pMenu, new(MenuItem, "Horizontal", 103,
#drawChart));
addItem(pMenu, new(MenuItem, "Pie Chart", 104,
#drawChart));
addltem(menu, pMenu);
addItem(menu, chartsPopup(self));
addltem(menu, new(MenuItem, "&Help", HELP, #help));
}
Def chartsPopup(self popup)
{ popup := new(Popup(menuItem, "&Options");
addItem(popup, stylePopup(self));
addItem(popup, new(MenuItem, "Brushes", 206, #brushes));
addItem(popup, new(MenuItem, "Colors", 207, #palette));
Apopup
}
Def stylePopup(self popup)
{ popup := newPopup(MenuItem, "&Style");
addItem(popup, new(MenuItem, "Dashed", 208, #style));
addItem(popup, new(MenuItem, "Datted", 209, #style));
addItem(popup, new(MenuItem, "Spray", 210, #style));
^popup
}
Def command(self, wp, lp msg)
{ if msg := action(menu, wp)
then Aperform(self, wp, msg)
endif;
}
Методы Stub для этого примера используют несложные диалоги с
окнами для ошибок для того, чтобы произвести некоторое дейст¬
вие, когда выбирается какой-либо пункт меню, и информировать
пользователя о том, что должно произойти, когда программа
закончится. Эти метод приводятся ниже:
Def brushes(self, wp)
{
errorBox(getString(menu, wp), "Brushes dialog goes
here")?
}
Def drawChart(self, wp)
{
errorBox(getString(menu, wp), "Chart will go here");
}
Def help(self, wp)
{
errorBox(getString(menu, wp), "Help dialog goes here");
}
Простой контроллер исполнения
193
Def load(self, wp)
{
errorBox(getString(menu, wp), "Load routine goes here");
}
Def palette(self, wp)
{
errorBox(getString(menu, wp), "Palette dialog goes
here");
}
Def save(self, wp)
{
errorBox(getString(menu, wp), "Save routine goes here");
}
Def style(self, wp)
{
errorBox(getString(menu, wp), "Style dialog goes
here");
}
После того, как вы введете эти методы в класс ClassWindow,
выполните их в рабочей области.
W := defaultNew(ChartWindow, "Chart Sampler");
show(W, 1);
■ ПРОСТОЙ КОНТРОЛЛЕР ИСПОЛНЕНИЯ
Следующий пример (листинг 1) подобен предыдущему, но предо¬
ставляет начальный прототип для более развитого приложения,
которое претерпит еще несколько изменений, пока не приобретет
окончательный вид в гл. 9. Здесь нас интересует лишь дизайн
главной полоски меню и, как и в предыдущем примере, для
фрагментов, описывающих собственно действия, пока они еще не
реализованы, используются заглушки.
Здесь приводится листинг файла класса, потому что этот класс
реально не содержит никакого нового материала. Методы, исполь¬
зующиеся здесь, уже знакомы нам, и теперь они применяются для
того, чтобы построить прототип приложения, которое сможет
принести определенную пользу.
Листинг 1. Пользовательский интерфейс версии ExecWindow
(заглушка для меню)
Класс ExecWindow
(только пользовательский интерфейс)
194
Объектно-ориентированный пользовательский интерфейс для Windows
inherit(Window, #ExecWindow, #(actions fileDlg), 2, nil))!!
now(class(ExecWindow))!!
/* Метод для иерархического меню для вызова графических
программ */
Def graphPopup(self popup)
{ popup := new(Popup(MenuItem, "&Graphics");
addItem(popup, new(MenuItem, "Zing”, 208, #type));
addItem(popup, new(MenuItem, "Windows CAD", 209,
#type));
addItem(popup, new(MenuItem, "Designer", 210, #type));
addItem(popup, new(MenuItem, "Object Draw", 211,
#type));
Apopup
}!!
/* Метод для иерархического меню для вызова утилит */
Def utilityPopup(self popup)
{ popup := new(Popup(MenuItem, "&Utilities");
addItem(popup, new(MenuItem, "Norton", 217, #type));
addItem(popup, new(MenuItem, "Xtree Gold", 218, #type));
addItem(popup, new(MenuItem, "Archive", 219, #type));
addItem(popup, new(MenuItem, "Other", 220, #type));
Apopup
}!!
/* Метод для иерархического меню для работы с системами
обработки текстов */
Def wpPopup(self popup)
{ popup := new(Popup(MenuItem, "&Word Processors");
addItem(popup, new(MenuItem, "Word for Windows", 208,
#type));
' addItem(popup, new(MenuItem, "Ami", 209, #type));
Apopup
}!!
/* Метод-заглушка для незавершенных команд меню */
Def stub(self, wp)
{
errorBox(getString(menu, wp),
”Не теряй времени и замени это на что-нибудь стоящееП;
}
» i
/* Метод-заглушка для незавершенных команд меню */
Def type(self, wp)
{
errorBox(getString(menu, wp),
”А теперь, представьте себе, программа выполняется1’);
}!!
/* Метод для иерархического меню для работы с электронными
таблицами */
Def ssPopup(self popup)
{ popup := new(Popup(MenuItem, "&Spreadsheets");
addItem(popup, new(MenuItem, "Excel", 208, #type));
addItem(popup, new(MenuItem, "Zing", 209, #type));
addItem(popup, new(MenuItem, "Quattro Pro", 210,
#type));
A popup
Простой контроллер исполнения
195
}!!
/* Метод для иерархического меню для работы с различными
программными системами */
Def miscPopup(self popup)
{ popup := new(Popup(MenuItem, "&Miscellaneous");
addItem(popup, new(MenuItem, "Toolbook", 208, #type));
addItem(popup, new(MenuItem, "Project", 209, #type));
addItem(popup, new(MenuItem, "Almanac", 210, #type));
Apopup
}П
/* Метод для загрузки файлов */
Def fileLoad(self, wp newfileName)
{ if not(fileDlg)
then fileDlg := new(FileDialog, ".");
endif;
if runModal(fileDlg, FILE_BOX, self) == IDOK
then load(loadFile(fileDlg));
endif;
tepaint(self)?
setFocus(self) ;
} M
/* Метод для иерархического меню для работы с системами
программирования */
Def develPopup(self popup)
{ popup := new(Popup(MenuItem, *'&Development Tools");
addItem(popup, new(MenuItem, "Actor", 212, #type));
addItem(popup, new(MenuItem, "С++ Views", 213, #type));
addItem(popup, new(MenuItem, "Spy", 214, #type))?
addItem(popup, new(MenuItem, "Heapwalker", 215, ^type));
addItem(popup, new(MenuItem, "Zoomin", 216, #type));
Арорир
}П
/* Метод для иерархического меню для работы
с приложениями */
Def appsPopup(self popup)
{ popup := new(Popup(MenuItem, "A&pplications");
addItem(popup, wpPopup(self));
addItem(popup, ssPopup(self));
addItem(popup, graphPopup(self));
addItem(popup, miscPopup(self));
addItem(popup, utilityPopup(self));
addItem(popup, develPopup(self));
^popup
}!!
/* Метод-заглушка для незавершенных команд меню */
Def save(self, wp)
{
errorBox(getString(menu, wp), "Работает программа Save”);
}
i i
Def help(self, wp)
{
errorBox(getString(menu, wp),
”Здесь идет диалог для подсказок (Help)”);
}
196
Объектно-ориентированный пользовательский интерфейс для Windows
! !
/* Инициализирует новое окно с динамическим
иерархическим меню */
Def init(self pMenu)
{ menu := create(new(Menu), self);
pmenu := newPopup(MenuItem, "&File");
addItem(pMenu, new(MenuItem, ’’&Load" + asString(Tab) +
"Ctrl+W",100, #fileLoad));
addItem(pMenu, new(MenuItem, "&Save" + asString(Tab) +
"Ctrl+S",101, #save));
addItem(menu, pMenu);
pmenu := newPopup(MenuItem, "&Accessories");
addItem(pMenu, new(MenuItem, "Calculator", 102, #stub));
AZ
addItem(pMenu, new(MenuItem, "Clock", 103, #stub));
addItem(pMenu, new(MenuItem, "Notepad", 104, #stub));
addItem(pMenu, new(MenuItem, "Control Panel", 105,
#stub));
addItem(pMenu, new(MenuItem, "Cardfile", 106, #stub));
addItem(pMenu, new(MenuItem, "Calendar", 107, ^stub));
addItem(pMenu, new(MenuItem, "Write", 108, #stub));
addItem(pMenu, new(MenuItem, "Painbrush", 109, ^stub));
addItem(pMenu, new(MenuItem, "Recorder", 110, #stub));
addItem(menu, pMenu);
addItem(menu, appsPopup(self));
addItem(menu, new(MenuItem, "&Help!", 99, #help));
drawMenu(self);
}
» i
/* Отвечает за события меню.Аргумент wp дает выбранный
идентификатор меню Ш.Берет символ сообщения из объекта-меню */
Def command(self, wp, ip msg)
{ if msg := action(menu, wp)
then Aperform(self, wp, msg)
endif;
} !!
■ НЕКОТОРЫЕ ИЗВЛЕЧЕНИЯ ИЗ ПРИМЕРА
РЕСУРСНОГО ФАЙЛА
Данный раздел (листинг 2) содержит некоторые извлечения из
примера ресурсного скрипт-файла (файла описания), который
используется при вызове среды Актора. Он будет особенно полезен
при изучении класса ActorApp.
Листинг 2. Пример синтаксиса ресурсного сценария.
#include "style.h"
#include "actor.h"
#include "track.h"
#include "demos.h"
work ICON work.ico
Browser ICON browser.ico
FileWindow ICON filewind.ico
Некоторые извлечения из примера ресурсного файла
Inspector ICON inspect.ico
cube DATA cube. dot
700 BITMAP actlogo.bmp
701 BITMAP act30.bmp
702 BITMAP acttext1.bmp
703 BITMAP acttext2.bmp
704 BITMAP acttext3.bmp
705 BITMAP acttext4.bmp
706 BITMAP acteye.bmp
Actor ACCELERATORS
BEGIN
VK_INSERT, EDIT_PAST, VIRTKEY
VK_DELETE, EDIT_CUT, VIRTKEY
VK_SUBTRACT, EDIT_CUT, VIRTKEY
VK_ADD, EDIT_COPY, VIRTKEY
VK_LEFT, VK_LEFT, VIRTKEY
VK_UP, VK_UP, VIRTKEY
VK_RIGHT, VK_RIGHT, VIRTKEY
VK_DOWN, VK_DOWN, VIRTKEY
,,Aa", EDIT_SELALL
"AgM, BR_CGOTO
"^r", BR_REFORM
,,Az", BR_ZOOM
VK_TAB, EDIT__TAB, VIRTKEY
VK_PRIOR, EDIT_PRIOR, VIRTKEY
VK_NEXT, EDIT_NEXT, VIRTKEY
VK_HOME, EDIT_HOME, VIRTKEY
VK_END, EDIT_END, VIRTKEY
VK_F1, VK_F1, VIRTKEY
VK_F2, VK_F2, VIRTKEY
VK_F3, VK_F3, VIRTKEY
VK_F4, VK_F4, VIRTKEY
VK_F5, VK_F5, VIRTKEY
VK_F6, VK_F6, VIRTKEY
VK_F7, VK_F7, VIRTKEY
VK_F8, VK_F8, VIRTKEY
VK_F9/ VK_F9, VIRTKEY
VK_F11, VK_F11, VIRTKEY
VK_F12, VK_F12, VIRTKEY
VK_DELETE, EDIT_CUT, VIRTKEY, SHIFT
VK_INSERT, EDIT_COPY, VIRTKEY, CONTROL
VK_INSERT, EDIT_PASTE, VIRTKEY, SHIFT
END
ABOUT_BOX DIALOG DISCARDABLE 59, 79, 151, 128
STYLE WS_POPUP WS_DLGFRAME
BEGIN
CTEXT '*Actor\256 3.0” -1, 1, 12, 147, 10
CTEXT '*Copyright\251 1986-1990" -1 , 1, 28, 147, 10
CTEXT "The Whitewater Group, Inc." -1, 1, 39, 147, 10
CTEXT "All rights reserved." -1, 1, 50, 147, 10
ICON "work" 5, 24, 98, 13, 17
ICON "browser" 6, 114, 98, 13, 17
CTEXT "Portions Copyright\251 1987-1990" -1, 1, 68,
147, 10
CTEXT "Microsoft Corporation", -1, 1, 79, 147, 10
198
Объектно-ориентированный пользовательский интерфейс для Windows
DEFPUSHBUTTON "OK"IDOK, 57, 99, 32, 14, WS_GROUP
END
INPUT_BOX DIALOG DISCARDABLE 77, 94, 165, 71
STYLE WS_BORDER WS_CAPTION WS_DLGFRAME WS_POPUP
BEGIN
EDITTEXT FILE_EDIT, 10, 32, 138, 12, WS_BORDER
WS_CHILD
WS_TABSTOP ES_AUTOHSCROLL
LTEXT "", INPUT_MSG, 11, 5, 143, 18, WS_CHILD
DEFPU SHBUTTON "&OK" IDOK, 32, 50, 32, 14, WS_CHILD
PUSHBUTTON "&Cansel", IDCANSEL, 99, 50, 32, 14, WS_CHILD
END
REPLACE_BOX DIALOG DISCARDABLE 77, 94, 165, 85
STYLE WS_BORDER WS_CAPTION WS_DLGFRAME WS_POPUP
BEGIN
CONTROL M,\ FILE_EDIT, "EDIT", WS_CHILD WS_VISIBLE
WS_BORDER WS_TABSTOP 0x80L, 10, 16, 138, 12
CONTROL "", RPLC_EDIT, "EDIT”, WS_CHILD WS_VISIBLE
WS_BORDER WS_TABSTOP 0x80L, 10, 46, 138, 12
CONTROL "", INPUT_MSG, "STATIC", WS_CHILD WS_VISIBLE
WS_GROUP, 11, 5, 143, 9
CONTROL "Replace with", 501, "STATIC", WS_CHILD
WS_VISIBLE
WS_GROUP, 11, 34, 143, 9
CONTROL "&OK", IDOK, "BUTTON", WS_CHILD WS_VISIBLE
WS_TABSTOP 0x1L, 10, 64, 32, 14
CONTROL "&Replace All", RPLC_ALL, "BUTTON", WS_CHILD
WS_VISIBLE WS_TABSTOP 48, 64, 64, 14
CONTROL "&Cansel", IDCANSEL, "BUTTON", WS_CHILD
WS_VISIBLE WS_TABSTOP, 117, 64, 32, 14
END f
ERR_BOX DIALOG DISCARDABLE 48, 32, 210, 85
STYLE WS POPUP WS_CAPTION
CAPTION ^Error Dialog"
BEGIN
DEFPU SHBUTTON "&OK" IDOK, 172, 8, 28, 14, WS_GROUP
PUSHBUTTON "&Debug", IDYES, 172, 28, 28, 14, WS_GROUP
LISTBOX ERR_LB, 4, 8, 160, 70
END
DW_BOX DIALOG DISCARDABLE 27, 27, 201, 105
STYLE WS_DLGFRAME WS_POPUP
BEGIN
LTEXT "The text in Browser edit window has been" 2,
10, 11, 180, 10
LTEXT "changed. Accept or Cut to Clipboard?" 3, 10,
24, 150, 10
PUSHBUTTON "&Accept", DW_ACC, 10, 47, 75, 14, WS_CHILD
PUSHBUTTON "Cut to C&lipboard", DW_CTC, 10, 74, 75,
14, WS_CHILD
DEFPU SHBUTTON "A&bandon”, DW_ABA, 1 10, 47, 75, 14,
WS_CHILD
PUSHBUTTON "&Cansel", IDCANSEL, 110, 74, 75, 14, WS_CHILD
END
FILE_BOX DIALOG DISCARDABLE 27, 23, 170, 116
STYLE WS DLGFRAME w£ POPUP DS ABSALIGN
Некоторые извлечения из примера ресурсного файла
199
BEGIN
CONTROL мм FILE_LB, "ComboBox", CBS_SIMPLE CBS_SORT
WS_VSCROLL WS_NABSTOP WS_CHILD, 4, 30, 55, 80
CONTROL "Files:" 3, "static”, SS_LEFT WS_CHILD, 4, 19
31 , 10
CONTROL "" FILES_DIRLB, "ListBox", LDS_STANDARD
WS_TABSTOP
WS_CHILD, 65, 42, 55, 68
CONTROL "Directories:" 3, "static", SS_LEFT WS_CHILD,
65, 31, 38, 10
DEFPUSHBUTTON "&Open", IDOK, 130, 37, 30, 15, WS_CHILD
PUSHBUTTON "&Cansel", IDCANSEL, 130, 63, 30, 15,
WS_CHILD
CONTROL "Directory:" 3, "static", SS_LEFT WS_child, 4
7, 32, 11
CONTROL "" FILE_DIR, "static", SS_LEFT WS_CHILD, 39,
7, 146, 11
END
MBrowMenu MENU
BEGIN
MENUITEM "&Accept!", BR_ACCEPT
POPUP "&Edit"
BEGIN
MENUITEM "Cu&t\tShift+Del", EDIT_CUT
MENUITEM "&Copy\tCtrl+Ins", EDIT_COPY
MENUITEM "Paste\tShift+Ins", EDIT_PASTE
MENUITEM "C&lear", EDIT_CLEAR
MENUITEM SEPARATOR
MENUITEM "Select &All\tCtrl+A", EDIT_SELALL
MENUITEM "&Reformat\Ctrl+R", BR_REFORM
END
POPUP "&Search"
BEGIN
MENUITEM "&Find...", EDIT_SRCH
MENUITEM "Find &Next\tF3", VK_F3
MENUITEM "&Replace...", EDIT_RPLC
END
MENUITEM "&Doit", INSP_DOIT
MENUITEM "&Inspect!", INSP_ISEL
MENUITEM "&Browse!", WORK_BROWSE
POPUP "&Utility
BEGIN
MENUITEM "&Implementors", WORK_IMP
MENUITEM "&Senders", WORK_SYMSEND
MENUITEM "Window Routine Senders", WORK_WINDSEND
MENUITEM "&Global References", WORK_GLOSEND
MENUITEM "&References", WORK_SEND
END
POPUP "&Templates"
BEGIN
MENUITEM "&do", TEMP_DO
MENUITEM "&if/then", TEMP_IF
MENUITEM "if/&else", TEMP_IFEL
MENUITEM "&block", TEMP_BLOCK
MENUITEM "&select/case", TEMP_CASE
MENUITEM "&loop", TEMP_LOOP
MENUITEM SEPARATOR
200
Объектно-ориентированный пользовательский интерфейс для Windows
MENUITEM "&New method", TEMP_NMETH
END
END
■ ДИРЕКТОРИИ DOS
Работа с директориями DOS и именами маршрутов (pathname) в
Windows не такая простая, как можно было бы ожидать, посколь¬
ку в Windows нет функций для непосредственного обеспечения
этих работ. Хотя имеется функция GetSystemDirectory, но по
некоторым причинам нет соответствующей функции
SetSystemDirectory. Функция DlgDirListComboBox является спе¬
циальной функцией, работающей только со специальными файло¬
выми диалогами. В классах, которые поставляются с Актором, нет
непосредственной поддержки для прямого изменения маршрута.
Метод setFileSpec класса FileDialog предназначен для использова¬
ния только с объектами класса FileDialog. Однако имеются неко¬
торые общедоступные классы Public Domain, которые начинал
разрабатывать Марк Солински и значительно расширил Джеймс
Xoy. Классы вполне отвечают требованиям для выполнения этих
работ. Поскольку они будут использоваться в нескольких после¬
дующих примерах, я здесь приведу эти классы (см. листинги 3-6).
Для работы с директориями и маршрутами имеются два класса:
FileString и Directory. Кроме того существует одна программа,
которая улучшает класс Актора SortedCollection<,
В классе Directory полное имя хранится в двух экземплярных
переменных: diskDrive, которая хранит букву — имя дискового
драйвера, и pathName, которая хранит оставшуюся часть марш¬
рута, без имени драйвера.
Directory широко использует методы класса (т.е. не экземпляр-
ные. — Прим. nepee.).
fullName (current(Directory));
"C:\Actor"
Dir := current(Directory);
а Directory
fullName(Dir);
"C:\Actor"
setDiskDrive(Dir, "D");
a Directory
setPathName(Dir,11/") ;
a Directory
fullName(Dir);
"D:/"
Директории DOS
201
Листинг 3. SYSCHNG.ACT
/* Системные изменения для поддержки класса Directory
**
** Модифицировано: Джеймсом В. Xoy
** Дата:0б/30/1988
*/!!
add(Constants, #DOS_OFFSET, 700);!!
now(DosStruct)!!
/* Взять длинный указатель и разместить значение в
корректных регистрах сегмента и смещения */
Def setPtr(self lPtr, Segment, Offset)
{
putWord(self, high(lPtr), Segment);
putWord(self, low(lPtr), Offset);
}!!
now(Struct)!!
/* Дается размещение объекта в памяти в соответствии
со структурой: размер данные */
Def fillStruct(self, lpMem)
{
do(overBy(0, size(self), 2),
{
using(i) putWord(self, wordAt(lp(Mem + asLong(i)), i);
});
Aself;
>!!
/*
** Изменения в системе, внесенные для класса
** SortedCollection. Эти изменения позволяют
** записать вид сортировки блоков при создании
** экземпляра, а не при помощи отдельного шага.
** Новый метод используется так же, как и раньше.
** Новый метод** ”sortBlock” в дополнение к
** параметру size получает параметр
** "сортировка блоков”.
**
** Модифицировано: Джеймсом В. Xoy
** Дата:06/30/1988
*/!!
now(SortedCollectionClass)!!
/* Выдать новый объект-SortedCollection,
используя метод сортировки, выбираемый по умолчанию.
Этот метод производит сравнение блоков непосредственно,
не обращаясь к методу init. */
Def new(self, size aCollection)
{
aCollection := init(variableNew(self : Behavior, size));
aCollection.compareBlock := { using(item1, item2) iter^
item2 };
AaCollection
} ! •
now(SortedCollectionClass)!!
202 Объектно-ориентированный пользовательский интерфейс для Windows
/* Выдать новый объект-SortedCollection,
используя указанный метод сортировки */
Def sortBlock(self, size, sortBlock aCollection)
{ aCollection := init(variableNew(self : Behavior,
size));
aCollection.compareBlock := sortBlock;
^aCollection
} !!
now(SortedCollection)!!
/* Инициализирует объект SortedCollection.
Замечание: Этот метод теперь не записывает
сортировку блоков, используемую по умолчанию.
Она запоминается в новом методе для класса */
Def init(self)
{
firstElement := lastElement := 0;
} !!
Листинг 4. Файл класса FileString: FILESTRI.CLS
/* Этот класс используется для разбора строки,
в которой содержится имя файла */ !!
inherit(Object, #FileString, #(fileString), 2, nil)!!
now(FileStringClass)!!
/* Выдать новый объект FileString, используя
в качестве его имени строку из параметра */
Def new(self, aString)
{
^setName(new(self : Behavior), aString);
} '!!
now(FileString)!!
/* Выдать спецификацию маршрута, содержащуюся в fileString. Она
не включает в себя имя дискового
драйвера и имя файла. Этот метод предполагает, что вся
информация, следующая за именем дискового драйвера,
определяет только маршрут */
Def asPath(self startPos)
{
startPos := indexOf(fileString, ':', 0);
if startPos
startPos := startPos;
else
startPos := -1;
endif;
^subString(fileString, startPos + 1,
size(fileString) + 1);
} !!
/* Дописать aString в качестве имени файла в fileString.
Это бывает полезно, когда текущий fileString содержит лишь указа¬
ние на директорий */
Def appendFile(self, aString)
{
if at(fileString, size(fileString) " 1) = '\'
fileString := fileString + aString;
Директории DOS
203
else
fileString := fileString + "\" + aString;
endif;
} !!
/* Выдать расширение файла, содержащееся в fileString. Оно не
будет содержать имени драйвера диска,
спецификации маршрута и имени файла */
Def fileExtention(self fileSpec, dotPos)
{
fileSpec := fileSpec(self);
dotPos := indexOf(fileSpec, '.', 0);
if not(dotPos)
Л M If .
/
else
^sudString(fileSpec, dotPos = 1, size(foleSpec) + 1);
endif;
} !!
/* Выдать спецификацию файла, содержащуюся в fileString.
Она не включает имени дискового драйвера или
спецификации маршрута — только имя файла
и его расширение */
Def fileSpec(self curSlash, lastSlash, startPos)
{
startPos := indexOf(fileString, ":", 0);
if startPos
startPos := startPos;
else
startPos := -1
endif;
lastSlash := startPos;
loop
while (curSlash := indexOf(fileString, '\',
lastSlash+1))
lastSlash := curSlash;
endLoop;
^subString(fileString, lastSlash+1,
size(fileString) + 1);
} 11
/* Выдать содержимое fileString */
Def fileString(self)
{
^fileString
}
/* Выдать имя файла, содержащееся в fileString.
Оно не содержит ни имени дискового драйвера,
ни спецификации маршрута, ни расширения */
Def fileName(self fileSpec, dotPos)
{
fileSpec := fileSpec(self);
dotPos := indexOf(fileSpec, '.', 0)?
if not(dotPos)
^fileSpec;
else
AsubString(fileSpec, 0, dotPos);
endif;
204
Объектно-ориентированный пользовательский интерфейс для Windows
} !!
/* Выдать спецификацию маршрута, содержащуюся в fileString.
Оно не содержит ни имени дискового драйвера, ни имени файла */
Def pathSpec(self curSlash, lastSlash, startPos)
{
startPos := indexOf(fileString, ”:", 0);
if startPos
startPos := startPos;
else
startPos := -1
endif;
lastSlash := startPos;
loop
while (curSlash := indexOf(fileString, '\',
lastSlash+1))
lastSlash := curSlash;
endLoop;
AsubString(fileString, startPos, lastSlash+1);
} !!
/* Выдать строку, содержащую букву дискового драйвера,
вслед за которой стоит двоеточие. Если в fileString нет буквы
драйвера, то вернуть пустую строку */
Def diskDrive(self)
{
if indexOf(fileString, ':', 0) = 1
AsubString, 0, 2);
else
А П I» .
/
endif;
} !!
/* Присвоить переменной fileString значение aString */
Def setName(self, aString)
{
fileString := aString);
} !!
Листинг 5. Файл класса Directory: DIRECTOR.CLS
/************************************************************
**
** Объект Directory представляет директорий с указанной
** буквой драйвера и спецификацией маршрута. Методы,
** используемые этим объектом, наследуются из класса DirStuff,
** разработанного Марком Солинским йз Whitewater Group.
** Public Domain класс, автор: Джеймс В. Xoy
** Дата: 06/29/1988
************************************************************^
inherit(Object, #Directory, #(diskDrive
/* Буква дискового драйвера */
pathName /* не включает драйвера */) t 2, nil) ! !
now(DirectoryClass)!!
Директории DOS
205
/* Выдать новый объект-директорий, соответствующий
указанному имени файла */
Def pathName(self, aString theDir, aFileString)
{
aFileString := new(FileString, aString);
theDir := new(Directory);
theDir.pathName := asPath(aFileString);
theDir.diskDrive(aFileString);
if diskDrive(theDir) = ""
theDir.diskDrive ;+ currentDisk(self);
endif
AtheDir;
} !!
/* Выдать полную спецификацию маршрута, включающую
букву дискового драйвера для текущего директория */
Def fullName(self)
{
AcurrentDisk(self) + currentPath(self);
} !i
/* Сменить директорий. */
Def makeCurrent(self, aPathName aDTA, ds, lpPN, result)
{
aDTA := getDTA(Directory);
ds := new(DosStruct);
lpPN := lP(aPathName);
fill(ds, 0);
setPtr(ds, lpPN, DOS DS, DOS_DX);
setCall(ds, ОхЗВ); /^"функция DOS CHDIR */
call(ds) ;
result := getError(ds);
if result 0
errorBox("Dos Error", loadString(DOS_OFFSET+result));
endif;
freeHandle(aPathName);
} !!
/* Удалить указанный директорий */
Def remove(self, aPathName aDTA, ds, lpPN, result)
{
aDTA := getDTA(Directory);
ds := new(DosStruct);
lpPN := lP(aPathName);
fill(ds, 0);
setPtr(ds, lpPN, DOS_DS, DOS_DX);
setCall(ds, 0x3A); /* функция DOS RMDIR */
call(ds) ;
result := getError(ds);
if result 0
errorBox("Dos Error”, loadString(DOS_OFFSET+result));
endif;
freeHandle(aPathName);
} !!
206
Объектно-ориентированный пользовательский интерфейс для Windows
/* Создать указанный директорий */
Def create(self, aPathName aDTA, ds, lpPN, result)
{
aDTA := getDTA(Directory);
ds := new(DosStruct);
lpPN := lP(aPathName);
fill(ds, 0);
setPtr(ds, lpPN, DOS_DS, DOS_DX);
setCall(ds, 0x39); /* функция DOS MKDIR */
call(ds);
result := getError(ds);
if result 0
errorBox("Dos Error", loadString(DOS_OFFSET+result));
endif;
freeHandle(aPathName);
} !!
/* Выдать отсортированную коллекцию файлов для
указанного маршрута*/
Def filesOf(self, dir, fileStr, mask | ds, lpPN, aDTA,
files, result,
aPathName, aFileString, anArray, errors)
{
files := sortBlock(SortedCollection, 1,
{using(elem1,elem2) at(elem1, 0)at(elem2, 0()});
ds := new(DosStruct);
aDTA := getDTA(self);
fileStr := if fileStr then
fileStr;
else
»★ ★".
• t
endif;
mask := if mask then
mask;
else
0x0;
endif;
aFileString := if dir then
new(FileString, dir);
else
new(FileString, currentDisk(self)
+ currentPath(self));
endif;
aPathName := fileString(appendFile(aFileString,
fileStr));
lpPN := lP(aPathName);
fill(ds, о);
setPtr(ds, lpPN, DOS_DS,DOS_DX);
putWord(ds, mask, DOS_CX);
setCall(ds, 0x4E);
call(ds);
getError(ds);
errors := new(Set, 2);
add(errors, 2);
add(errors, 18);
Директории DOS
207
loop result := wordAt(ds, DOS_AX);
while not(result in errors)
begin
anArray := new(Array, 2);
put(anArray,
removeNulls(setClass(fillStruct(new(Struct, 13),
aDTA+30L),String)), 0);
put(anArray, (wordAt(aDTA+21L) bitAnd 0xFF), 1);
add(files, anArray);
fill(ds, 0);
setPtr(ds, lpPN, DOS_DS,DOS_DX);
putWord(ds, mask, DOS_CX);
setCall(ds, 0x4F); /* функция DOS Find Subsequent */
call(ds);
getError(ds);
endLoop;
freeHandle(aPathName);
^files;
} !!
/* Выдать отсортированную коллекцию директориев для
указанного маршрута*/
Def subdirectoriesOf(self, dir, fileStr | ds, lpPN,
aDTA, subdirs,
aPathName, aFileString, errors)
{
subdirs := new(SortedCollection, 1);
ds.:= new(DosStruct);
aDTA := getDTA(self);
fileStr := if fileStr then
fileStr;
else
»★ ★».
• /
endif;
aFileString := if dir then
new(FileString, dir);
else
new(FileString,
currentDisk(self) + currentPath(self));
endif;
aPathName := fileString(appendFile(aFileString, fileStr))
lpPN := lP(aPathName);
fill(ds, 0);
setPtr(ds, lpPN, DOS_DS,DOS_DX);
putWord(ds, 0x10, DOS_CX);
setCall(ds, 0x4E);
call(ds);
getError(ds);
errors := new(Set, 2);
add(errors, 2);
add(errors, 18);
loop result := wordAt(ds, DOS_AX);
while not(result in errors)
begin
208 Объектно-ориентированный пользовательский интерфейс для Windows
if{wordAt(aDTA+21L) bitAnd 0xFF) = 16 /* Директорий */
add(subdirs,
removeNull(selfClass(fillStruct(new(Struct, 13)
aDTA+30L), String));
endif;
fill(ds, 0);
setPtr(ds, lpPN, DOS_DS,DOS_DX);
putWord(ds, 0x10, DOS_CX);
setCall(ds, 0x4F); /* функция DOS Find Subsequent */
call(ds);
getError(ds);
endLoop;
freeHandle(aPathName);
Asubdirs;
} !!
/* Выдать значением DTA по умолчанию */
Def getDTA(self | ds)
{
ds := new(DosStruct);
setCall(ds, 0x2F);
call(ds);
getError(ds);
^pack(wordAt(ds, DOS_BX), wordAt(ds, DOS_ES))
} !!
/* Выдать новый объект-директорий, соответствующий
текущему директорию */
Def current(self | theDir)
{
theDir := new(Direotor^);
theDir.pathName := currentPath(self);
theDir.diskDrive := currentDisk(self);
^theDir;
} !!
/* Выдать метку тома для диска, содержащего текущий
директорий */
Def volumeLabel(self | ds, lpPN, aDTA, result,
volumeLabel,
aPathName, aFileString, errors)
{
ds := new(DosStruct);
aDTA := getDTA(self);
aFileString := new(FileString, fullName(self));
aPathName := fileString(appendFile(aFileString, "*.*"));
lpPN := lP(aPathName);
fill(ds, 0);
setPtr(ds, lpPN, DOS_DS,DOS_DX);
putWord(ds, 0x8, DOS_CX);
setCall(ds, 0x4E);
call(ds);
getError(ds) ;
errors := new(Set, 2);
add(errors, 2) ;
Директории DOS
209
add(errors, 18);
result := wordAt(ds, DOS_AX);
if not(result in errors) then
volumeLabel :=
removeNulls(setClass(fillStruct(new(Struct, 3),
aDTA + 30L), String));
else
volumeLabel := nil;-
endif;
freeHandle(aPathName);
AvolumeLabel;
} П
/* Выдать строку, содержащую маршрут текущего директория */
Def currentPath(self | ds, lpDir, aTC, aStr, bStr)
{
ds := new(DosStruct);
fill(ds, 0);
lpDir := Lp(aStr := new(String, 128));
setPtr(ds, lpDir, DOS_DS,DOS_SI);
putWord(ds, 0x8, DOS_CX);
setCall(ds, 0x47);
call(ds);
bStr := "\" + removeNulls(getText(Handles[aStr]));
freeHandle(aStr);
getError(ds);
’AbStr;
} !!
/* Выдать букву драйвера диска.*/
Def currentDisk(self | ds)
{
ds := new(DosStruct);
setCall(ds, 0x19);
call(ds);
getError(ds);
AasString(asChar(atLSB(ds, DOS_AX) + asInt('A'))) + ":"
} !!
now(Directory)!!
/* Выдать отсортированную коллекцию файлов для self */
Def filesOf(self, fileStr, mask | ds, lpPN, aDTA,
files, result, aFileString,
aPathName, anArray, errors)
{
files := sortBlock(SortedCollection, 1,
{using(elem1,elem2) at(elem1, 0)at(elem2, 0()});
ds := new(DosStruct);
aDTA := getDTA(Directory);
fileStr := if fileStr then
fileStr;
else
»«* *".
• /
endif;
mask := if mask then
mask;
210
Объектно-ориентированный пользовательский интерфейс для Windows
else
0x0;
endif;
aFileString := new(FileString, fullName(self));
aPathName := fileString(appendFile(aFileString,
fileStr) ) ;
lpPN := lP(aPathName);
fill(ds, 0);
setPtr(ds, lpPN, DOS_DS,DOS_DX);
putWord(ds, mask, DOS_CX);
setCall(ds, 0x4E);
call(ds);
getError(ds);
errors := new(Set, 2) ;
add(errors, 2);
add(errors, 1 8);
loop result := wordAt(ds, DOS_AX);
while not(result in errors)
begin
anArray := new(Array, 2);
put(anArray,
removeNulls(setClass(fillStruct(new(Struct, 13),
aDTA+30L), String)), 0);
put(anArray, (wordAt(aDTA+21L) bitAnd 0xFF), 1);
add(files, anArray);
fill(ds, 0);
setPtr(ds, lpPN, DOS_DS,DOS_DX);
putWord(ds, mask, DOS_CX);
setCall(ds, 0x4F); /* функция DOS Find Subsequent */
call(ds);
getError(ds);
endLoop;
freeHandle(aPathName);
^files;
} П
/* Выдать true, если aDirectory представляет тот же
директорий, что и получатель данного сообщения;
в противном случае — false */
Def =(self, aDirectory)
{
AfullName(self) = fullName(aDirectory)
} П
/* Выдать true, если получатель имеет хотя бы один
поддиректорий */
Def hasSubdirectory(self | ds, result, lpPN, aDTA,
aFileString,
aPathName, errors)
{
ds := new(DosStruct);
aDTA := getDTA(Directory);
aFileString := new(FileString, fullName(self));
aPathName := fileString(appendFile(aFileString, "*.*"));
Директории DOS
211
lpPN := lP(aPathName);
fiU(ds, 0);
setPtr(ds, lpPN, DOS_DS,DOS_DX);
putWord(ds, 0x10, DOS_CX);
setCall(ds, 0x4E);
call(ds);
getError(ds);
errors := new(Set, 2) ;
add(errors, 2);
add(errors, 18);
result := wordAt(ds, DOS_AX);
if result = 0 then
result := true;
else
if not(result in errors) then
errorBox("DosError", LoadString(DOS_OFFSET+result)) ;
endif;
result := false;
endif;
freeHandle(aPathName) ;
Aresult;
} !!
/* Удалить директорий, указанный в self */
Def remove(self | aPathName, aDTA, ds, lpPN, result)
{
aDTA : = getDTA(Directory);
ds := new(DosStruct);
aPathName := fullName(self);
lpPN := lP(aPathName);
fill(ds, 0);
setPtr(ds, lpPN, DOS_DS, DOS_DX);
setCall(ds, 0x3A); /* функция DOS RMDIR */
call(ds);
result := getError(ds);
if result 0
errorBox("Dos Error", loadString(DOS_OFFSET+result));
endif;
freeHandle(aPathName);
} ! !
/* Создать директорий, указанный в self */
Def create(self | aPathName, aDTA, ds, lpPN, result)
{
aDTA := getDTA(Directory);
ds := new(DosStruct);
aPathName := fullName(self);
lpPN := lP(aPathName);
fill(ds, 0);
setPtr(ds, lpPN, DOS_DS, DOS_DX);
setCall(ds, 0x39); /* функция DOS MKDHl */
call(ds);
result := getError(ds);
if result 0
212
Объектно-ориентированный пользовательский интерфейс для Windows
errorBox("Dos Error", loadString(DOS_OFFSET+result));
endif;
freeHandle(aPathName);
} ! !
/* Сменить директорий, указанный в self */
Def makeCurrent(self | aPathName, aDTA, ds, lpPN,
result)
{
aDTA := getDTA(Directory);
ds := new(DosStruct);
aPathName := fullName(self);
lpPN := lP(aPathName);
fill(ds, 0);
setPtr (ds, lpPN, DOS_DS, DOS_DX);
setCall(ds, 0x3B); /* функция DOS CHDIR */
call(ds);
result := getError(ds);
if result 0
errorBox("Dos Error”, loadString(DOS_OFFSET+result));
endif;
freeHandle(aPathName);
} !!
/* Выдать отсортированную коллекцию директориев для
указанного объекта*/
Def subdirectoriesOf(self, fileStr | ds, lpPN, aDTA,
subdirs,
aFileString, aPathName, errors)
{
subdirs := new(SortedCollection, 1);
ds := new(DosStruct);
aDTA := getDTA(self);
if fileStr = nil then
fileStr := "*.*";
endif;
aFileString := new(FileString, fullName(self));
aPathName := fileString(appendFile(aFileString,
fileStr));
lpPN := lP(aPathName);
fill(ds, 0);
setPtr(ds, lpPN, DOS_DS,DOS_DX);
putWord(ds, 0x10, DOS_CX);
setCall(ds, 0x4E);
call(ds);
getError(ds);
errors := new(Set, 2);
add(errors, 2);
add(errors, 18);
loop result := wordAt(ds, DOS_AX);
while n^>t(result in errors)
begin \
if(wordAt(aDTA+21L) bitAnd 0xFF^ - 16 /* Директорий */
add(subdirs,
Директории DOS
213
removeNulls(setClass(fillStruct(new(Struct, 13)
aDTA+30L), String));
endif;
fill(ds, 0);
setPtr(ds, lpPN, DOS_DS,DOS_DX);
putWord(ds, 0x10, DOS_CX);
setCall(ds, 0x4F); /* функция DOS Find Subsequent */
call(ds) ;
getError(ds);
endLoop;
freeHandle(aPathName);
^subdirs;
/* Установить новое значение маршрута
данного объекта равным aString */
Def setPathName(self, aString)
{
pathName := aString;
} ! !
/* Установить новое имя дискового драйвера
данного объекта-директория равным aCharacter */
Def setDiskDrive(self, aCharacter)
{
diskDrive := asString(aCharacter) + ”:";
} !!
/* Выдать для данного директория полную спецификацию маршру¬
та, включая букву дискового драйвера */
Def fullName(self)
{
^diskDrive(self) + pathName(self);
} !!
/* Выдать метку тома для диска, содержащего объект */
Def volumeLabel(self | ds, lpPN, aDTA, result,
volumeLabel,
aFileString, aPathName, errors)
{
ds := new(DosStruct);
'aDTA := getDTA(Directory);
aFileString := new(FileString, fullName(self));
aPathName := fileString(appendFile(aFileString, "*.*"));
lpPN := lP(aPathName);
fill(ds, 0);
setPtr(ds, lpPN, DOS_DS,DOS_DX);
putWord(ds, 0x8, DOS_CX);
setCall(ds, 0x4E);
call(ds);
getError(ds);
errors := new(Set, 2);
add(errors, 2);
add(errors, 18);
214
Объектно-ориентированный пользовательский интерфейс для Windows
result := wordAt(ds, DOS_AX);
if not(result in errors) then
volumeLabel :=
removeNulls(setClass(fillStruct(new(Struct, 13),
aDTA + 30L),
String));
else
volumeLabel := nil;
endif;
freeHandle(aPathName);
AvolumeLabel;
} !!
/* Выдать маршрут, ассоциированный с данным
объектом-директорием */
Def pathName(self);
{
ApathName;
} !!
/* Выдать дисковый драйвер, ассоциированный с данным
объектом-дирскторием */
Def diskDriver(self)
{
AdiskDrive;
} !!
Листинг 6. Direct.RC
(Справа от текста диагностических сообщений приводится их русский пере¬
вод.— • Пф «5|¥ • ¥Ф .)
;Dos error messages (Error number+DOS_OFFSET)
DOS_OFFSET+1, "Invalid function number" ^ неверный
номер функции
D0S_0FFSET+2, "File not found" ^ файл не найден
DOS_OFFSET+ 3, "Path not found" ^ маршрут не найден
DOS_OFFSET+4, "Too many open files" " слишком много
открытых файлов
D0S_0FFSET+5, "Invalid function number" ' неверный
номер функции
DOS_OFFSET+6, "File not found" ~ файл не найден
DOS_OFFSET+7, "Path not found" " маршрут не найден
D0S_0FFSET+8, "Too many open files" ^ слишком много
открытых файлов
D0S_0FFSET+9, "Invalid function number" ^ неверный
номер функции
DOS_OFFSET+10, "Invalid environment" ' неверная среда
DOS_OFFSET+11, "Invalid format" ^ неверный формат
DOS_OFFSET+12, "Invalid access code" “ неверный код
доступа
DOS_OFFSET+1 3, "Invalid data" " неверные данные
D0S_0FFSET+15, "Invalid drive specified" " неверно
указан драйвер
DOS_OFFSET+16, "Attempt to remove current directory"
попытка уничтожить текущий директорий
DOS_OFFSET+1 7, "Not same device" ^ не то же устройство
DOS 0FFSET+18, "No more files" ^ нет больше файлов
Директории DOS
215
DOS_OFFSET+19,
DOS_OFFSET+2O,
DOS_OFFSET+21,
DOS_OFFSET+22,
DOS_OFFSET+23,
DOS_OFFSET+24,
DOS_OFFSET+25,
DOS_OFFSET+26,
DOS_OFFSET+27,
DOS_OFFSET+28,
DOS_OFFSET+29,
DOS_OFFSET+ 3 0,
DOS_OFFSET+ 31 ,
DOS_OFFSET+32,
DOS_OFFSET+33,
DOS_OFFSET+34,
DOS_OFFSET+ 3 5,
DOS_OFFSET+36,
DOS_OFFSET+50,
DOS_OFFSET+51,
DOS_OFFSET+52,
DOS_OFFSET+53,
DOS_OFFSET+54,
DOS_OFFSET+ 5 5,
DOS_OFFSET+56,
DOS_OFFSET+57,
DOS_OFFSET+58,
DOS_OFFSET+59,
DOS_OFFSET+6O,
DOS_OFFSET+61,
DOS_OFFSET+ 6 2,
DOS_OFFSET+63,
DOS_OFFSET+64,
DOS_OFFSET+65,
"Attempt to write on write-protected
diskette" " попытка писать на дискету,
защищенную от записи
"Unknown unit" ^ неизвестное устройство
"Drive not ready" ^ драйвер не готов
"Unknown command" ' неизвестная команда
"Data error (CRC)" " ошибка в данных (CRC)
"Bad request structure length"
неверная длина структуры требования
"Seek error" ^ ошибка поиска
"Unknown media type" ^ ^известный тип
среды
"Sector not found" “ сектор не найден
"Printer out of paper" ^ кончилась
бумага в принтере
"Write fault" ^ зарегистрировать
неисправность
"Read fault" ' прочитать неисправность
"General failure" " общий отказ
"Sharing violation" ^ нарушение доступа
к общим данным
"Lock violation" ^ нарушение блокировки
"Invalid disk change" ^ неверная смена
диска
"FCB unavailable" ^ недопустимый FCB
"Sharing buffer overflow" ^ переполнение
буфера общих данных
"Network request not supported" ^ работа
с сетью не поддерживается
"Remote computer not listening"
удаленный компьютер не доступен
"Duplicate name on network"
дублирующееся имя сети
"Network name not found" ^ имя сети не
найдено
"Network busy" ^ сеть занята
"Network device no longer exists"
сетевое устройство больше не существует
"NETBIOS command limit exceeded" ^ превы¬
шено ограничение команды системы NETBIOS
"Network adapter hardware error”
аппаратная ошибка в сетевом адаптере
"Incorrect response from network"
некорректный возврат из сети
"Unexpected network error"
неописанная ошибка сети
"Incompatible network adapter"
несовместимый сетевой адаптер
"Print queue full" ' очередь принтера
заполнена
"Not enough space for print file"
нет места для файла печати
"Print file was deleted" ^ файл печати
был удален
"Network name was deleted" " имя сети
было уничтожено
"Access denied"
доступ запрещен
216
Объектно-ориентированный пользовательский интерфейс для Windows
DOS_OFFSET+66,
DOS_OFFSET+67,
DOS_OFFSET+68,
DOS OFFSET+69,
DOS_OFFSET+7Q,
DOS_OFFSET+71,
DOS OFFSET+72,
DOS__OFFSET+8O,
DOS__OFFSET+82,
DOS_OFFSET+83,
DOS_OFFSET+84,
DOS_OFFSET+85,
DOS_OFFSET+86,
DOS_OFFSET+87,
DOSOFFSET+88,
"Network device type incorrect"
некорректный тип сетевого устройства
"Network паше not found" " имя сети не
найдено
"Network name limit exceeded"
превышено ограничение на имя сети
"NET3I0S session limit exceeded"
превышено ограничение сеанса системы
NETBIOS
"Temporarily paused" временная задержка
"Network request not accepted" ' -сетевой
запрос не принят .
"Printer or disk redirection is paused"
переназначение принтера или диска
задерживается
"File exist" ^ файл (уже) существует
"Cannot make directory entry" " нельзя
создать вход в директорий
"Fail on INT 24" ^ отказ на прерывании 24
"Too many redirections" ' слишком много
переназначений
"Duplicate redirection" ' повторное
переназначение
"Invalid password" " неверный пароль
"Invalid parameter" " неверный параметр
"Network data fault" ^ поврежденные
сетевые данные
Глава 7
■ ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ
ГРАФИЧЕСКОЕ
ПРОГРАММИРОВАНИЕ
ДЛЯ WINDOWS
Глава посвящена подробному рассмотрению основ объектнс-ори-
ентированного программирования графики в Windows, использу¬
ющего встроенные возможности Актора. Среди рассматриваемых
вопросов битовые карты и рисование цветных прямоугольников,
эллипсов, многоугольников и диаграмм.
То, что среда Windows поддерживает рисование графических
объектов в своих окнах, не является неожиданностью. Однако
некоторые следствия из этого факта далеко не очевидны. Одна из
первых трудностей, которую надо преодолеть, связана с координа¬
тами. Любое окно в среде типа Windows может двигаться по
экрану. Однако хотелось бы, чтобы графические объекты, создан¬
ные в окне, двигались вместе с ним и сохраняли свои очертания
всегда, когда окно видно на экране. Это подразумевает наличие
двух различных систем координат: одной для экрана, второй для
окна. В большинстве систем программирования в Windows вам
приходится сталкиваться с этой проблемой постоянно.
■ ВСТРОЕННЫЕ ГРАФИЧЕСКИЕ КЛАССЫ
В Акторе всего шесть основных графических классов: Point,
Graphics Object, REct, RndRect, WinPolygon и WinEliipse. Эти
классы не являются производными от Point. На самом деле они
производные от класса Struct. Классы построены как коллекция
объектов Point, а не как специализации этого класса. В системе с
множественным наследием графические классы могли бы насле¬
довать из более чем одной линии наследования, так что автомати¬
чески приобретались бы как графические, так и другие свойства.
Объектно-ориентированное графическое программирование для Windows
Все классы графических объектов обеспечивают свои собственные
версии метода draw.
Иерархия встроенных графических классов в Акторе выглядит
так:
Object
Collection
IndexedCollection
ByteCollection
Struct
GraphicsObject
Rect
RndRect
WinEllips
WinPolygon
В последующих разделах мы рассмотрим наиболее важные из этих
встроенных графических классов.
■ КЛАСС POINT
Объекты Point являются двумерными, с двумя экземплярными
переменными x и у. Эти переменные представляют пары коорди¬
нат, определяющих двумерную точку. Объект Point можно создать
одним из трех различных способов: 1) используя сообщение new,
с последующей установкой значения координат при помощи сооб¬
щений setX и setY; 2) создавая литеральную точку непосредствен¬
но, аналогично способу, при помощи которого мы могли бы создать
некоторый массив; 3) при помощи сообщения point. Ниже приво¬
дятся примеры трех различных способов создания одной и той же
точки:
Start := new(Pomt};
setX(Start, 30);
setY(Start, 40);
Start := 30040;
Start := point(30,40);
■ SCRIBBLE (КАРАКУЛИ)
Демонстрационный пример Scribble, который поставляется вместе
с Актором, использует одноименный класс, показывающий, как
относительно просто можно быстро получить полезные графиче¬
ские программы. Для того, чтобы создать окно Scribble, исполь¬
зуйте следующие сообщения:
Sc := defaultNew(Scribble, "Scribble Away");
show(Sc, 1);
Scribble (каракули)
219
Вообще класс Scribble — это хорошее введение в графическое
программирование при помощи Актора и объектно-ориентирован¬
ных систем, поскольку он предоставляет возможности ручного
рисования при помощи мыши, путем реализации всего лишь
четырех методов и единственной экземплярной переменной. Мы
последовательно рассмотрим каждый из компонентов данного
приложения.
Как и в любых интерактивных графических программах Актора,
использующих мышь, управление ею в Scribble обеспечивается
посредством трех методов, называемых beginDrag, drag и endDrag
соответственно. Три 'Jти метода соответствуют сообщениям
Windows: WM_LBUTTONDOWN, WM_MOUSEMOVE и
WM_LBUTTONUP. Эти сообщения посылаются, когда нажимается
левая кнопка мыши, когда мышь передвигается при нажатой
кнопке и когда кнопка отпускается.
Единственной экземплярной переменной Scribble служит перемен¬
ная dragDC, которая хранит позицию, куда была приведена мышь.
Сообщение beginDrag запоминает контекст дисплея в экземпляр¬
ной переменной dragDC и устанавливает положение точки, где в
данный момент находится мышь. Этот метод выглядит так:
Def beginDrag(self, wP, point)
{ dragDC := get(Context(self);
moveTo(point, dragDC);
}
Метод drag проводит прямую, связывающую точку с позицией из
dragDC.
Def drag(self, wP point)
{ lineTo(point, dragDC);
}
Метод endDrag завершает рисование прямой, освобождая контекст
дисплея, когда кнопка отпускается.
Def endDrag(self, wP, point)
{ releaseContext(self, dragDC);
}
И наконец вводится метод для использования правой кнопки
мыши для очищения экрана.
Def WM_RBUTTONDOWN(self, wp, lp)
{ repoint(self);
}
Хотя данная программа делает не слишком много, но этого доста¬
точно для тех задач, для которых она предназначена. Просто
удивительно, какой короткий код пришлось написать для того,
чтобы сделать все это. И важно, что программа интересна не только
сама по себе, все интерактивные графические программы можно
писать таким же образом, с использованием одних и тех же трех
220 Объектно-ориентированное графическое программирование для Windows
методов управления мышьк: beginDrag, drag и endDrag. Ниж
приводится формальное опьсание класса Scribble.
■ ОПИСАНИЕ КЛАССА SCRIBBLE
Исходный файл: SCRffiBLE.CLS
Наследует из: Window
Экземплярные переменные:
dragDC
Методы объекта:
beginDrag Инициализирует передвижение мыши,
запоминая контекст дисплея для рисования
drag Реагирует на сообщение от мыши,
рисует прямую, соединяющую текущую
позицию и указанную точку
endDrag Завершает передвижение мыши, освобождая
контекст дисплея. WM_RBUTTONDOWN
реагирует на соответствующее сообщение
MS-Windows, стирая окно при нажатии
правой кнопки на мыши
■ ПРЯМОУГОЛЬНИКИ
Говоря на языке геометрии, прямоугольник — это особый тип
многоугольников, поэтому можно было бы ожидать, что Rectangle
(прямоугольник) должен быть подклассом из WinPolygon (много¬
угольников). Однако в Акторе это не так. Rect находится на одном
уровне с классом WinPolygon. Причиной этого служит особое
внимание, придаваемое прямоугольникам в AP^ поскольку пря¬
моугольники используются буквально повсеместно, при их помо¬
щи создаются окна и элементы управления Q?uc. 7.1). Для
прямоугольников нет необходимости наследовать свои возможно¬
сти. Эти возможности заложены в функциях Windows.
Ниже приводится интерактивный сеанс, в котором создается пря¬
моугольник R1 и затем запрашиваются его ширина и высота.
R1 := rect (5, 5, 15, 10) ;
Rect (5 5 15 10)
with(R1);
10
height(R1);
5
Следующий метод вычисляет площадь прямоугольника:
Def area(self)
{ ^abs(right(self) - left(self)) * abs(bottom(self) -
top(self) ) ;
Прямоугольники
221
Рис. 7.1. Рисование в окне гурямоугольника с закругленными краями
Для применения этого метода к прямоугольнику, определенному
выше, достаточно сообщения:
area(R1);
50
Аналогично мы можем определить метод для вычисления пери¬
метра:
Def perimeter(self)
{ ^(2 * abs (right(self) - left(self)) + (2*
abs(bottom(self) - top(self))));
} •
perimeter(R1) ;
30
И наконец ниже приводится метод, который использует теорему
Пифагора для вычисления диагонали прямоугольника:
Def diagonal(self)
{ ^(square(abs(right(self) - left(self))) +
(square(abs(bottom(self) - top(self)))) ** .5;
}
diagonal(R1) ;
11.18033989
222 Объектно-ориентированное графическое программирование для Windows
■ ПРЯМОУГОЛЬНИКИ С ЗАКРУГЛЕННЫМИ КРАЯМИ
Прямоугольники с закругленными краями реализуются в Акторе
при помощи класса RndRect. Ниже приводится интерактивный
сеанс, в котором сначала создается объект RndRect, а затем
задается его размер:
RR := new(RndRect)
RndRect(0 0 0 0 0 0)
init(RR, 5, 5, 15, 10, 10, 2);
RndRect(5 5 15 10 10 2)
Для того чтобы нарисовать объект RndRect, мы должны создать
окно, в котором он будет нарисован.
WW := new(Window, ThePort, nil, "Window For Round
Rectangle”, &(275, 60, 500, 200));
show(WW, 1);
RR := new(RndRect);
imt(RR, 25, 25, 75, 80, 1 5, 10);
Wct := getContext(WW);
draw(RR, Wct);
releaseContext(WW, Wct);
Для того чтобы увеличить размеры такого прямоугольника, надо
просто послать следующие сообщения:
inflate(RR, 20, 20);
Wct := getContext(WW);
draw(RR, Wct);
releaseContext(WW, Wct);
■ ЭЛЛИПСЫ
Лри рисовании эллипса необходимо воспользоваться тем же опы¬
том, что и при рисовании прямоугольника. Сначала надо создать
подходящее окно. После этого эллипс инициализируется как
объект класса WinEllipse, сохраняется контекст, изображение
выдается на дисплей.
WW := new(Window, ThePort, nil, "Window For Ellipse”,
& (275, 60, 500, 200));
show(WW, 1);
E1 := new(WinEllipse);
init(E1, 55, 25, 155, 75);
cxt := getContext(WW);
draw(E1, cxt);
releaseContext(WW, cxt);
■ МНОГОУГОЛЬНИКИ
ДХ'Я реализации многоугольников Актор использует класс
WinPolygon 0vuc. 7.2). Объект WinPolygon является по существу
коллекцией объектов Point. Это более сложный графический объ¬
ект, чем те, с которыми мы имели дело ранее, но вместе с тем
Многоугольники
223
Рис. 7.2. Псввдотрвхмврный многоугольник
развитие того же подхода, какой использовался для прямоуголь¬
ников.
Для использования класса WinPolygon при рисовании параллелог¬
рамма можно выполнить следующие сообщения:
WW := new(Window, ThePort, nil, "Window For
Parallelogram", &(275, 60, 600, 300));
show(WW, 1);
P1 := new(WinPolygon, #(100050 250050 2500150 150@150
100@50));
cxt := getContext(WW);
draw(P1, cxt);
rele'aseContext(WW, cxt);
Метод draw для WinPolygon записывается в одну строчку:
Def draw(self, hdc)
{ Call Polygon(hdc, self, size(self)/4);
}
Как можно заметить, метод draw класса WinPolygon просто выьы-
вает функцию Windows Polygon, но при этом делит размер яв
четыре для того, чтобы подготовить корректный аргумент nCount
Ключом к пониманию класса Polygon является метод класса new,
его определение таково:
Объектно-ориентированное графическое программирование для Windows
Def new(self, aColl aPoly)
{ aPoly := variableNew(self : Behavior, size(aColl) * 4);
do(over(0), size(aColl)),
{ using)idx) putWord(aPoly, x(aColl(idx), idx * 4);
putWord(aPoly, y(aColl(idx), (idx * 4) + 2)
};
^aPoly;
}
Что здесь происходит? Просто этот метод перебирает всю коллек¬
цию точек и использует метод putWord, наследуемый из класса
Struct, для получения памяти под каждую точку в коллекции при
запоминании данных многоугольника. Метод variableNew, предо¬
ставляемый в классе Behavior, как раз и используется для опре¬
деления новых методов класса Collection.
Эти сообщения используют класс WinPolygon при рисовании
”псевдотела”:
Pc := new(Window, ThePort, nil, "Window For Solid”,
&(175, 50, 600, 400));
show(Pc, 1);
P1 := new(WinPolygon, #(1000100 2000100 2500200 1500200
1000100));
P1 := new(WinPolygon, #(1000100 154075 250075 2000100
1000100));
P1 := new(WinPolygon, #(2500200 2930167 250075 2000100
2500200));
cxt := getContext(Pc);
draw(P1, cxt);
draw(P2, cxt);
draw(P3, cxt);
releaseContext(Pc, cxt);
При использовании WinPolygon важно помнить, что базисная
функция Polygon всегда будет закрывать рисунок, таким образом
придется специфицировать точки для всего четырехугольника,
иначе появятся ненужные диагонали и автоматически они закро¬
ют рисунок.
Ниже приводится последовательность сообщений, которая иллю¬
стрирует использование функции Windows PolyPolygon:
PP1 := new(Wmdow, ThePort, nil, "Window For
PolyPolygon”, &(175, 50, 600, 400));
show(PP1, 1);
cxt := getContext(PP1);
Call PoiyPolygon(cxt, #(#(20020 40020 60080 40080
20020)), #(5), 1);
releaseContext(PP1, cxt);
Другой пример альтернативного способа рисования и анимации
псевдокуба представлен в демонстрационной программе Актора
Hypercybe. Если вы хотите запустить программу Актора для
анимации гиперкуба в рамках интерактивного сеанса, в рабочей
области Актора, воспользуйтесь сообщениями:
Многоугольники
225
CW := defaultMew(CubeWindow, "HyperCube");
show(CW, 1);
run(CW);
Одно из наблюдений, которое вы могли сделать по поводу всех
графических объектов, рисовавшихся до сих пор, состоит в том,
что они исчезают, как только фокус ввода переносится в другое
окно или окно передвигается или изменяет свои размеры. Это
происходит потому, что окно перерисовывает себя и не имеет
возможности узнать, что в нем имеется объект, который тоже надо
перерисовать. Для того, чтобы быть уверенным, что содержимое
окна будет перерисовываться всегда при получении сообщения
paint, необходима новая версия сообщения paint.
Руководство по Актору предлагает следующий метод paint для
класса GraphicsWindow:
Def paint(self, hDC theRect)
{
theRect := rect(10, 10, width(clientRect(self))/2,
height(clientRect(self))/2);
draw(theRect, hDC);
}
Проследим за действиями этого метода. Если у вас есть Актор,
введите метод в класс GraphicsWindow и выполните эти сообщения
в рабочей области:
W := defaultNew(GraphicsWindow, "Try Me");
show(W, 1);
Как вы можете видеть, в окне уже есть прямоугольник, и он
перерисовывается каждый раз, когда окно получает сообщение
paint (рисовать) из Windows. Это же вы видите и в данной
программе. Вместе с тем то, что написано здесь, имеет крайне
узкую направленность, метод paint занимается тем, что всегда
перерисовывает именно этот прямоугольник.
Один из способов придать этой программе большую общность
состоит в том, чтобы определить экземплярную переменную, ко¬
торая будет поддерживать коллекцию всех графических объектов,
нарисованных в данном окне. В метод draw следовало бы добавить
работу с объектом, который представляет коллекцию перерисовы¬
ваемых объектов . В этом случае метод paint можно было бы
повторно вызывать для каждого элемента коллекции и перерисо¬
вывать их. Важной деталью здесь является поиск подходящего
способа передачи имени особого окна в метод draw, так, чтобы
было известно, в каких окнах следует изменить список объектов.
Было бы идеально, если бы методы draw переписывались так,
чтобы они могли работать и с окнами, которые поддерживают
такой механизм.
«-857
Объвктно-оривнтированнов графическое программирование для Windows
Ш ЦВЕТНЫЕ БИТОВЫЕ КАРТЫ
Как мы уже отмечали в Windows, начиная с версии 3,0 и далее,
поддерживаются аппаратно-независимые цветные битовые карты
(bitmaps). Нет необходимости изменять один раз написанцую
программу для запуска приложения на той или иной аппаратной
платформе. Так же как меню и диалоги, битовые карты можно
объявлять как статические ресурсы. Наименьшей битовой картой,
которую можно использовать в шаблоне для кисти, является карта
8 на 8. Хотя встроенные классы Актора не включают в себя
большого количества прямых средств управления битовыми кар¬
тами, из Актора доступны все функции Windows для работы с
ними. Некоторые из этих функций приведены в табл. 7.1.
В среде Актора общий размер экрана хранится в системе, и для
того, чтобы узнать его, вам придется послать следующее системное
сообщение screenSize:
screenSize();
Если у вас монитор VGA, сообщение вернет следующее значение:
6400480
В действительности это координатный адрес самой дальней точки
в нижнем углу в системе координат экрана.
Помимо системы координат экрана имеется и еще одна система
координат, связанная с окном. Она называется системой коорди¬
нат клиента.
Сначала, перед тем, как что-нибудь рисовать в окне, вы должны
взять контекст дисплея. После того как рисование закончено, вы
должны вернуть, освободить этот контекст. Эти операции выпол¬
няются при помощи сообщений getContext и releaseContext. Если
мы нарисовали некоторый графический объект в обычном окне,
этого еще недостаточно для того, чтобы рисунок сохранялся при
изменяющихся условиях. Обычно это бывает связано с перекры¬
тием изображения другим окном, изменением размеров окна и
другими модификациями, в результате которых перерисовка изо¬
бражения не выполняется. Чтобы быть уверенным, что изображе¬
ние перерисовывается, следует рисовать его в окнах, обладающих
методом paint, они производят перерисовку автоматически.
Таблица 7.1. Функции Windows для работы с битовыми картами
CreateDIBitmap
LoadBitmap
GlobalUnlock
BitBlt
PatBlt
PolyLine
Трехмерная точка
227
■ ТРЕХМЕРНАЯ ТОЧКА
Из всего изложенного очевидно, что встроенные графические
средства, которые мы успели обсудить, работают только с двумя
измерениями. Отметим, что они не только используются для
рисования на двумерном дисплее — практически вся компьютер¬
ная графика связана с этим ограничением, — описанные нами
объекты сами по себе являются двумерными. В этом разделе
предлагаю простое упражнение в графическом объектно-ориенти-
рованном программировании (листинг 1). Оно состоит исключи¬
тельно в разработке нового класса для представления точек,
класса, который описывает точку в трехмерном пространстве.
Листинг 1. Трехмерные точки
I ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ j
/* The Point3-D Class */
/* */
j ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ j
Def (z(self)
{
^z
}
Def setZ(self, zVal)
{ ^z := zVal);
}
Def set(self, xVal, yVal, zVal)
{ x := xVal;
у := yVal;
z := zVal;
}
Def round(self)
{ ^point(asInt(
if x < 0
then x - 0.5
else x + 0.5
endif), asInt(
if у < 0
then у - 0.5
else у + 0.5
endif), asInt(
if z < 0
then z - 0.5
else z + 0.5
endif));
}
Def asStruct(self | struct)
{ struct := new(Struct, 6);
putWord(struct, x, 0);
putWord(struct, у, 2);
putWord(struct, z, 4);
Astruct;
}
Объектно-ориентированное графическое программированиедля Windows
Def +(self, arg)
{ ^paint(x(arg) + x, y(arg) + у, z(arg) + z);
}
Def - (self, arg)
{ ^paint(x(arg) - x, y(arg) - у, z(arg) - z);
}
Def =(self, a3DPt)
{ ^class(a3DPt) == class(self) cand x = x(a3DPt) cand у =
y(a3DPt) cand z = z(a3DPt);
}
Def printOn(self, aStream)
{
printOn(x, aStream);
nextPutAll(aStream, "0");
printOn(y, aStream);
nextPutAll(aStream, "@");
printOn(z, aStream);
}
DEf point3D(self, yVal, zVal a3DPoint)
{ a3DPoint := new(Point3D);
set(a3DPoint, self, yVal, zVal);
^a3DPoint
}
3D1 := point3D(30, 40, 50);
30040050
Метод moveTo класса Point изменяет текущее положение окна на
то, которое указано. При этом используется специфицированный
дескриптор контекста устройства. Метод lineTo рисует прямую от
текущей позиции дисплея, используя дескриптор, специфициру¬
ющий его контекст, до позиции, соответствующей объекту self, но
не включая ее. Затем он переустанавливает текущую позицию
окна, принимая в этом качестве положение получателя. Метод
d*aw для рисования точки просто вызывает функцию Windows
Rectangle с аргументами, требующимися для рисования ’’безраз¬
мерного” прямоугольника.
Def draw(self, hdc)
{ Call Rectangle(hdc, x, у, x, у) ;
}
■ ДИАГРАММЫ
Поскольку для различных целей возможно существование огром¬
ного многообразия графических приложений, в традиционном
программировании типичной картиной является разработка каж¬
дой новой графической программы с нуля. Только с появлением
объектно-ориентированных систем вполне реально встал вопрос,
каким образом на некотором общем фундаменте может разрабаты¬
ваться широкий спектр графических приложений. В данном раз¬
деле я продемонстрирую несколько практических примеров
Диаграммы
229
построения простых столбчатых и секторных диаграмм, базирую¬
щихся на небольшом числе классов Актора, которые легко расши¬
рить. На основании этих примеров мы сделаем ряд выводов о том,
как можно построить графические системы более общего типа.
Поставяемые классы Актора, поддерживающие рисование диаг¬
рамм, приведены в следующей ветви иерархии классов:
Object
Chart
HBarChart
PieChart
VBarChart
W indowsObject
Window
BCWindow
Класс BCWindow является специализацией класса Window, кото¬
рая имеет экземплярную переменную chart для запоминания
данных диаграммы и особый метод paint, который использует эти
данные при перерисовке диаграммы в окне. Если классы
BCWindow и HBarChart загружены в систему, то следующие
Рис. 7.3. Горизонтальная столбчатая диаграмма
230 Объектно-ориентированное графическое программирование для Windows
сообщения создают горизонтальную столбчатую диаграмму, пока¬
занную на puc. 7.3.
Н := new(HBarChart);
setData(H, #(7, 15, 18, 22));
setLabels(H, #("Jan", "Feb", "Mar", "April"));
setArea(H, 1006100);
С := defaultNew(DCWindow, "Chart");
context := getContext(C);
draw(H, context);
releaseContext(C, context);
setChart (С, H);
show(С, 1);
Практически так же можно построить класс PieChart для создания
изображений секторных диаграмм:
Pie := new(PieChart);
setData(Pie, #(7, 15, 18, 22));
setArea(Pie, 1006100);
adjustScale(Pie, 80);
С ;= defaultNew(DCWindow, "Chart");
context := getContext(C);
drawData(Pie, context);
releaseContext (С, context);
setChart (С, Pie);
show(С, 1);
Результат работы этого фрагмента показан на puc. 7.4.
Рис. 7.4. Вокнесекторнаядиаграмма
Анимация
231
■ АНИМАЦИЯ
Анимация (реализация движения изображения) поставила особые
проблемы перед средой Windows, поскольку здесь требуется муль-
тизадачность. Если Windows будет иметь возможность прервать
вашу процедуру анимации даже на небольшой отрезок времени,
вы никогда не сможете контролировать процесс анимации.
Windows получает управление, когда приложение запрашивает
следующее сообщение. Только в этой точке может включаться
мультизадачность и происходить передача сообщений другому
приложению. Обычно это происходит, когда очередь сообщений к
исходному приложению пуста. Единственный способ сохранить
управление в ходе анимации — это гарантировать, что очередь
сообщений никогда не будет пустой. Для того чтобы избежать
мерцания во время анимации, необходимо добиться, чтобы ни в
какой момент времени изображение на экране не пропадало и не
изменялось слишком резко.
Один из методов анимации в Windows состоит в том, чтобы
образовать дочернее окно в точности по размеру изображения и
затем передвигать это окно, а не многократно рисовать и перери¬
совывать изображение большего окна. Другой метод состоит в
создании объекта анимации в виде пиктограммы и пользовании
функцией Windows DrawIcon, а не MoveWindow.
Если вы заранее знаете направление движения, то иногда можно
создавать битовые карты с незаполненными участками, тогда при
перерисовке достаточно затереть предшествующее изображение.
Это избавляет от необходимости специальной процедуры для сти¬
рания изображения.
Один из способов контролировать время при анимации состоит в
использовании сообщения WM_TEVIER. Следующие демонстраци¬
онные программы анимации в Акторе используют ’’оживляющее”
(’aHve’) сообщение для обеспечения мультизадачности.
Def flipActor(self | idx, array, count)
{ idx := -1;
array := #(0 1 2 3 2 1);
count := (size(array) * 4) + 1);
loop
alive(self) cand checkMessage();
while alive(self) cand (count := count - 1) = 0
begin
doDraw(self, bitmaps[array[idx + 1] mod size(array)]],
{ using(bitmapArray, hDC, hMemDC)
Call BitBlt(hDC, bitmapArray[l], bitmapArray[2],
bitmapArray[3], bitmapArray[4],
hMemDC, 0, 0, bitmapArray[5],
);
});
delay(self, 30);
232 Объектно-ориентированное графическое программирование для Windows
endLoop;
}
/* Нарисовать строки человечков */
Def flipPeople(self hDC, count, idx, rect)
{ rect := rect(50, 85, 400, 112);
loop
alive cand checkMessage();
while alive(self)
idx := 2000;
do(findNumber(self, loadString(idx)),
{ using(number jc, sz, str, which)
if alive(self)
then ос := new(OrderedCollection, number);
do(number, { using(i) add(oc,
loadString(idx ;= idx + 1));});
loop
while alive(self) cand (sz := size(oc)) 0
begin
hDC := getContext(self);
Call SetTextColor(hDC, 0xFFFFFFL);
Call SetBkMode (hDC, T^*ANSPARENT);
wyich := random(sz);
Call DrawText(hDC, asciiz(oc :
Object[which]), -1, rect,
0x25 /* DT_CENTER, DT_VCENTER &
DT_SINGLELINE */);
releaseContext(self, hDC);
remove(oc, which);
delay(self, 1000);
scrollRect(self, rect,
point(#( -1 1 )[random(2)],
#( -1 1 )[random(2)]), height(rect)
) ;
endLoop;
endif;
}) ;
endLoop;
}
■ НЕКОТОРЫЕ ИТОГИ
Одной из наиболее привлекательных сторон Актора является его
интерактивная среда. Это означает, по крайней мере теоретически,
что вы можете попробовать ввести любую программу и тут же
получить ее результат, не используя традиционный цикл компи¬
ляции и редактирования внешних связей. Среди других досто¬
инств отметим, что программист избавляется от траты времени,
борьбы с чужими ошибками и необходимости заниматься не
своими проблемами. Одним из факторов, который может свести
на нет все эти преимущества,является то, что Актору приходится
иметь дело с компилируемыми ресурсами типа меню. По этой
причине, хотя это требует чуть большего труда, я рекомендовал
бы в процессе разработки приложения пользоваться только дина¬
Описания графических классов
233
мическими ресурсами. Потом, когда вы завершите разработку про¬
граммы, можно будет переделать эти ресурсы в компилируемые.
■ ОПИСАНИЯ ГРАФИЧЕСКИХ КЛАССОВ
Turtle /* Черепашка */
Исходный файл: TURTLE.CLS
Происходит из: Object
Экземплярные переменные:
position,
heading,
visibility,
penDown,
stepSize,
kitchenFloor,
kitchenFloorDC,
turConstants
Методы класса:
Методы объектов:
b Возвращает черепашку назад. Если перо
было опущено, след будет стерт
backup (то же самое)
down Опускает перо для того, чтобы когда
начнется движение, за черепашкой
оставался след
draw Метод для рисования панциря черепашки
на экране
erase Стирает черепашку на экране, перерисовывая
свое изображение белым цветом
f Передвигает черепашку на x шагов вперед
face Ориентирует черепашку под определенным
углом. 0 градусов соответствует направлению
на север
faceRad Задает для черепашки направление
в радианах
form Рисует изображение черепашки на экране
forward Передвигает черепашку на x шагов вперед
getWindowDC Берет контекст для kitchen floor окна
goTo Перемещает черепашку в точку,
специфицированную в nextPosition
hide Скрывает (делает невидимой) черепашку
home Устанавливает черепашку в центр экрана
234 Объектно-ориентированное графическое программирование для Windows
init
koch
1
left
leftRad
letGoWindowDC
next
peano
peanoRecursel
peanoRecurse2
r
recurseKoch
recurseSqKooh
right
rightRad
setWindow
show
sqKoch
starl
star2
starry
up
Устанавливает размер шага, инициализирует
окно для черепашки, создает коллекцию
turConstants и устанавливает черепашку
в центр экрана
Рисует фрактальную кривую Коха порядка n
Поворачивает черепашку налево (против
часовой стрелки) на x градусов
Поворачивает черепашку налево (против
часовой стрелки) на x градусов
Задает поворот черепашки налево в радианах
Освобождает контекст дисплея для базисного
серого (kitchen floor) окна
Возвращает следующую точку, в которую
должна прийти черепашка
Рисует кривую Пеано указанного размера
и порядка
Обеспечивает рекурсию для рисования
кривых Пеано
Обеспечивает рекурсию для рисования
кривых Пеано
Поворачивает черепашку на x градусов
направо (по часовой стрелке)
Обеспечивает рекурсию для метода koch
Обеспечивает рекурсию для метода sqKoch
Поворачивает черепашку на x градусов
направо (по часовой стрелке)
Задает поворот черепашки направо
в радианах
Переустанавливает kitchen floor окно
Делает черепашку видимой (рисует ее
на экране)
Рисует ”квадратную” фрактальную кривую
Коха порядка n
Рисует пятиконечную звезду без
пересекающихся прямых
Рисует с пересекающимися прямыми
Рисует n звезд со случайными размерами и
ориентацией
Поднимает перо, после этого черепашка не
будет оставлять след
ShapesWindow (Формы окна)
Исходный файл: SHAPESWI.CLS
Наследует из: Window
Описания графических классов
235
Имеет наследников: TrackWindow
Переменные класса:
$NativeLiterals
Переменные экземпляров:
shapes
currentShape
blackBrush
pen
outline
Методы класса:
Методы объектов:
command
init
paint
setup
Массив типов очертаний
Текущая форма для рисования
Темная кисть
Текущий объект-перо
Набор форм для контура прямоугольника
Получает командное сообщение и проверяет
выбранный айтем в меню после старого
айтема меню, который не проверяется
Устанавливает начальную форму и цвет
кисти. Кроме того, добавляет
about-альтернативу (комментарий) для
данного приложения в системное меню
Устанавливает цвет пера и кисти для
очертаний, затем рисует их
Устанавливает систему координат, которую
будет использовать окно при рисовании.
Кроме того, делает это так, что форма не
будет изменяться при изменении размеров
окна
TrackWindow (Перетащить окно)
Исходный файл: TRACKWIN.CLS
Наследует из: ShapesWindow
Имеет наследников:
Переменные класса:
$NativeLiterals
Переменные экземпляров:
dragDC
pOrigin
pOld
Методы класса:
Методы объектов:
beginDrag
drag
Контекст дисплея для drag
Начальная точка shape-окна
Старая концейая точка shape-окна
Направляет все сообщения от мыши в данное
окно и устанавливает для заполненной
кисти черный цвет
Стирает нарисованный перед этим
shape-прямоугольник и рисует новый
236 Объектно-ориентированное графическое программированиедля Windows
endDrag
(эта программа вызывается каждый раз,
когда мышь передвигается к новой точке)
Стирает shape-прямоугольник, освобождает
контекст дисплея и посылает аннулирующее
сообщение в окно (для того, чтобы вызвать
перерисовку)
DemosWindow
Исходный файл:
Наследует из:
Переменные экземпляров:
demos
Методы объектов:
addDemo
DEMOSWIN. CLS
TurtleArea
command
eol
init
initDemos
recreate
Добавляет демонстрационную программу
в коллекцию demos
Отвечает за командные сообщения
Пересылает сообщения eol (end of line —
прим. nepee.), посланные самому себе,
объектам в OutPort
Инициализирует DemosWindow
Инициализирует списковое окно Demos с
подходящими альтернативами
Восстанавливающий метод для окна
демонстрационных программ
Chart (Диаграммы)
Исходный файл: CHART.CLS
Наследует из: Object
Имеет наследников: HbarChart, PieChart, VBarChart
Переменные класса:
$NativeLiterals
Переменные экземпляров:
data
labels
scale
area
prarea
lead
space
brushes
Методы класса:
Методы объектов:
Коллекция данных
Метки, ассоциированные с данными
Указание масштаба
Указание размеров области значений
Указание размеров страницы
Указание расположения начала диаграммы
в окне
Промежуток между айтемами
Запоминает цветовую таблицу
Описания графических классов
237
addData
addLabel
checkError
draw
drawBox
drawKey
drawText
getData
getLabel
init
load
prReScale
save
setArea
setData
setLabels
size
Добавляет элемент данных
Добавляет метку
Берет номер ошибки из файла и выдает
подходящее окно с ошибкой на экран
Рисует диаграмму в данном дисплейном
контексте. Область перед рисованием должна
быть уже установлена
Принимает на входе прямоугольник и
выдает заполненное окно, определяемое
этим прямоугольником
Принимает прямоугольник и список цветов
на входе и выдает заполненное окно,
определяемое этим прямоугольником. После
того, как создается кисть и окно заполнено,
новая кисть должна быть уничтожена и
старая кисть опять должна быть сделана
активной
Пишет на экран текст в окнах, определенных
в контексте устройств, в позиции x и у
Берет данные из диаграммы
Берет метку из диаграммы
Инициализирует новый объект-диаграмму
Считывает данные диаграммы из файла и
передает их экземплярные переменные
объекта-диаграммы данного ASCII-файла
Изменяет масштаб диаграммы, опираясь
на значения data и area
Сохраняет данные диаграммы в ASCII-файле,
имя которого указано в аргументе
Устанавливает диапазон изменения данных
в диаграмме. Должно быть сделано до
начала рисования
Устанавливает значения данных диаграммы
Устанавливает метки диаграммы
Возвращает размер данных
HBarChart (Горизонтальные столбчатые диаграммы)
Исходный файл: HBARCHAR.CLS
Наследует из: Chart
Методы объектов:
adjustPrScale Регулирует масштаб на основании значений
data и area
238 Объектно-ориентированное графическое программирование для Windows
adjustScale
drawData
drawLabel
getClkRgn
resetLead
resetPrLead
resetPrSpace
resetScale
resetSpace
Регулирует масштаб на основании значений
data и area. Использует дробные масштабы,
если они < 1
Рисует элементы данных, диаграммы в форме
горизонтальной полоски. Для определения
того, где должна начаться диаграмма по
отношению к нижнему левому углу окна,
используется значение экземплярной
переменной lead. Экземплярная переменная
scale регулирует размеры диаграммы при
изменении размеров окна
Рисует метку в позиции, соответствующей
расположению полоски. Преобразует метку
в строку, если это не было сделано раньше
Берет пару координат и определяет, какая
из полосок была отмечена при помощи
нажатия кнопки (мыши)
Переустанавливает значение lead для
уточненных значений данной диаграммы
Переустанавливает значение lead для
уточненных значений данной диаграммы
Переустанавливает величину промежутка
между полосками с тем, чтобы
промежутки были подходящими для данного
типа диаграммы
Переустанавливает значение scale для
уточненных значений диаграммы
данного типа
Переустанавливает величину промежутка
между полосками с тем, чтобы промежутки
были подходящими для данного типа
диаграммы
VBarChart (Вертикальные столбчатые диаграммы)
Исходный файл: VBARCHAR.CLS
Наследует из: Chart
Имеет наследников:
Переменные класса:
$NativeLiterals-
Переменные экземпляров:
Методы класса:
Методы объектов:
adjustPrScale Регулирует масштаб на основании значений
data и area
Описания графических классов
239
adjustScale
drawData
drawLabel
getClkRgn
resetLead
resetPrLead
resetPrSpace
resetScale
resetSpace
Регулирует масштаб на основании значений
data и area. Использует дробные масштабы,
если они < 1
Рисует элементы данных, диаграммы в форме
вертикальной полоски. Для определения того,
где должна начаться диаграмма по
отношению к верхнему левому углу окна,
используется значение экземплярной
переменной lead. Экземплярная переменная
scale регулирует размеры диаграммы при
изменении размеров окна
Рисует метку в позиции, соответствующей
расположению полоски. Преобразует метку
в строку, если это не было сделано раньше
Берет пару координат и определяет, какая
из полосок была отмечена при помощи
нажатия кнопки (мыши)
Переустанавливает значение lead для
уточненных значений данной диаграммы
Переустанавливает значение lead для
уточненных значений данной диаграммы
Переустанавливает величину промежутка
между полосками с тем, чтобы
промежутки были подходящими для
данного типа диаграммы
Переустанавливает значение scale для
уточненных значений диаграммы
данного типа
Переустанавливает величину промежутка
между полосками с тем, чтобы
промежутки были подходящими
для данного типа диаграммы
PieChart (Секторные диаграммы)
Исходный файл: PIECHAR.CLS
Наследует из: Chart
Имеет наследников:
Переменные класса:
$NativeLiterals
Переменные экземпляров:
Методы класса:
Методы объектов:
240 Объектно-ориентированное графическое программирование для Windows
adjustPrScale
adjustScale
drawData
drawLabel
getClkRgn
resetLead
resetPrLead
resetPrSpace
resetScale
resetSpace
Регулирует масштаб картинки. Он
выбирается с таким расчетом, чтобы по
вертикали занимал 80% размеров окна
Регулирует масштаб картинки. Он
выбирается с таким расчетом, чтобы по
вертикали занимал 80% размеров окна
Рисует секторную диаграмму, масштабируя
с учетом параметров диаграммы и размеров
окна. На основании значения экземплярной
переменной lead устанавливается расстояние
от диаграммы до верхнего левого угла окна.
Масштаб изменяет размер диаграммы в
соответствии с высотой окна
Рисует окошко для клавиши и текстовую
метку для каждого из цветов секторов круга.
На всякий случай преобразует метку в текст
Берет пару координат и определяет, какой
из секторов был отмечен при помощи
нажатия кнопки (мыши)
Устанавливает расстояние от верхнего левого
угла до точки начала секторной диаграммы
Устанавливает расстояние от верхнего левого
угла до точки начала секторной диаграммы
Переустанавливает расстояние между
окошками для клавиш с тем, чтобы
промежутки были подходящими для данной
секторной диаграммы
Переустанавливает значение scale для
уточненных значений диаграммы
данного типа
Переустанавливает расстояние между
окошками для клавиш на основании
величины area. Использует некоторое
умолчание, если значение area еще
не установлено
Глава 8
Я ПРОГРАММИРОВАНИЕ ПРИ
ПОМОЩИ OBJECTGRAPHICS
Хотя Windows 3.0 дает основательный фундамент для создания
графических приложений, постройка всех остальных частей про¬
граммного здания при помощи базисных средств все равно остается
за прикладным программистом. Это означает, что сохраняется ог¬
ромная потребность в инструментах для программирования графики
в среде Windows. По мере роста популярности объектно-ориентиро-
ванных инструментов поддержка графического программирования в
Windows будет одним из самых главных желаний разработчиков.
Однако до сих пор именно в область графики были направлены
основные усилия серьезных разработчиков. Средства ObjectGraphics
группы Whitewater также служат удовлетворению этих требований.
Использование ObjectGraphics в Windows 3.0 существенно упрощает
работу с графикой, теперь программист уже не представляется
первопроходцем, вступившим на нетронутую территорию, не имея
ни карты, ни компаса.
Библиотека объектно-ориентированной графики демонстрирует
настолько эффектно, насколько это возможно, каковы преимуще¬
ства и потенциал среды программирования Актор для пользовате¬
лей Windows 3.0. ObjectGraphics спроектирован в расчете на три
связанные между собой типа поддержки: инструменты для фор¬
мирования изображения (rendering rools), фильтры платформ и
графические объекты. Как убедительно показывает демонстраци¬
онная программа ObjectDraw, библиотека поддержки в основном
нацелена на приложения, связанные с объектно-ориентированным
рисованием и CAD (Computer Aided Design — проектирование при
помощи компьютеров). ObjectDraw весьма распространенная про¬
грамма. Многие пользователи несомненно оценят ее значение во
множестве профессиональных проектов. И поскольку все исход¬
ные тексты поставляются, программисты будут склоняться ис¬
пользовать ее как пусковую установку для своих еще более
242
Программирование при помощи ObjectGraphics
изощренных графических приложений. И в заключение мы рас¬
смотрим, как строятся программы, подобные этой, в
ObjectGraphics, и обсудим некоторые улучшения, которые в нее
можно внести.
■ ОБЗОР OBJECTGRAPHICS
Перед тем как перейти к реализации ObjectGraphics, рассмотрим
некоторые возможности библиотеки объектно-ориентированной
графики подобного типа. Как я уже упоминал, помимо инструмен¬
тов для работы с графическими объектами как таковыми
ObjectGraphics предоставляет объектно-ориентированные инстру¬
менты для формирования изображения и фильтры для платформ.
Основные приложения, использующие данную библиотеку, ори¬
ентированы на двумерное рисование, проектирование и CAD-про¬
граммы. Инструменты для формирования изображения
ObjectGraphics включают в себя средства типапера, кисти, тексто¬
вого пера, графических пространств. И, конечно, ObjectGraphics
поддерживает шесть стилей линий, предоставляемых Windows 3.0.
Замысел ObjectGraphics основан на идее графического пространст¬
ва, свойства которого можно отобразить на реальный дисплей.
Одной из ключевых возможностей здесь служит возможность
группировать объекты в более крупные объединения, а затем, если
необходимо, "разгруппировывать” их. Помимо этого
ObjectGraphics предоставляет объекты Picture (рисунки), которые
представляют собой коллекции графических объектов. На базе
таких объектов определен протокол для общей рассылки сообще¬
ний для формирования изображений с тем, чтобы унифицировать
ассортимент рисунков и текстов. Кроме того, предоставляются
специальные классы для временных инструментов типа горизон¬
тальных и вертикальных масштабных линеек (rules), полимарке¬
ров и окон-палитр.
В таблицах, приведенных в данной главе, приводится информа¬
ция, которую вы можете не найти в других руководствах по
ObjectGraphics.
■ ДЕМОНСТРАЦИОННЫЙ ПРИМЕР SAMPLEDRAW
Простейший способ оценить мощность ObjectGraphics — это рас¬
смотреть демонстрационную программу SampleDraw. Класс
SampleDraw является потомком класса Window. Он имеет три
новых экземплярных переменных и семь методов, которые реали¬
зуют простую программу для рисования линий. SampleDraw по¬
зволяет строить прямые при помощи мыши и производить выбор
в меню WidthI (ширина) и Color! (цвет) для того, чтобы задавать,
как должна выглядеть та или иная линия. SampleDraw использует
Добавление меню и новых форм в SampleDraw
243
три экземплярные переменные: theLine, width и rgbBox. Как вы
и могли ожидать, theLine используется для того, чтобы запоми¬
нать новый объект-линию, пока она создается при перемещении
мыши. Переменная rgbBox используется для хранения экземпля¬
ра класса RGBDialog, который, и вы не должны удивляться
услышав это, является диалогом, применяемым для выбора цвета
путем определения интенсивности основных тонов красного (red),
зеленого (green) и синего ф1ие). Переменная width просто содер¬
жит целое число, которое является текущим значением ширины
линии. Чтобы вызвать SampleDraw, достаточно воспользоваться
следующими сообщениями:
S1 := defaultNew(SampleDraw, "Window for Drawing Lines");
show(S1, 1);
SampleDraw включает в себя следующие методы: init, command,
gPaint, drag, beginDrag, endDrag и getWidth. Эти методы весьма
интересны, поскольку те или иные их вариации потребуются
почти во всех приложениях, которые пишутся при помощи
ObjectGraphics. Метод init инициализирует все экземплярные пе¬
ременные и объекты, которые использует SampleDraw. Сюда отно¬
сятся переменные port и picture, а также диалогы и меню. Тройка
методов для работы с мышью отражает схему внутренних сообще¬
ний Актора, которые инициируются, когда пользователь вводит
информацию посредством мыши. Сообщение beginDrag соответст¬
вует WM_LBUTTONDOWN. Сообщение drag соответствует
WM_MOUSEMOVE, и endDrag — WM_LBUTTONUP. Метод
command содержит два оператора case. Оба они открывают диало¬
ги, которые получают сведения о цвете и типе линий. Метод gPaint
дает уверенность, что любая линия, которая была нарисована
ранее, будет перерисована, когда окно SampleDraw получит сооб¬
щение WM_PAINT.
Как упоминалось ранее, в ObjectGraphics имеется четыре основных
инструмента для формирования изображения: перья, кисти, тек¬
стовые перья и графические пространства.
■ ДОБАВЛЕНИЕ МЕНЮ И НОВЫХ ФОРМ
В SAMPLEDRAW
Каков простой способ расширить программу типа SampleDraw,
чтобы она была еще более полезной? В порядке эксперимента в
добавлении новых функциональных возможностей в эту програм¬
му начнем с того, что включим систему динамического меню.
После этого можно будет поэкспериментировать с добавлением
различных функций и опций меню для их активации. И, наконец,
придется написать новый метод init, который управляет инициа¬
лизацией системы динамического меню. Наилучший способ про¬
244
Программирование при помощи ObjectGraphics
двинуться в этом направлении — создать подкласс SampleDraw,
который назовем NewDraw. Ниже приводится пример метода init
для такого класса NewDraw.
Def init(sel)
{
port := new(Port);
picture := new(Picture);
RGBox := new(RGBDialog);
setCursor(self, #graphics);
width := 0;
createMenu(self);
changeMenu(self, 0, asciiz("&Shape!"), 100, MF_APPEND);
changeMenu(self, 0, asciiz("&Width!"), 101, MF_APPEND);
changeMenu(self, 0, asciiz("&Color!"), 102, MF_APPEND);
changeMenu(self, 0, asciiz("&Dimensions!"), 103,
MF_APPEND);
drawMenu(self);
}
К имевшимся исходным меню Width! и Color! добавлены меню
Shape! и Dimensions! Теперь вам предстоит решать, какие опции
для определения форм и размерности надо задать в качестве
позиций меню и как они будут реализовываться. Чтобы ознако¬
миться с одним из подходов к решению такой задачи, можно
изучить демонстрационную программу Track!, которая написана
на Акторе с использованием встроенных графических функций.
Если хотите, можете исследовать ряд интересных возможностей,
которые содержат в себе средства ObjectGraphics.
Для того чтобы создать новые меню для работы с программой
рисования, необходим новый метод command. Его можно напи¬
сать следующим образом:
Def command(self, wP, lP)
{ select
case wP == 100
getShape(self);
endCase
case wP == 101
getWidth(self);
endCase
case wP == 102
runModal(RGBox, RGBOX, self);
endCase
case wP == 103
runModal(DIMBox, DIMBOX, self);
endSelect;
}
В этом эскизе метода command предполагается, что вы хотите
использовать класс DimensionDlg из ObjectGraphics для реализа¬
ции диалога с новым меню Dimensions! Для вас это будет своего
рода небольшое и интересное исследование. Вы относительно
быстро получите некоторые результаты, увидите, как реализуются
ObjectDraw
245
ваши идеи. Такая оперативная обратная связь становится возмож¬
ной благодаря организации относительно простой исследователь¬
ской среды, в результате чего повышается скорость процесса
обучения.
■ OBJECTDRAW
ObjectDraw — существенно более сложная программа по сравне¬
нию с SampleDraw. Она демонстрирует применение почти всех
расширений библиотеки ObjectGraphics. Хотя полное понимание
реализации этой программы потребует хорошего знания принци¬
пов и выводов, приведенных в последующих разделах данной
главы, получение четкого представления об общей схеме програм¬
мы — это один из лучших способов охватить всю библиотеку
ObjectGraphics в целом.
Главное окно ObjectDraw подобно большинству программ для
рисования и закрашивания имеет полосу верхнего меню и полосу
палитры или инструментальную планку (toolbox strip) сбоку.
Графические инструменты предоставляют следующие возможно¬
сти: выбор, распахивание (zoom), прямая, прямоугольник, прямо¬
угольник с закругленными краями, эллипс, многоугольник,
ломаная линия, кривая, пиктограмма, редактор битовой карты
(bitmap cropper) и текст. Меню F.ile в ObjectDraw поддерживает
сохранение, загрузку и печать рисунков. Кроме того, имеется
обеспечение, позволяющее открывать Windows в формате битовых
карт версии 2.0.
Самая уникальная часть этой программы представлена меню
ArrangeMenu. Оно позволяет спланировать расположение объек¬
тов в соответствии с особым порядком. Команды Bring to Front
(переместить вперед) и Send to Back (отослать назад) задают
позицию выбора графических объектов, используя дисциплину
стека. Графические объекты, перемещаемые на передний план,
как и следовало ожидать, будут последовательно перекрывать
объекты, лежащие за ними. Команда Group собирает отобранные
объекты в рисунки таким образом, что после этого ими можно
манипулировать как единым целым. Команда Ungroup разделяет
все отобранные рисунки на отдельные объекты. И наконец, Align
to Grid выравнивает источники всех отобранных объектов в соот¬
ветствии с текущей активной сеткой. Все это должно дать вам
представлениеобуникальной мощности программы. Теперь я по¬
пытаюсь дать небольшой обзор организации ObjectDraw.
С точки зрения понимания программы ObjectDraw, наиболее су¬
щественными, ключевыми являются два класса: Draw и
ObjectDraw. Оба они являются потомками Window и находятся на
одном уровне. ObjectDraw предоставляет родительское окно, а
246
Программирование при помощи ObjectGraphics
Draw — большое дочернее окно, в котором происходит рисование.
Интересной особенностью ObjectDraw является то, что каждое из
четырех дочерних окон реагирует на сообщения paint по-разному.
Класс ObjectDraw управляет инициализацией всех дочерних окон,
включая большой объект Draw, который формирует главную об¬
ласть графического рисования и отвечает за управление и моди¬
фикацию всех инструментов формирования изображения.
ObjectDraw предоставляет базис для создания сложных объектов,
составленных из владельца объекта ObjectDraw и различных до¬
черних объектов-окон, таких как окно Draw, toolbox (набор инст¬
рументов), и вертикальных и горизонтальных масштабных
линеек.
Класс Draw реализует окна, обеспечивающие интерактивное рисо¬
вание и средства восстановления изображений, необходимые для
ObjectDraw. Оно обеспечивается возможностью распахнуть окно
(на весь экран) средствами горизонтального и вертикального
скроллирования, копирования, замазывания при помощи работы
с системным буфером (clipboard) и интерактивного выравнивания
любых выбранных графических объектов вдоль некоторой сторо¬
ны, в цеитре, у центральной горизонтали и в форме сетки. Кроме
того, окно Draw предоставляет графическое пространство, которое
модифицируется интерактивно.
Каждый раз, когда приходит сообщение gPaint, объект Draw
совершает следующие пять действий:
1. Рисует окошко вблизи мирового прямоугольника графиче¬
ского пространства данного окна для того, чтобы отметить
страницу.
2. Рисует сетку графического пространства, если эта опция
задана.
3. Рисует целиком графический образ, представленный в дан¬
ном окне и хранящийся как коллекция графических объ¬
ектов экземплярной переменной picture.
4. Рисует маркеры (marking handles) всех графических объек¬
тов, отмеченных в настоящий момент.
5. Переустанавливает маркеры полосок скроллирования с
тем, чтобы иметь возможность показывать любые, даже
очень большие объекты.
Окно-объект Draw может создавать графические объекты различ¬
ных типов, используя имя класса, хранящееся в экземплярной
переменной ObjectDraw, в зависимости от того, какой инструмент
выбрал пользователь в палитре инструментов. Выбранный графи¬
ческий объект всегда запоминается в экземплярной переменной
Draw под названием TheChosen.
ObjectDraw
247
Метод toolBoox в ObjectDraw записывается так:
Def toolBox(self)
{
Atool;
}
Он просто возвращает значение экземплярной переменной tool.
Метод toolBox используется методом beginDraw из Draw. Метод
beginDraw содержит четыре оператора case. В случае, когда ни
один из case-onepaTopoB не сработает, нужно выполнить следую¬
щую инструкцию:
theGraphic := build(toolBox(parent), aPaint, aPaint);
Это сообщение предоставляет класс графического объекта, кото¬
рый должен рисоваться как аргумент для конструируемого метода.
Отличный пример анимации при помощи ObjectGraphics представ¬
ляет собой метод spinDividers из класса AboutObjGfx.
Def spin(Dividers(self index2)
{
associate(port, self);
loop
alive(self) cand checkMessage();
while alive(self)
begin
fastDraw(centerAt(frames[index], 98@153), port);
index2 := if (index 5) index - 6
else index + 6
endif;
fastDraw(centerAt(frames[index2], 1906153], port);
if (index := index + 1) > 11
index := 0;
endif;
delay(self, 5);
endLoop;
dissaciate(port);
}
/* Сообщение для перерисовывания нового стиля.
Реагируют перерисовкой:
1. Окошко вокруг ”мира”, чтобы все выглядело как страница.
2. Если надо, то сетка графического пространства.
3. Объект Picture.
4. Если надо, маркеры объектов (marking handles).
*/
Def gPaint(self, aPort, badRect page)
{
page := asRextangle(worldRect(space));
setPattern(brush(page), #invisible);
setPen(page, normal(Pen, primary(Color, #block)));
setCombo(pen(page), #nxor);
draw(page, aPort);
/*
setColor(pen(page), primary(Color, #blue));
setStyle(pen(page), #dot);
248
Программирование при помощи ObjectGraphics
inflate(page,
*/
if isMenuCheckd(parent, DW_GRID)
drawGrid(aPort)?
endif;
draw(picture, aPort);
draw(theMarks, aPor t);
Call SetScrollPos (hWnd, SB_HORZ,
left (mappmgRect (space)) , 1) ;
Call SetScrollPos (hWnd, SB_VERT,
top(mappingRect(space)) , 1) ;
Если у вас остались некоторые неясные места в данной главе, то
этого и следовало ожидать. Некоторые из концепций , на которых
базируется ObjectGraphics, мы еще не объясняли. Их описания
будут даны в последующих разделах. Поэтому было бы неплохо
вернуться к этому разделу и перечитать его после того, как
прочтете данную главу до конца. После этого некоторые вещи,
которые были не ясны с первого раза, приобретут гораздо больше
смысла.
■ РАСШИРЕННЫЙ АКТОР
Перед тем как двигаться дальше, я должен заметить, что
ObjectGraphics вносит несколько важных изменений в систему
Актор. При загрузке классов поддержки в классах Актора Window
и Printer выполняются существенные модификации, что, как
можно догадаться, влияет на имеющиеся программы. В Window
добавляются четыре новых экземплярных переменных: port,
space, picture и cursor. Это важные элементы данных, которые
используются при создании окон, поддерживающих протоколы
ObjectGraphics. Одним из важнейших отличий является то, что
когда переменцая port некоторого окна инициализирована путем
присваивания ей некоторого порта вместо paint, автоматически
для модификации окна становится действующим сообщение
gPaint. Наиболее существенной особенностью, которая обеспечи¬
вает новые возможности библиотеки классов ObjectGraphics, яв¬
ляется присваивание графического пространства и порта окну.
Присваивание объектов экземплярной переменной picture из
Window позволяет обеспечить их автоматическую перерисовку, но
это не так существенно, как присваивание окну порта и графиче¬
ского пространства.
ObjectGraphics добавляет более тридцати нозых методов объекта в
класс Window. Это могло бы быть основанием для создания нового
отдельного класса графических окон, но у группы Whitewater
вполне определенные намерения сделать так, чтобы для всех окон
были доступны средства ObjectGraphics. Аннотированный список
Фильтры платформ
249
новых методов класса Window приведен в справочном разделе в
конце главы. Как мы уже заметили, одним из наиболее важных
новых методов является gPaint. Для целей графики он заменяет
старый метод paint класса Window.
■ ФИЛЬТРЫ ПЛАТФОРМ
Фильтры платформ представляют собой слой программ, который
обеспечивает аппаратно-независимую работу графики в специфи¬
ческих аппаратных средах. Главными классами фильтров в
ObjectGraphics являются: Port, PhysicalPort, Bitmap и Color. Весь
графический вывод в ObjectGraphics как на экран, так и в виде
твердой копии выполняется через классы Port и PhysicalPort.
Любой объект Port имеет графическое пространство, связанное с
ним, и все рисование в данном объекте выполняется по отношению
к этому графическому пространству. Port — это абстрактный
класс, который является аппаратно-независимым. Реальный ин¬
терфейс с аппаратным дисплеем осуществляется потомком класса
Port, называемым PhysicalPort.
Эти классы реализованы таким образом, что объектно-ориентиро-
ванный подход позволяет упрятать аппаратную зависимость еще
дальше, чем это делает сама система Windows. Хотя с точки зрения
программиста, текст программы выглядит так, как будто иници¬
ализируется класс Port, реально создается лишь экземпляр класса
Таблица 8.1. Экземплярные переменные класса Port
theDisplay
Текущая связь
thePen
Текущее перо
theBrush
Текущая кисть
theTPen
Текущий шрифт
theSpace
Текущее графическое пространство
invRect
Текущий прямоугольник изменений
invRegion
Текущая область изменений
palette
Цветовая палитра (ОС)
Таблица 8.2. Экземплярные переменные класса PhysicalPort
hDC
Дескриптор Window, Printer или Bitmap,
с которыми связан данный Port
hBrush
Дескриптор текущей кисти
hPalette
Дескриптор текущей палитры
stockPen
Стандартное перо платформы
stockBrush
Стандартная кисть платформы
stockFont
Стандартный шрифт платформы
250
Программирование при помощи ObjectGraphics
PhysicalPort. Таким образом, специфические аппаратные аспекты
PhysicalPort никак не попадают в программы приложений.
■ КЛАСС COLOR
Класс Color в ObjectGraphics имеет четыре экземплярные перемен¬
ные: red, green, blue (красный, зеленый, синий) и physical. Кроме
того, он использует переменную класса $Primaries, которая в
действительности представляет собой символьный цветовой сло¬
варь с символьными ключами для основных цветов. Помимо этого
класс Color имеет интересный набор методов класса: addSymColor,
primary, read и RGB. Подход к манипулированию с цветом в
ObjectGraphics сочетается с основными цветами RGB (red, green,
blue) и основными цветами CMY (cyan, magenta, yellow), так же,
как и с черно-белыми вариантами, которые используются в боль¬
шинстве систем, базирующихся на MS-DOS.
Однако цветовая система ObjectGraphics при этом еще и расширяема
в том смысле, что вы вправе добавить новые символы и значения их
основых цветовых компонентов в символьный цветовой словарь. Вы
можете определить новый цветовой символ при помощи метода
класса RGB и использовать метод класса addSymColor для того, чтобы
корректно добавить его в цветовой словарь.
■ БИТОВЫЕ КАРТЫ
Средства для непосредственной работы с битовыми картами в
объектно-ориентированной манере предоставляются классами:
LogBitmap, Bitmap и HugeBitmap. Для того чтобы бегло обрисо¬
вать содержание этих классов, можно рассмотреть их экземпляр¬
ные переменные. Естественно, что сообщения этих классов также
важны, но здесь мы ограничимся лишь кратким описанием.
Сообщение asBitmap позволяет конвертировать любой графиче¬
ский объект в соответствующий Bitmap объект — битовую карту.
Объекты классов Bitmap и HugeBitmap — это лишь объекты из
библиотеки классов ObjectGraphics. В пределах библиотеки клас¬
сов ObjectGraphics только объекты классов Bitmap и HugeBitmap
представляют одновременно визуализируемые изображения и ус¬
тройства вывода, при помощи которых происходит рисование.
■ ПРЯМОУГОЛЬНИКИ
Роль прямоугольников как в Windows, так и в ObjectGraphics
велика, и поэтому совсем не удивляет то, что с этими объектами
работают два новых класса. Класс Rectangle заменяет класс Rect,
который ранее использовался в графике Актора. Класс MathRect
принадлежит к расширению ObjectGraphics, его появление связа¬
но с одной новой идеей. Новый класс понадобился для того, чтобы
Формы
251
Таблица 8.3. Значения цветовых составляющих в RGB
Символ
Красный
Зеленый
Синий
#black
0
0
0
#white
255
255
255
#red
255
0
0
#green
0
255
0
#blue
0
0
255
#cyan
0
255
255
#magenta
255
0
255
#yellow
255
255
0
Таблица 8.4. Экземплярные переменные класса LogBitmap
height
Число пикселов по высоте
width
Число пикселов по ширине
scanBytes
Размер данных на строку сканирования (scanline)
bitsPixel
Размер данных на пиксел
planes
Число цветовых плоскостей
clipRect
Локальный буферный прямоугольник
bits
Биты изображения
brush
Кисть фона
color
#mono или #color
theSpace
Графическое пространство объекта Bitmap
pen
Перо переднего плана
отразить математические аспекты понятия "прямоугольник”. Ме¬
тод asMathRect класса Rectangle предназначен для возвращения
для каждого экземпляра Rectangle эквивалентного ему объекта
MathRect. Прямоугольники — объекты одной и той же формы, и
до некоторой степени это утверждение верно, но то обстоятельство,
что различные объекты имеют различные аспекты рассмотре¬
ния — математические и изобразительные, несколько усложняет
ситуацию. Несмотря на то, что работа в ObjectGraphics с прямо¬
угольниками и не самоочевидна, она и не особенно сложна,
объекты Rectangle и их потомки являются потомками класса
Shape (формы) и должны следовать протоколу, определенному в
этом классе. Поэтому имеет смысл, перед тем как двигаться
дальше, остановиться на вопросах работы с формами.
■ ФОРМЫ
Класс Shape обеспечивает базис для всех графических объектов,
которые изображаются последовательным вычерчиванием точек
при помощи объекта Pen и затем заполнением объектом Brusl-
252
Программирование при помощи ObjectGraphics
внутреннего пространства, ограниченного данной формой. Объек¬
ты Shape имеют две новых экземплярных переменных pen и brush
и, кроме того, origin и corner, которые наследуются из класса
Graphics. Для того чтобы сообщить отправителю, какой из мето¬
дов класса PhysicalPort используется для их рисования, потомки
класса Shape применяют сообщение drawMethod. Например, при
помощи своего сообщения drawMethod объект Rectangle возвраща¬
ет метод drawRect из класса PhysicalPort, используемый для
вывода прямоугольников на экран.
■ ГРАФИЧЕСКИЕ ПРОСТРАНСТВА
Графические пространства управляют следующими параметрами:
направление осей, мировые размеры, система координат, единицы
измерений, рамки ограничений для точек координатной сетки,
масштабирование, пеннинг (панорамирование) и организация сет¬
ки. Пеннинг — это синоним скроллинга в контексте графики.
Коротко говоря, графические пространства управляют выдавае¬
мым на дисплей изображением как в целом, так и в отдельных
деталях. Объекты GraphSpace состоят из трех различных прямо¬
угольников, все они являются экземплярами класса MathRect. Это
мировой прямоугольник, отображающий прямоугольник и прямо¬
угольник-дисплей. В отличие от объектов Rectangle MathRect-объ-
екты нельзя нарисовать на экране. Они используются для
представления прямоугольников в их реальных размерах, а не в
тех, в которых они будут изображаться на дисплее. Хотя графи¬
ческое пространство определяет графический мир, этот мир в
действительности не содержит определяемые в нем объекты.
Объект GraphSpace создается путем определения мирового прямо¬
угольника. Исходная и краевая точки координат (origin и corner)
мирового прямоугольника одновременно определяют и рамки и
направление координат в этом пространстве. Если какой-либо ко¬
ординате даются отрицательные значения в мировом прямоугольнике,
то это определяет мир, в котором координаты увеличиваются в соот¬
ветствующем направлении и уменьшаются в противоположном.
Все графические объекты наследуют две экземплярные перемен¬
ные origin и corner из класса Graphic. Эти переменные использу¬
ются для хранения координат двух точек, которые определяют
прямоугольник, ограничивающий графический объект. Этот огра¬
ничивающий прямоугольник используется как главная рабочая
область для самых разнообразных манипуляций с графическими
объектами.
Для визуализации содержимого различных графических объектов
особенно удобны Инспекторы. Ниже приводится несколько сооб¬
щений, которые создают и ’’инспектируют” графический объект:
Пиктограммы
253
Таблица 8.5. Экземплярные переменные класса GraphSpace
wordRect Логический мировой прямоугольник
mappingRect Логический оконный прямоугольник
displayRect Физический оконный прямоугольник
units Единицы измерения
granularity Число тактов в единицу времени
aspectLock Значение true соответствует изотропному отображению
zoom Коэффициент распахивания в процентах
grid х@у ячейки / сетка в точках
g1 := build(Graphic, 20020, 100075);
Graphic 02- 20020
inspect(g1);
В целом для тех операций, которые доступны для любых графи¬
ческих объектов5предоставляется единообразный протокол, дис¬
циплина работы. Как и в других объектно-ориентированных
системах, имена соответствующих сообщений одинаковы, даже
если реализация для различных классов-потомков выполнена по-
разному. Так, например, любой графический объект можно пока¬
зать на дисплее при помощи сообщений draw и fastDraw. Еще
одним сообщением такого рода служит invert, оно выполняет
инвертирование цвета у графических объектов. Это сообщение
изменяет каждый из цветов объекта на его дополнительный.
■ ПИКТОГРАММЫ
Как видно из иерархии классов, Icon (пиктограммы) — это объек¬
ты, происходящие из класса Graphic и наследующие весь прото¬
кол, при помощи которого можно манипулировать с графическими
объектами. С пиктограммой вы можете делать все, что и с любым
другим графическим объектом. Кроме того, как это ни кажется
невероятным, пиктограммами можно управлять и манипулиро¬
вать при помощи ваших программ некоторым способом, который
не зависит от аппаратной и программной платформ. Класс Icon
разработан таким образом, что даже такими специфически аппа¬
ратными вещами,как пиктограммы,можно управлять в соответст¬
вии с некоторой общей запрограммированной схемой. Как и в
случае других графических объектов, вы можете использовать
сообщения build и inspect. Как вы могли уже заметить, объекты
Icon имеют дополнительную экземплярную переменную, которая
используется для хранени^ пиктограммного ресурсного скрипта
ID. Конечно, ясно, что для того, чтобы обеспечить некоторую
схему, которая будет переносима на различные платформы, тре¬
буются определенные усилия.
254
Программирование при помощи ObjectGraphics
■ ПОЛИФОРМЫ И ЛОМАНЫЕ ЛИНИИ
Объекты PolyShape (полиформы) определяют свое визуальное про¬
явление в виде коллекции точек. PolyShape имеет две визуальных
переменных: vertices, которая хранит OrderedCollection из объек¬
тов Point, и physical, которая хранит структуру данных.
PolyShape — это абстрактный или формальный класс, используе¬
мый для того, чтобы обеспечить едикый базис для своих потомков:
PolyLine (ломаная), Curve (кривая) и Polygon (многоугольник).
PolyShape реализует лишь одну новую экземплярную переменную
vertices. Кроме того, он берет на себя и все управление тем, что
наследует из класса Shape.
PolyLine (ломаная) является потомком PolyShape и может исполь¬
зоваться для создания более сложных и интересных фигур. Пред¬
ставленные ниже две группы сообщений демонстрируют типичный
пример работы с объектами PolyLine. Два сообщения size демон¬
стрируют, как сказывается действие операций добавления точек.
PL := new(PolyLine);
setOrigin(PL, 20020);
size(PL);
0
add(PL, 20020);
aad(PL, 100020);
add(PL, 20050);
size(PL);
3
■ КРИВЫЕ
Класс Curve и его потомки предоставляют важное расширение
GDI-машины MS-Windows, который сам по себе не вносит никакой
особой поддержки для кривых. Класс Curve реализует кривые при
помощи отображения в аппроксимирующую их ломаную, пред¬
ставленную объектом PolyLine. PolyLine и Curve оба представляют
собой коллекции точек. У объектов PolyLine и Polygon коллекции
точек используются для определения вершин графа, которые
соединяются отрезками прямых. В классе же Curve эти коллекции
точек используются как контрольные точки. Контрольные точки
необязательно лежат на кривой, но служат как указательные или
опорные точки для отдельных участков кривой. Экземплярная
переменная approxPoly содержит Polygon-объект, который форми¬
рует линейную аппроксимацию кривой. Экземплярная перемен¬
ная step определяет размер отрезка прямой в такой
аппроксимации. Помимо собственно Curve имеются еще и два
TWi^MKa этого класса: Bezier и Cubic. Различные подклассы Curve
огаслечивают различные способы, при помощи которых по конт¬
рольным точкам определяется форма кривой.
Рисование треугольников
255
■ РИСОВАНИЕ ТРЕУГОЛЬНИКОВ
Для такой богатой библиотеки, как ObjectGraphics, почти всегда
имеется не один способ использования в той или иной работе.
Обычно у каждого метода найдутся как преимущества, так и
недостатки. Рассмотрим простой пример с рисованием треуголь¬
ника в некотором окне. Из предшествующих рассуждений вы,
вероятно, поняли, что нарисовать треугольник при помощи класса
PolyLine должно быть довольно просто. Ниже приводится несколь¬
ко сообщений, которые позволяют нарисовать прямоугольный
треугольник:
W1 := defaultNew(Window, "Drawing а Triangle: First Way");
P1 := new(Port);
show(W1, 1);
set(W1, P1);
setPort(W1, P1);
PL := new(PolyLine);
setOrigin(PL, 50025);
add(PL, 50025);
add(PL, 300025);
add(PL, 500200);
add(PL, 50025);
associate(port(W1), W1);
draw(PL, port(W1));
dissociate(port(W1));
Для того чтобы реализовать более традиционный способ рисова¬
ния треугольников, мы могли создать отдельный класс Triangle
как прямой потомок PolyLine с экземплярными переменными
pointl, point2 и point3. С этой целью мы вероятно дополнили бы
класс пятью следующими методами:
/* устанавливает первую точку */
Def firstPoint(self, aPoint)
{
point1 := aPoint;
}
/* устанавливает вторую точку */
Def secondPoint(self, aPoint)
{
point2 := aPoint;
}
/* устанавливает третью точку */
Def thirdPoint(self, aPoint)
{
point3 := aPoint;
}
/* добавляет три точки в PolyLine и возвращается в исходную точку */
Def triangle(self)
{
add(self, point1);
add(self, point2);
add(self, point3);
256
Программирование при помощи ObjectGraphics
add(self, point1);
}
/* определяет, какой метод будет использоваться для рисования */
Def drawMethod(self)
{
А #drawPolyLyne
}
Сообщения, которые при помощи класса Triangle рисуют тот же
самый прямоугольный треугольник, имеют следующий вид:
W1 := defauItNew(Wmdow, "Drawing а Triangle: Second Way");
P1 := new(Port);
show(W1, 1);
set(W1, P1);
TR := new(Triangle);
firstPoint(TR, 50020);
secondPoint(TR, 300025);
thirdPoint(PL, 500200);
triangle(TR);
associate(port(W1), W1);
draw(TR, port(W1));
dissociate(port(W1));
Шестой метод для Triangle соберет все эти шаги в один, так, что
треугольник можно будет описать одним сообщением:
Def createTriangle(self, aPoint1, aPoint2, aPoint3)
{
pomt1 := aPoint1;
point2 := aPoint2;
pomt3 := aPoint3;
triangle(self);
}
W1 := defaultNew(Window, "Drawing а Triangle: Third Way");
P1 := new(Port);
show(W1, 1);
set(W1, ?1);
TR := new(Triangle);
createTnangle (TR, 50025, 300025, 500200);
associate(port(W1), W1);
draw(TR, port(W1));
dissociate(port(W1));
При необходимости можно построить класс TriangleWindow с
автоматическим диалогом, который, как только открывается ок¬
но, запрашивает точки для нового треугольника. Вся программа
в этом случае была бы сосредоточена в методе init, тогда для
создания окна потребовались бы только два сообщения:
TW := defaultNew(TriangleWmdow, "Draw а Triangle") ;
show(TW, 1);
Такой же стратегией можно было бы воспользоваться для рисова¬
ния параллелограммов. Листинг, приведенный ниже, демонстри¬
рует простую программу, реализующую первый вариант
определения класса параллелограммов.
Рисование треугольников
257
/*********************************/и
/* Файл класса Parallelogram */!!
/* Первая версия */!!
/*********************************/Ц
inherit(PolyLine, #Parallelogram, #(origin corner
originAngle secondAngle pen brush side1 side2), 2, nil)!!
now(class(Parallelogram))!!
now(Parallelogram)I!
/* задать точку начала (origin) */
Def setOrigin(self, aPoint)
{
add(self, aPoint);
}
! I
/* задать оставшиеся точки */
Def setPoints(self);
{
secondPoint(self) ;
thirdPoint(self) ;
fourthPoint(self)
} ! I
/* угол угол прц начальной точке */
Def originAngle(self)
{
origmAngle (self)
} ! !
/* вернуть дополнительный угол */
Def secondAngle(self)
{
AsecondAngle
}!!
/* вернуть первую сторону */
Def side1(self)
{
Aside1
}!!
/* вернуть вторую сторону */
Def side2(self)
{
Aside2
}!!
/* задать точку для возвращения */
Def endPomt(self)
{
add(self, at(vertices(self), 0));
} ! !
/* задать четвертую точку */
Def fourthPomt(self)
{
add(self, point(x(at(vertices(self), 0))
- (side2(self) * cos(degToRad(secondAngle))),
y(at(vertices(self), 2))));
9-857
258
Программирование при помощи ObjectGraphics
}П
/* задать третью точку */
Def thirdPoint(self)
{
add(self, point(x(at(vertices(self), 1))
- (side2(self) * cos(degToRad(secondAngle)),
(side2(self) * sin(degToRad(secondAngle))
+ y(at(vertices(self), 1))));
}!!
/* задать вторую точку */
Def secondPoint(self)
{
add(self, pomt(side1 + x(at(vertices(self), 0)),
y(at(vertices(self), 0)) ));
}!!
/* задать углы */
Def setAngles(self, a1)
{
origmAngle := a1;
secondAngle := 180 - a1;
}!!
/* задать стороны */
Def setSides(self, s1, s2)
{
side1 := s1;
side2 := s2;
}!!
/* метод рисования для использования в классе PhysicalPort */
Def drawMethod(self)
{
А #drawPolyLme
}!!
Как вы увидите, если попытаетесь реализовать класс Parallelogram
таким образом, он предлагает отнюдь не лучший способдля
решения данной проблемы. Хотя в принципе способ правильный,
но тригонометрические вычисления делают рисование слишком
чувствительным к начальным параметрам этого метода. Дополни¬
тельной трудностью может стать требование определять паралле¬
лограмм лишь через две стороны и угол между ними. Для более
точного и эффективного решения этой проблемы можно применять
различные методы, например использовать тригонометрические
таблицы. Читатель приглашается поэкспериментировать с мето¬
дами, которые, сохранив общий замысел, улучшат точность и
скорость тригонометрических вычислений.
■ РИСУНКИ (PICTURES)
Класс Picture расширяет поведение и протоколы графических
объектов на случай коллекций таких объектов. Он делает это,
вводя экземплярную переменную elements, которая хранит объек¬
Области (Region)
259
ты OrderedCollection. Эти графические коллекции являются мно¬
гоуровневыми, состоящими из вложенных гнезд, и ключевой
проблемой разработки мощных интерактивных объектно-ориенти-
рованных графических приложений является изучение методов
обработки таких коллекций, позволяющих делать это искусно и
эффективно. Версия сообщения asBitmap может преобразовывать
целые коллекции графических объектов в объекты Bitmap.
Если к объекту Picture посылается сообщение draw, то это сооб¬
щение распространяется (ретранслируется) далее на все графиче¬
ские элементы данной коллекции.
pic1 := new(Picture);
Picture0O 0@0
Объекты Picture придерживаются стандартного протокола из
одиннадцати сообщений OrderedCollection, который включает в
себя следующие сообщения: add, insert, put, at, find, first, last,
remove, collect, do и extract.
■ ОБЛАСТИ (REGION)
Объекты класса Region имеют широкий спектр использования,
включая нацеленное (hit) тестирование, обрезание (clipping) и
обратное движение. Метод boundsRegion класса Graphic, который
создает и возвращает объекты Region, определяется просто:
Def boundsRegion(self aRgn)
{
Таблица 8.6. Экземплярные переменные класса Pen
width
Ширина пера
color
Цвет объекта — Color
style
Стиль линии — Symbolic
combo
Комбинированный режим — Symbolic
transparent
Пустой/заполненный — булевская величина
Таблица 8.7. Экземплярные переменные класса TextPen
color
Цвет текста — Color
backColor
Цвет фона — Color
style
Гарнитура шрифта — Symbol
height
Высота в мировых единицах
font
Шрифт — Symbol
combo
Комбинированный режим — Symbol
transparent
Прозрачность — Symbol
dirty
Булевский флаг, который указывает на то, что
изменился некоторый атрибут, влияющий на
размеры изображения
260
Программирование при помощи ObjectGraphics
add(aRgn := new(Region), self);
AaRgn;
}
К сожалению, объекты WinPolygon нельзя собирать в коллекции
Picture, т.к. они не являются потомками класса Graphic. В объект
Picture можно включать только потомков класса Graphic.
Объекты RichText — это особый вид потомков Picture, которые
ограничивают членство своей коллекции только объектами Label
и объектами RichText.
■ ПАЛИТРЫ
Класс Palette представляет собой общий механизм для построения
панелей из пиктограммных кнопок, которые бывают на toolbox-
панели, или цветовых палитр. Он использует две переменные
класса: $Maps для запоминания имен пиктограммных кнопок и
$IDs для хранения их ГО-номеров.
$Мар := #("chooser", "zoomer", "line", "rect", "roundRect",
"circle", "polygon", "polyline", "curve", "icon",
"cropper", "text");
Метод beginDrag класса Palette иллюстрирует работу с экземпляр¬
ной переменной picture.
Def beginDrag(self, wP, aPoint)
{
if selectTool(self, contains(picture, aPoint))
command(parent, $IDs[find(picture, selectedTool)], 0L);
endif;
}
■ ГРУППИРОВКИ
Одной из наиболее широко используемых и мощных возможностей
библиотеки ObjectGraphics являются средства поддержки интерак¬
тивной группировки и ”разгруппировки” графических объектов.
Это выполняется при помощи методов groupChoice и
unGroupChoice класса Draw. Метод groupChoice определяется так:
Def groupChoice(self)
{
add(picture, theChosen);
do(theChosen,
{ using(aGraphic)
remove(picture, aGraphic);
});
updateChoise(self);
}
С другой стороны, определение unGroupChoice таково:
Def unGroupChoice(self)
{
Группировки
261
do(theChosen,
{ using(aGraphic)
if isAncestor(class(aGraphic, Picture)
remove(picture, aGraphic);
do(aGraphic,
{ using(element)
add(picture, element);
}?
endif;
}) ?
updateChoise(self);
}
start(new(ObjectDraw));
Для того, чтобы дать пользователям возможность задавать основ¬
ные параметры рисования для графического пространства, в
ObjectDraw используется объект DimensionDlg. Вы можете сме¬
нить единицы измерения, установить параметр для зернистости
(granularity) и специфицировать ширину и высоту страницы в
Таблица 8.8. Экземплярные переменные класса Draw
theGraphic
Объект перемещается
thePoly
Полиформа перемещается -PolyShape
theChosen
Выбранные графические объекты — Pic
theMarks
Маркер дескриптора — PolyMark
startStop
Состояние таймера
editor
Текстовый редактор для меток — Text
clipRect
Используется для сборки битовой карты
fName
Имя файла — String
startPt
Перемещение началось отсюда
marker
Используется для маркировки объектов
при выборе — Graphic
Таблица 8.9. Экземплярные переменные класса ObjectDraw
display
Объект Draw
hRuler
Горизонтальное окно с масштабной линейкой
vRuler
Вертикальное окно с масштабной линейкой
toolBox
Графическая палитра
tool
Текущий инструмент
colorDlg
RGB-диалог
dimDlg
Измерение Dlg
thePen
Перо по умолчанию
theBrush
Кисть по умолчанию
theTPen
Текстовое перо по умолчанию
fileDlg
Файловый открытый диалог
262
Программирование при помощи ObjectGraphics
графическом пространстве. Метод sizeKids класса ObjectDraw ис¬
пользует такой оператор:
setCRect(toolBox, rect(0, 0, 25, bottom(cRect)));
setCRect — это метод, наследуемый из класса WindowsObject. Он
задает размер прямоугольника окна. Аргумент cRect в сообщении
bottom — это экземплярная переменная, которая также наследу¬
ется из WindowsObject.
$Colors := static(%Dictionary(
810 -> #black 811 - #white
812 -> #red 813 - #green
814 ->#blue 815 - #cyan
816 ->#magenta 817 - #yellow));
$BrushStyles := static(%Dictionary(
761 ->#solid 762 - #invisiole
763 ->#ten 764 - #fifteen
765 ->#thirty 766 - #fifty
767 ->#seventy 768 - #e~gnty5
769 ->#vertical 770 - #horizontal
7 71 ->#diagonal 7 72 - #hatch
773 ->#pebble 774 - #brick));!!
colorBox := newChild(Palette, 1040, self, nil);
cbBrush := new(Brush);
colors := new(Picture);
patterns := new(Picture);
■ ПРИМЕНЕНИЕ ИНСТРУМЕНТОВ ФОРМИРОВАНИЯ
ИЗОБРАЖЕНИЯ
Использование инструментов, формирующих изображения для
рисования в некотором окне, относительно просто. Должен быть
инициализирован порт, выбраны перья, кисти и цвета. После того
как эти приготовления сделаны, можно нарисовать любой из
графических объектов, имеющихся в наборе. Ниже приводятся
два примера использования инструментов ObjectGraphics для ри¬
сования прямоугольника в окне. Второй пример иллюстрирует
использование объекта Brush.
W1 := defaultNew(Window, "Graphics Drawing");
P1 := new(Port);
setPort(W1, P1);
p1 := new(Pen);
setColor(p1, primary(Color, #red));
setPen(port(wD, p1);
show(W1, 1) ;
R1 := new(Rectangle);
setOrigin(R1, 120@20);
setCorner(R1/ 200@100);
associate(port(W1), W1);
draw(R1, port(W1));
dissociate(port(W1));
Применение инструментов формирования изображения
263
W1 := defaultNew(Window, "Graphics Drawing");
P1 := new(Port);
setPort(W1, P1);
show(W1, 1);
R1 := new(Rectangle);
setOrigin(R1, 120020);
setCorner(R1, 2000100);
B1 := new(Brush);
setBrush(R1, B1);
setColor(brush(R1), RGB(Color, 255, 0,0));
associate(port(W1), W1);
draw(R1, port(W1));
dissociate(port(W1));
Конец данного раздела посвящен различным способам получения
одних и тех же рисунков при значительном сокращении длины
программ. Если хотите, мы пройдем путь от поразительно малых
до грандиозных результатов. Задача, которой мы займемся, край¬
не проста: надо нарисовать круговую мишень (такого типа мишени
вы могли видеть в стрелковом тире). Первая версия программы
будет сделана прямым методом, громоздко и многословно (puc. 8.1).
Все действия выполняются методом init класса TargetWindow:
/* инициализировать экземпляр класса Target
методом прямым, неэффектвным */
Def init(self)
{
port := new(Port);
picture1 := build(Ellipse, 000, 2000200)
picture2 := build(Ellipse, 000, 1800180)
picture3 := build(Ellipse, 000, 1600160)
picture4 := build(EHipse, 000, 1400140)
picture5 := build(EHipse, 000, 1200120)
picture6 := build(Ellipse, 000, 1000100)
picture7 := build(Ellipse, 000, 80080);
picture8 := build(Ellipse, 000, 60060);
picture9 := build(Ellipse, 000, 40040);
setColor(brush(picture1), primary(Color, #red));
setStyle(pen(picture1), #invisible);
setColor(brush(picture2), primary(Color, #white));
setStyle(pen(picture2), #invisible);
'setColor(brush(picture3), primary(Color, #red));
setStyle(pen(picture3), #invisible);
setColor(brush(picture4), primary(Color, #white));
setStyle(pen(picture4), #invisible);
setColor(brush(picture5), primary(Color, #red));
setStyle(pen(picture5), #invisible);
setColor(brush(picture6), primary(Color, #white));
setStyle(pen(picture6), #invisible);
setColor(brush(picture7), primary(Color, #red));
setStyle(pen(picture7), #invisible);
setColor(brush(picture8), primary(Color, #white));
setStyle(pen(picture8), #invisible);
setColor(brush(picture9), primary(Color, #red));
setStyle(pen(picture9), #invisible);
264
Программирование при помощи ObjectGraphics
Рис. 8.1. Изображение мишени
setCursor(self, #finger);
^self
}
Для того чтобы быть уверенными, что мишень будет перерисова¬
на, что бы ни случилось с окном, мы напишем следующий метод
gPaint:
/* перерисовать объекты в окне */
Def gPaint(self, aPort, invalidRect bullsEye)
{
centerAt(picture, bullsEye);
centerAt(picture2, bullsEye);
centerAt(picture3, bullsEye);
centerAt(picture4, bullsEye);
centerAt(picture5, bullsEye);
centerAt(picture6, bullsEye);
centerAt(picture7, bullsEye);
centerAt(picture8, bullsEye);
cer.terAt (picture9, bullsEye) ;
draw(picture, aPort);
draw(picture2, aPort);
draw(picture3, aPort);
draw(picture4, aPort);
draw(picture5, aPort);
draw(picture6, aPort);
draw(picture7, aPort);
draw(picture8, aPort);
Применение инструментов формирования изображения
265
draw(picture9, aPort);
}
Теперь, пока эта программа работает, мы можем с уверенностью
заявить, что выбрали не лучший способ решения нашей задачи.
Но наверняка в процессе усовершенствования программы мы
сумеем найти какие-нибудь мощные возможности класса Picture
и эффективнейшие приемы их использования.
В следующей версии инициализируется экземплярная переменная
picture и при помощи сообщений add изображения, построенные
сообщениями build для эллипсов добавляются к рисунку.
/* инициализировать экземпляр Target */
Def init(self)
{
port := new(Port);
picture := build(Picture, 40040);
add(picture, build(Ellipse, 000, 2000200));
add(picture, build(Ellipse, 000, 18O018O));
add(picture, build(Ellipse, 000, 16O016O));
add(picture, build(Ellipse, 000, 14O014O));
add(picture, build(Ellipse, 000, 1200120));
add(picture, build(Ellipse, 000, 1000100));
add(picture, build(Ellipse, 000, 8O08O));
add(picture, build(Ellipse, 000, 6O06O));
add(picture, build(Ellipse, 000, 4O04O));
setColor(brush(picturel), primary(Color, #red));
setStyle(pen(picturel) , #invisible);
setColor(brush(picture2), primary(Color, #white));
setStyle(pen(picture2), #invisible);
setColor(brush(picture3), primary(Color, #red));
setStyle(pen(picture3) , #invisible);
setColor(brush(picture4), primary(Color, #white));
setStyle(pen(picture4), #invisible);
setColor(brush(picture5), primary(Color, #red));
setStyle(pen(picture5) , #invisible);
setColor(brush(picture6), primary(Color, #white));
setStyle(pen(picture6), #invisible);
setColor(brush(picture7), primary(Color, #red));
setStyle(pen(picture7), #invisible);
setColor(brushXpicture8), primary(Color, #white));
setStyle(pen(picture8) , #invisible);
setColor(brush(picture9), primary(Color, #red));
setStyle(pen(picture9) , #invisible);
setCursor(self, #finger);
^self
}
Def gPaint(self, invalidRect bullsEye)
{
bullsEye := point(right(displayRect(self))/2,
bottom(displayRect(self))/2);
alignCenter(picture, bullsEye);
draw(picture, aPort);
}
266
Программирование при помощи ObjectGraphics
Окончательная версия сожмет программу еще сильнее за счет
использования двух циклов, которые придут на смену тотальному
вызову инструментов формирования изображения прямым мето¬
дом.
/* инициализировать экземпляр Target */
Def init(self i j)
{
port := new(Port);
picture := build(Picture, 40040);
add(picture, build(Ellipse, 000, 2000200));
add(picture, build(Ellipse, 000, 18O018O));
add(picture, build(Ellipse, 000, 1600160));
add(picture, build(Ellipse, 000, 1400140));
add(picture, build(Ellipse, 000,' 12O012O));
add(picture, build(Ellipse, 000, 1000100));
add(picture, build(Ellipse, 000, 80080));
add(picture, build(Ellipse, 000, 6O06O));
add(picture, build(Ellipse, 000, 4O04O));
{
i := 0;
loop
while i 9
setColor(brush(at(picture, i)), primary(Color,
#red));
i := i + 2;
endLoop;
};
{
j := 1;
loop
while j 9
setColor(brush(at(picture, j)), primary(Color,
#white));
j := j + 2;
endLoop;
};
do(picture,
{ using(pic) setStyle(pen(at(picture, pic)),
#invisible) });
setCursor(self, #finger);
^self
}
■ ПОЛИМАРКИ
ObjectGraphics представляет класс, который называется
PolyMark. Он используется для создания объектов, которые можно
увидеть в окне, но они не будут являться частью законченного
рисунка фис. 8.2). Полимарки — это объекты, состоящие из
коллекций маленьких прямоугольничков, окружностей или дру¬
гих графических элементов, которые используются для маркиров¬
ки границ других графических объектов. PolyMark разработан с
расчетом на то, чтобы использовать другие графические элементы,
Полимарки
267
Рис. 8.2. Квадратсчетырьмяполимарками
которые создаются стандартным способом, затем размножать их в
необходимых количествах и расставлять по узлам (вершинам)
графических объектов. Некоторые из методов в классе Polymark
являются методами класса. Кроме того, используется и перемен¬
ная класса, названная $StdSize. Это означает, что в любой момент
времени используется только один стандартный размер для всех
полимарок. Сообщение standardMark используется для получения
размеров стандартного маркерного объекта, измеренного в пиксе¬
лах.
Следующий пример демонстрирует рисование квадрата и последу¬
ющую маркировку каждой из его вершин при помощи квадратных
маркеров:
W1 := defaultNew(Window, "Marking а Square with
PolyMarks");
P1 := new(Port);
show(Wl, 1);
R1 := build(Rectangle, 20020, 1000100);
setPort(Wl, P1);
Markl := build(Rectangle, 20020, stand¬
ardMark (PolyMark));
Pmarkl := new(PolyMark);
268
Программирование при помощи ObjectGraphics
setMark(Pmark1, Mark1);
add(Pmark1, origin(R1));
add(Pmark1, corner(R1));
add(Pmark1, point(left(R1), bottom(R1)));
add(Pmark1, point(right(R1), top(R1)));
associate(port(W1), W1);
draw(Rl, port(W1));
draw(Pmark1, port(W1));
dissociate(port(W1));
■ ИЕРАРХИЯ КЛАССА OBJECTGRAPHICS
Object
GraphSpace Включает в себя оси координат, размер мира
и единицы измерения, координатные
ограничения на точки сетки.
GraphSpace может для окон, связанных с ним,
преобразовывать логические координаты в
физические
Pen Реализует логическое перо для рисования как
текстов, так и рисунков
TextPen Специализация класса Pen для текстов
Brush Реализует логические графические кисти,
используемые для заполнения Shape-объектов
Color Реализует независимую от платформы
стандартную спецификацию цвета, которая
является разновидностью
общеупотребительной цветовой модели RGB.
Каждый из трех основных цветов
представляется целым в диапазоне от 0 до 255
IndexColor Позволяет специфицировать цвет при помощи
указателя внутри какой-нибудь цветовой
палитры, установленной для порта, где эти
цвета используются
Graphic Формальный класс, который объединяет все
объекты, рисующие себя сами, порт дается
в качестве аргумента
Icon Реализует рисуемые пиктограммы
LogBitmap Реализует независимый от платформы
режим работы с частями битовой карты
Bitmap Реализует независимый от платформы
режим работы с частями битовой карты
Иерархия класса ObjectGraphics
269
ObjectGraphics
HugeBitmap
Shape
Picture
PolyMark
Region
RichText
Rectangle
Chooser
MathRect
RoundRect
PolyShape
Curve
Bezier
Cubic
Port
Поддерживает битовые карты размером
более 16 Кбайт
Объединяет все объекты — потомки Graphic,
которые рисуют себя, очерчивая границы
при помощи Реп-объектов, а затем
закрашиваются при помощи объектов Brush
Содержит коллекцию упорядоченных по
вложенности объектов из Graphic.
Полностью поддерживает протокол
OrderedCollection, так же как и протоколы
Pen и Brush
Приспособления, которые используются для
маркировки углов и вершин в других
рисунках
Реализует области как некоторые коллекции
замкнутых изображений
Реализует коллекции атрибутированных
текстовых меток
Реализует объекты прямоугольной формы
Реализует интерактивный режим выбора
Используется для математических действий,
а не для рисования. Поскольку такие
объекты не создают и не используют Pen
или Brush, к ним не применимы сообщения
draw и fastDraw
Реализует прямоугольники с закругленными
краями с регулируемыми радиусами
кривизны
Объединяет потомков Shape, которые
определяются как коллекции точек (Point)
Объединяет все кривые линии в
ObjectGraphics
Создает и рисует параметрическую
кубическую кривую Безье
Создает и рисует параметрическую
кубическую кривую
Обеспечивает работу дисплея в оконной
среде
270
Программирование при помощи ObjectGraphics
PhysicalPort Реализует ту часть взаимодействия между
Port и PhysicalPort для MS-Windows,
которая является специфической для
различных машин
■ РАСШИРЕНИЯ КЛАССА WINDOW
В OBJECTGRAPHICS
Методы объектов:
attachPort Каждое устройство вывода реализует метод
attachPort, который вместе с PhysicalPort:
associate подготавливает его к рисованию.
(См. Bitmap:attachPort и Printer:attachPort.)
Задает разрешение GraphSpace окружения
для того, чтобы учесть характеристики
принтера, представленного в данном
контексте дисплея
Создает новый оконный класс Struct.
Переключение символьного курсора
требует нулевого значения со смещением 14,
при этом курсор будет пустым
Очищает контрольные маркеры (checkmarks)
в части меню, определенной при помощи
low и high IDs. Затем проверяет menuID
Создает окно в MS-Windows в соответствии
с параметрами, указанными в аргументах.
Аргумент style определяет стиль нового окна
Освобождает контекст дисплея для self-объекта,
Port отключает окно от устройства
Определяет, объявлен ли данный пункт
меню как контролируемый
Возвращает область прямоугольного окна.
Этот метод является частью общего протокола
устройств вывода, Который распределен
между классами Window, Bitmap и Printer
Запрашивает текущую позицию курсора
в мировых координатах
Прячет курсор
Переустанавливает позицию курсора
(в мировых координатах)
setResolution
newWClass
checkMenuGroup
create
detachPort
isMenuChecked
displayRect
getCursorPos
hideCursor
setCursorPos
Расширения класса Window в ObjectGraphics
271
fenceCursor
free
gPaint
greyMenuGroup
modMenuGroup
physicalOffset
picture
port
scroll
setBackColor
setCursor
Ограничивает движение курсора рамками
указанного прямоугольника
(в мировых координатах)
Освобождает курсор от ограничений,
наложенных ранее, после этого курсор
может двигаться по всему экрану
Сообщение для перерисовки в новой манере.
aPort — это порт, который был связан с
указателем на объект self для рисования.
badRect определяет текущий прямоугольник,
который требует модификации. ObjectGraphics
автоматически будет отвергать попытки
рисовать критический по времени рисунок
снаружи от badRect. Используйте badRect,
если хотите избавиться от ненужного
рисования в дальнейшем
Серые позиции меню (неиспользуемые)
определяются при помощи low и high IDs
Применяет метод, указанный в
модификаторе, для части меню, определяемой
при помощи low и high IDs
Возвращает координаты верхнего левого угла
для печати или рисования на данной
странице. Класс Window в отличие от Printer
не имеет физического смещения
Возвращает оконный объект Picture
Возвращает оконный объект Port
Вызывается при обслуживании оконных
портов для работы с графическими
пространствами Window и Port. Этот метод
входит в общий протокол устройств вывода
(OutputDevice)
Изменяет цвет фона всех окон данного
MS-Window класса на Color. Если параметр
nil, то фон не будет закрашиваться при
посылке сообщений WM_ERASEBKGND. На
открытых в текущий момент представителях
класса изменение скажется только после
аннулирования
Переустанавливает курсор окна
272
Программирование при помощи ObjectGraphics
showCursor
space
toggleMenuItem
unGreyMenuGroup
wait
endWait
setSpace
setPicture
setPort
Показывает курсор
Возвращает для данного окна объект
GraphSpace. Формально выдает новое Space,
запросившему пространство, если ничего не
найдет в IVar. Потомки могут переопределять
выбор из нескольких пространств,
построение специальных пространств во
время выполнения и т. п.
Для контрольных маркеров в меню,
отмеченных в menuID, меняет состояние
на противоположное
Делает доступными позиции меню,
определенные в low и high IDs
Инициирует ожидание для длинной операции
Завершает ожидание для длинной операции
Задает GraphSpace для окна
Записывает в pictureIVar значение,
предоставленное из Picture
Задает оконный объект Port. Инициализация
порта IVar открывает Актору доступ
к средствам ObjectGraphics
Глава 9
■ ПРИМЕР ПРИЛОЖЕНИЯ
В WINDOWS 3.0
Эта глава описывает некоторые широко распространенные систе¬
мы. В ней рассматриваются проблемы разработчиков графическо¬
го пользовательского интерфейса (GUI). Показывается, как
существенно может помочь техника объектно-ориентированного
программирования в разрешении этих проблем. Хотя GUI исполь¬
зует Windows 3.0 и язык Актор, описываемые здесь приемы
подходят для любых объектно-ориентированных GUI. Для того,
чтобы опробовать объектно-ориентированный подход в среде
Windows 3.0, я решил написать некоторую программу, которая
была бы полезна для меня самого, дала бы мне одновременно
возможность ’’мультидокументного” редактирования и предоста¬
вила средства контроля исполнения над всей разработкой и поль¬
зовательскими приложениями — и все это в одном основном окне.
Получившаяся программа поэтому называется Executive
Control — контроль исполнения. Данная программа является кон¬
тролером/редактором для разработчиков и рассчитана на особен¬
ности среды Windows 3.0. Хотя подобное применение кажется
разумным с точки зрения пользователя, удобство от концентрации
в одном месте таких сложных возможностей может превратиться
в настоящий кошмар для разработчика GUL
■ КРАТКИЙ ОБЗОР EXECUTIVE CONTROL
Как иной раз бывает в имеющихся на сегодняшний день GUI,
наряду с достоинствами в среде Windows встречаются и неудобст¬
ва, и несуразности. Наверное, и обычный пользователь Windows,
и программист приходят к мысли, как было бы хорошо, если бы
удалось обеспечить доступ ко всем предметам, с которыми прихо¬
дится иметь дело, в одном месте. Вопрос только в том, до какой
степени можно набивать возможностями одно командное окно, не
загромоздив его? На этот вопрос я намереваюсь ответить програм¬
274
Пример приложения в Windows 3. 0
мой. Я пришел к выводу, что использование объектно-ориентиро-
ванного подхода и приемов, доступных программистам, позволяет
создать программу, которая вместо огромного количества архи-
сложных меню и команд и наложенных друг на друга изображе¬
ний имеет лишь одно основное окно, где можно работать со всеми
вещами, с которыми сталкивается пользователь или разработчик
в ходе сеанса с Windows.
Я должен уточнить, что это не просто программа, отвечающая на
поставленный вопрос. Это программа, которую я использую сам,
и написал я ее для того, чтобы мне было легче жить. Приведенное
в этих Листингах является полезным работающим прототипом
программы, появится и коммерческая версия. Раз так, то ее очень
легко приспособить для вашей собственной системы и расширить
так, как вам нравится.
Программа Executive Control, представленная здесь, разработана
специально для того, чтобы отвечать потребностям и пользовате¬
лей, и разработчиков приложений Windows. Для создания управ¬
ляющего окна, которое немедленно может выполнить множество
программ в среде Windows из его системы меню и всё то, что можно
было бы ожидать от мультидокументного редактирования, исполь¬
зовался Multiple Document Interface (MDI) — мультидокументный
интерфейс Windows. Я пришел к выводу, что написать такую
программу, используя объектно-ориентированную среду програм¬
мирования на Акторе, сравнительно несложно.
В данной разработке пользовательского интерфейса мне пришлось
использовать лишь агрегат из popup-меню. Как видите, еще есть
много мест расширения набора вариантов и дополнительных
иерархических рорир-инструментов. Хотя все может быть выпол¬
нено и при имеющейся полоске основного меню, если необходимо,
ее легко расширить, не опасаясь загромождения. В данном прило¬
жении не было попытки увеличить область пользователя основно¬
го окна, поскольку в этом не было необходимости. Если появится
такая необходимость, сделать это будет совсем несложно.
Popup-меню настольной системы позволяет выполнять любую на¬
стольную принадлежность, указав на нее в меню. Меню приложе¬
ний Applications — это иерархическое popup-меню системы,
которое упрощает доступ к приложениям за счет того, что они
разбиты на категории. Ветвь инструментов Development, напри¬
мер, обеспечивает мгновенный доступ к набору инструментов
пакета SDK.
Приложение Executive Control строится на трех удивительно про¬
стых классах: ExecApp — подклассе класса Application;
ExecWin — подклассе класса MDIFrameWindow; и SysDialog —
специализации класса FileDialog. В объектно-ориентированных
MDI-поддержка в Акторе
275
GUI много усилий уходит на то, чтобы решить, какие классы надо
использовать или создавать. В данном случае наиболее эффектив¬
ный подход состоит в создании небольшого количества новых
классов, в которых некоторые основные методы могут переписы¬
ваться для того, чтобы обеспечить альтернативные способы их
выполнения. Хотя вначале поиски наилучшего решения могут
показаться поисками иголки в стоге сена, но когда правильное
решение найдено, обычно сразу становится ясно, что это и есть
наилучшее или, по крайней мере, весьма достойное решение
поставленной задачи.
В качестве инструмента использовались Актор 3.0, интерактивная
объектно-ориентированная система программирования, подобная
Смолтоку, но имеющая синтаксис, близкий к Си. ПосколькуАктор
3.0 позволяет в ходе диалога вызвать любую функцию Windows,
это великолепная лаборатория для изучения средств Windows 3.0.
Единственной неприятностью, которая сдерживала использование
Актора, была ограниченность памяти. Теперь, когда у вас в
Windows имеется масса мегабайтов в расширенном режиме 386-го
процессора, этой проблемы больше не существует. Четырьмя глав¬
ными преимуществами Актора на мой взгляд являются: 1) подо¬
бный Си синтаксис; 2) объектно-ориентированность; 3)
интерактивность; 4) большая библиотека классов для Windows,
уже готовая для использования.
■ МУЛЬТИДОКУМЕНТНЫЙ ИНТЕРФЕЙС (MDI)
MDI — одна из наиболее привлекательных новых сторон системы
Windows 3.0. Вместе с тем работа по овладению этими средствами
сложного взаимодействия между элементами управления фреймо¬
вых окон и рядом выбираемых дочерних окон, которые открывает
пользователь, может обернуться лишь неуклюжими попытками
как на стадии проектирования, так и на стадии реализации. Но,
как оказалось, это как раз тот круг проблем, который превосходно
решается объектно-ориентированными системами.
■ MDI-ПОДЦЕРЖКА В АКТОРЕ
Актор поставляется вместе с набором классов, которые обеспечи¬
вают полную поддержку MDI стандарта Windows. Как следует из
его названия, класс MDIFrameWindow используется для создания
фреймовых (образующих общую рамку) окон MDI. Он переадресо¬
вывает соответствующие сообщения Windows своим процедурам и
сохраняет цепочку содержащихся в нем дочерних окон. Для того
чтобы создать некоторое фреймовое окно приложения, обычно вам
требуется лишь создать потомок того класса, который содержит
подходящий командный метод. Каждый раз при обработке сооб¬
276
Пример приложения в Windows 3.0
щения, которое создает новое дочернее MDI-окно, вызывается
метод processMenu с указанием желаемого текста, названия окна,
так что меню *’Windows” в вашем фреймовом окне будет иметь
корректный список дочерних окон.
Класс MDIFileWindow является потомком MDIFrameWindow. Он
разработан для управления обработкой командных сообщений.
Класс MDIChild содержит модификации, необходимые для того,
чтобы окно могло функционировать как дочернее. Эти составляю¬
щие MDI-приложения предназначены для использования дочер¬
ним окном, являющимся потомком этого класса. Для того, чтобы
использовать MDIChild в уже существующих программах, надо
либо скопировать этот класс и сделать его потомком класса вашего
дочернего окна, либо сделать ваш класс потомком MDIChild.
Класс FileChildMDI является потомком класса FileWindow. В
FileChildMDI имеются средства, позволяющие использовать
FileWindow в MDI-приложениях. Класс MDIClient управляет со¬
зданием и сопровождением MDIClient окна. Это окно должно
создаваться с окном MDIFrameWindow как родителем и автомати¬
чески уничтожаться по умолчанию при вызове закрывающей
процедуры этого окна. И, наконец, имеется FileApp. Это простой
класс, который содержит все необходимые методы для инициали¬
зации готовой MDIFileWindow-программы. Он используется для
"распечатывания” (seal off) MDIFileWindow классов и создания
автономных приложений. Он определяет главное окно, которое
инициализирует все остальные. Это базисные классы, которые
определяют мультидокументный интерфейс в Акторе. Давайте
рассмотрим некоторые из существенных деталей их реализации.
И MDIFRAMEWINDOW
Поскольку это самый общий класс, его экземплярные переменные
являются ключом к пониманию того, как работает данный под¬
класс Window. Эти переменные приведены в табл. 9.1.
Переменная clientWindow хранит экземпляр класса MDIClient,
связанный с главным фреймовым окном. Экземпляр класса
Dictionary хранится в переменной childList для того, чтобы хра¬
нить бирки (tally) текущих открытых дочерних окон. Другая
Таблица 9.1. Экземплярные переменные MDIFrameWindow
clientWindow
Указывает на оконное пространство клиента
childList
Словарь дочерних окон
activeHWnd
Хранит цепочку активных окон
childClass
Хранит цепочку типов дочерних классов
windowMenu
Хранит окно ”Window”
MDIFileWindow
277
экземплярная переменная хранит дескриптор текущего активного
дочернего окна, за счет этого фокус управления определяется
однозначно. Экземплярная переменная windowMenu используется
для хранения пунктов динамического меню ”Window”, которое
содержит различные опции для переупорядочивания дочерних
окон.
■ MDIFILEWINDOW
Для каждого MDI-приложения должен создаваться некоторый
особый командный метод с таким расчетом, чтобы он знал, как
организовать создание нового дочернего окна MDI. После того как
MDI-дочернее окно создано, не должно быть проблем с передачей
командных инструкций командному методу этого окна.
Часть обработки команд, связанная с дочерними окнами, в про¬
грамме Executive Control локализована в методе childCommand,
который определен ниже:
Def childCommand(self, wP, 1)
{
if (wP == FILE_OPEN) or (wP == FILE_NEW)
then createMDIChild(self, childClass, "(Untitled)", nil);
command(childList[activeHWnd], wP, lP);
processMenu(self, getText(childList[activeHWnd]));
else if childList[activHWnd]
command(childList[activeHWnd, wP, lP);
endif;
endif;
exec WindowProc(self:ancestor, #WM_COMMAND, wP, lP);
}
Def init(self, cmdLine dlg fName)
{ init(self:ancestor, cmdLine);
mainWindow := newMain(MDIFileWindow, "FileEditMenu",
"File Editor”, nil);
setClassType(mainWindow, #FileChildMDI);
show(mainWindow, CmdShow);
}
Класс FileApp предоставляет средства, при помощи которых мо¬
жет быть создан MDI-редактор и использован либо в среде Актора,
либо как автономное приложение. Сообщения для его создания
весьма просты:
F := new(FileApp);
init(F, nil);
Однако существует одна проблема в случае, если требуется создать
автономное приложение или даже приложение, работающее в
среде Актора, но в окне, которое остается открытым, когда главное
окно Актора закрывается. Окно, созданное при помощи FileApp,
не является главным, таким, как окна, создаваемые сообщением
newMain в Акторе. Посылка сообщения
278
Пример приложения в Windows 3.0
F := newMain(FileApp);
вызовет ошибку, поскольку оно предназначается для инициализа¬
ции класса.
Альтернативный способ состоит в непосредственной инициализа¬
ции объекта MDIFrameWindow. Вот эта программа будет работать:
W1 := newMain(MDIFrameWindow, "adirmenu", "К", nil);
show(W1, 1);
Эта программа уже справляется с созданием главного MDI-окна.
Однако меню редактора является стандартным меню и не имеет
средств для файловых операций, а без них это, конечно, не
настоящий MDI. Даже после того как подходящая система меню
будет определена, оно все равно не будет работать, пока не будет
написан корректный командный метод для управления команда¬
ми меню. Модификация имеющегося командного метода здесь не
поможет. Программа Executive Control разработана для того,
чтобы решить проблемы, предоставив для автономных MDI-при-
ложений настоящую массовую сборку меню.
В ИЕРАРХИЯ КЛАССОВ MDI
Как уже отмечалось, приложение Executive Cotitrol построено на
трех достаточно простых наследуемых классах: ExecApp — под¬
классе класса Application; ExecWin — подклассе класса
MDIFrameWindow; и SysDialog — потомке класса FileDialog. Со¬
ответствующая ветвь иерархии классов Актора, дополненная но¬
выми классами, выглядит так:
Object
Application
ExecApp
WindowsObject
Control
MDIClient
Dialog
FileDialog
SysDialog
Window
MDIChild
MDIFrameWindow
ExecWin
TextWindow
EditWindow
WorkEdit
FileWindow
FileChildMDI
Иерархия классов MDI
279
Выполнение этих сообщений открывает главное окно
ExecutiveControl:
EW := new(ExecApp);
init(EW, nil);
Из новых специфических методов в данном приложении важными
являются два: метод init в классе ExecApp и командный метод в
классе ExecWin. Главное окно использует скомпилированный
меню-ресурс ?,ExecMenu’\ который определен в листинге 1, приве¬
денном несколько далее.
Иерархические меню создаются просто при помощи вложенных
POPUP инструкций. Текст главной программы командного метода
ExecWin имеет следующий вид:
if (wP == FILE_OPEN) or (wP == FILE_NEW)
then createMDIChild(self, childClass, "(Untitled)", nil);
command(childList[activeHWnd], wP, lP);
processMenu(self, getText(childList[activeHWnd]));
else
if childList[activeHWnd]
command(childList[activeHWnd], wP, lP);
endif;
endif;
execWindowProc(self:ancestor, WM_COMMAND, wP, lP);
Последовательность действий такова: выполняется проверка пара¬
метра сообщения WM_COMMAND wP, если оказывается, что был
’’нажат” айтем меню FileOpen, посылается сообщение
createMDIChild, которое создает в качестве дочернего окна новый
открытый файл. Это окно является экземпляром класса MDIChild.
Для рассылки команд меню во все открытые окна используется
встроенное сообщение childList.
Вложенная условная конструкция
else
if childList[activeHWnd]
command(childList[activeHWnd], wP, lP);
endif;
перед тем как послать командное сообщение, должна проверить,
существует ли дочерний объект. Оставшаяся часть командного
метода традиционна для Windows-программ. В ExecutiveControl
много таких меню, для которых по технологическим соображени¬
ям требуется расчленять командный метод на несколько команд¬
ных модулей — это единственный способ обеспечить их
компиляцию. Но, как оказывается, и в этом мы убедимся далее,
почти всегда в такой ситуации для расчленения имеются весомые
соображения и не только технологического плана.
280
Пример приложения в Windows 3.0
■ УПРАВЛЕНИЕ БОЛЬШОЙ И СЛОЖНОЙ
СИСТЕМОЙ МЕНЮ
Имеется ряд важных вопросов, которые необходимо рассмотреть
на двух различных уровнях для того, чтобы досконально разо¬
браться в дизайне пользовательского интерфейса. Прежде всего
это планировка полоски меню. Данная хорошо знакомая разбивка
полоски позволяет создавать пользовательский интерфейс, кото¬
рый может оказаться и очень хорошим и очень плохим. В связи с
этим наиболее правильным решением здесь будет потратить неко¬
торые усилия и улучшить наиболее известные варианты дизайна,
а не пытаться разработать совершенно новый подход. Но и при
этом имеется масса важных деталей, требующих уточнения в
хорошо всем известной типовой структуре.
Пример. Пункт popup-меню, связанный с некоторым серьезным
необратимым действием, должен располагаться там, где вероят¬
ность случайного нажатия будет минимальна, и должен требовать
подтверждения, чтобы дать пользователю шанс вернуться назад.
■ ИЕРАРХИЧЕСКИЕМЕНЮ
Иерархические или каскадные меню представляют весьма важ¬
ный тип меню, позволяющий упаковать множество функциональ¬
ных возможностей в единственную полоску меню и избежать при
этом ее загромождения. Однако здесь, как и при использовании
любого другого полезного средства, важно не переусердствовать.
Иерархическое меню фактически представляет собой расширенное
эквивалентное меню с целой панелью опций. Но она использует
экранное пространство для опций вне собственно панели. На мой
вкус, два иерархических меню — это максимум того, что можно
размещать в одном окне. Хотя динамические меню можно созда¬
вать и динамически и статически, оказывается, что подход со
статическим описанием ресурса-сценария для создания меню на¬
столько прост, что очень часто этим способом имеет смысл поль¬
зоваться с самого начала.
Подобно любому другому меню иерархическое может быть создано
из статически скомпилированного ресурса или по мере необходи¬
мости, динамически, используя интерфейс Windows. В Акторе
имеется несколько различных способов динамического создания
меню. Ниже приводится пример динамического иерархического
меню, использующий класс MenuItem:
Def appsPopup(self popup)
{ popup := newPopup(MenuItem, "&Applications");
addItem(popup, wpPopup(self));
addItem(popup, ssPopup(self));
addItem(popup, graphPopup(self));
Комбинирование статических и динамических меню
281
addItem(popup, miscPopup(self));
addItem(popup, utilityPopup(self));
addItem(popup, develPopup(self));
Арорир
}
Def wp(Popup(self popup)
{ popup := newPopup(MenuItem, "&Word Processors11);
addItem(popup, new(MenuItem, "Word for Windows", 228,
#word));
addItem(popup, new(MenuItem, "Ami”, 229, #ami));
addItem(popup, new(MenuItem, "Word Perfect", 230, #wpf));
Apopup
}
Динамические popup-меню полезны тогда, когда еще нет полной
определенности, какой будет окончательная форма меню. Недо¬
статком этого варианта является то, что для создания меню он
требует специальной процедуры. В случае статического иерархи¬
ческого меню процедура существенно проще. Описание ресурсного
сценария для такого меню — это перечисление вложенных POPUP-
инструкций, определяющих ресурс для меню. Например, иерар¬
хическое меню, определенное выше как динамическое, статически
определялось бы следующим образом:
POPUP "&Applications"
BEGIN
POPUP "&Word Processors"
BEGIN
MENUITEM "Word for Windows", WFW
MENUITEM "Ami", AMI
MENUITEM "Word Perfect", WPF
END
■ КОМБИНИРОВАНИЕ СТАТИЧЕСКИХ
И ДИНАМИЧЕСКИХ МЕНЮ
Пользовательский интерфейс с программой Executive Control по¬
зволяет одновременно использовать статические и динамические
меню в пределах одной полоски меню. Первая моя попытка
добиться этого была безуспешной. Динамические меню захваты¬
вали полоску меню целиком, этот вариант функционировал, но
только с динамическими меню. Первый вариант использовал
классы Menu и MenuInem. Для создания меню Windows, которое
является лишь системой меню на полоске меню, т.е. динамиче¬
ским меню, использовались средства MDIFrameWindow. Другой
вариант описывается в ресурсном сценарии в листинге. Почти
такое же действие имеет фрагмент из метода init
MDIFrameW indow:
windowMenu := Call CreatePopupMenu();
Call AppendMenu(window, MF_STRING, 9997,
asciiz("&Cascade"));
282
Пример приложения в Windows 3.0
Call AppendMenu(window, MF_STRING, 9998,
asciiz("&Tile"));
Call AppendMenu(window, MF_STRING, 9999,
asciiz("&Arrange Icons"));
Call AppendMenu(hMenu, MF_POPUP, windowMenu,
ascii z("&Windows"));
■ ДИНАМИЧЕСКИЕ ФАЙЛОВЫЕ ДИАЛОГИ
Один из недостатков статических ресурсов состоит в том, что все
составляющие ресурса должны быть предопределены. В случае,
когда вам необходимо большое число элементов управления, от¬
личающихся друг от друга лишь незначительными деталями,
вместо определения отдельного ресурсного элемента для каждого
из них предпочительнее использовать динамические ресурсы. Это
решение подходит для использования с различными типами фай¬
лового диалога. Часто для различных операций по работе с фай¬
лами можно использовать идентичные файловые диалоги. Для
обеспечения этой потребности создан класс SysDialog, являющий¬
ся непосредственным подклассом класса FileDialog; его методы
определены так, что диалоги создаются динамически. Например,
метод copyDlg определяется следующим образом:
Def copyDlg(self dlg)
{
dlg := new(DialogDesign);
setText(dlg, "Copy File");
setSize(dlg, 000, 200070);
addItem(dlg, newStatic(DlgItem, "From:", 925, 10010,
60016, 0));
addItem(dlg, newStatic(DlgItem, "To:", 925, 15040,
60016, 0));
addItem(dlg, newStatic(DlgItem, "", 101, 40010,
1 50010, 0)) ;
addItem(dlg, newStatic(DlgItem, "", 101, 40040,
150010, 0));
runModa(dlg, nil, ThePort);
}
■ ФАКТОРИЗАЦИЯ КОМАНДНЫХ МЕТОДОВ
Одним из главных негативных факторов, с которыми встречаешь¬
ся при разработке сложных систем меню GUI, является перспек¬
тива кажущейся бесконечной цепочки саэе-операторов, которая
чрезвычайно затрудняет отладку. Общий дух объектно-ориентиро¬
ванного программирования нацеливает на конструирование фун¬
кциональных пакетов, которые будут работать всегда, даже тогда,
когда будут разработаны и добавлены в систему многие новые
пакеты. По этой причине хороший объектно-ориентированный
дизайн требует избегать создания монолитных громоздких коман¬
дных методов для обработки всех имеющихся меню. За счет
Факторизация командных методов
283
факторизации, обработки командных сообщений Windows в не¬
скольких отдельных командных методах, каждый из которых
может работать как автономно, так и во взаимодействии друг с
другом, программы компилируются быстрее, их легче отлаживать,
они требуют меньше ресурсов памяти и их проще модифицировать
(см. листинги 1-3).
Основной принцип поистине прост. Когда для класса Актора
определяется командный метод, обеспечивается возможность при¬
ема им WM_COMMAND-coo6njeHHft от Windows и прерывания его
выполнения. Программирование командного метода обычно при¬
нимает форму последовательности саэе-операторов, которые про¬
веряют элемент управления IDs различных позиций меню и затем
посылают соответствующие сообщения. Командный метод должен
быть либо в классе, определяющем окно, в котором расположено
данное меню, либо соответствующие сообщения-извещения
(notification) должны передаваться к другим объектам. Однако в
случае, когда число позиций меню слишком велико, этот подход
неприменим. Вместе с тем главный командный метод можно
написать так, что будет вызываться подкомандный метод, который
предназначается для работы с несколькими различными функци¬
ональными пакетами. Таким образом, подкомандный метод можно
написать так, что он всегда будет работать с каждым меню или с
другим элементом управления, не обращая внимания на контекст,
в котором используется. За вызов подчиненных командных мето¬
дов, которые требуются, отвечает командный метод-хозяин.
Листинг 1. Листинг класса ExecWin
/* Класс фреймового окна ExtcWin для программы
Executive Control, комбинированное управляющее
окно-хозяин Windows 3.0 и мультидокументный редактор */\\
inhent(MDIFrameWindow, #ExecWin, nil, 2, nil)!!
now(class(ExecWin))!!
now(ExecWin)!!
/* запускает файловый диалог для уничтожения файлов */
Def deleteFile(self aFile)
{
FD := new(FileDialog, "*.*");
runModal(FD, DF_BOX, ThePort);
copyAll(existsFile, aFile, 0));
}
j *
/* запускает файловый диалог для переименования файлов */
Def renameFile(self aFile)
{
FD := new(FileDialog, "*.*");
runModal(FD, RF_BOX, ThePort);
284
Пример приложения в Windows 3. 0
copyAll(existsFile, aFile, 0));
}
» !
/* создает динамический файловый диалог */
Def fileDlg(self fDlg, cBox, lBox)
{
fDlg := new(DialogDesign);
}
i i
/* запускает файловый диалог для копирования файлов */
Def copyFile(self aFile)
{
FD := new(FileDialog, "*.*");
runModal(FD, CF_BOX, ThePort);
copyAll(existsFile, aFile, 0));
}
I i
/* подкомандный метод для работы с системными командами */
Def systemCommand(self, wP)
{
select
case wP == CUD
is currentDir(self);
endCase;
case wP == CHD
is changeDir(self);
endCase;
case wP == CRD
is createDir(self);
endCase;
case wP == CF
is copyFile(self);
endCase;
case wP == RF
is renameFile(self);
endCase;
case wP == DF
is deleteFile(self);
endCase;
endSelect;
}
1 i
/* создать директорий */
Def createDir(self path dlg)
{
dlg := new(InputDialog, "Create Directory",
"Enter path to create", "");
runModal(dlg, INPUT_BOX, ThePort);
path := getText(dlg);
create(Directory, path);
}
1 i
/* уничтожить директорий */
Def removeDir(self path dlg)
Факторизация командных методов
285
{
dlg := new(InputDialog, "Remove Directory”,
"Enter path to remove", "") ;
runModal(dlg, INPUT_BOX, ThePort);
path := getText(dlg);
remove(Directory, path);
}
! »
/* сменить директорий */
Def changeDir(self newPath dlg)
{
dlg := new(InputDialog, "Change Directory",
"Enter new path", "");
runModal(dlg, INPUT_BOX, ThePort);
path := getText(dlg);
makeCurrent(Directory, newPath);
}
1 !
/* возвращает текущий директорий */
Def currentDir(self aString)
{
dlg := fullName(current(Directory));
new(ErrorBox, ThePort, "The current directory is"
+ aString, "Current Directory", 0);
}!!
/* подкомандный метод для работы с опциями приложений */
Def optionCommand(self, wP)
{
sdkCommand(self, wP);
if wP == GLS
glossWin(self, wP);
endif;
}
! !
/* подкомандный метод для выполнения приложений */
Def appCommand(self, wP)
{
select
case wP == WFW
is word(self, wP);
endCase;
case wP == AMI
is ami(self, wP);
endCase;
case wP == EXL
is excel(self, wP);
endCase;
case wP == ZNG
is zing(self, wP);
endCase;
case wP == OBD
is obdraw(self, wP);
endCase;
case wP == TBK
is tbook(self, wP);
286
Пример приложения в Windows 3.0
endCase;
case wP == NRT
is norton(self, wP);
endCase;
case wP == XTG
is xtree(self, wP);
endCase;
case wP == CPV
is cppv(self, wP)*
endCase;
endSelect;
}
t j
/*********************************************************
Командный модуль, который организует создание дочерних
окон для ведения документов и управление этими окнами
*********************************************************/
Def childCommand(self, wP, lP)
{
if (wP == FILE_OPEN) or (wP == FILE_NEW)
thencreateMDIChild(self, childClass, "(Untitled)", nil);
command(childList[activeHWnd], wP, lP);
processMenu(self, getText(childList[activeHWnd]));
else
if childList[activeHWnd]
command(childList[activeHWnd], wP, lP);
endif;
endif;
execWindowProc(self:ancestor, WM_COMMAND, wP, lP);
}
» t
/***************************************************
Модуль командных методов для пакета SDK
***************************************************j
Def sdkCommand(self, wP)
select
case wP == SPY
is spy(self, wP);
endCase;
case wP == HPW
is heapwalk(self, wP);
endCase;
case wP == ZMN
is zoomin(self, wP);
endCase?
case wP == SDP
is sdkpaint(self, wP);
endCase;
case wP == FED
is fontedit(self, wP);
endCase;
case wP == SHK
is shaker(self, wP);
endCase;
case wP == DED
Факторизация командных методов
287
is dlgedit(self, wP);
endCase;
endSelect;
}
I i
/**************************************************
Модуль командных методов Windows Menu, который
создает динамическую систему меню Windows
**************************************************/
Def windowCommand(self, wP)
{
select
case wP == IDM_CASCADE
is Call SendMessage(getHWnd(clientWindow),
messageID(#WM_MDICASCADE), 0, 0L);
endCase
case wP == IDM_TILE
is Call SendMessage(getHWnd(clientWindow),
messageID(#WM_MDITILE), 0, 0L);
endCase
case wP == IDM_ARRANGEICONS
is Call SendMessage(getHWnd(clientWindow),
messageID(#WM_MDIICONSARRANGE), 0, 0L);
endCase
endSelect;
}
* j
/* открывает один из вариантов окна-глоссария */
Def glossWin(self, wP WW)
{
WW := new(Window, ThePort, nil, "Glossary", &(375, 63,
535, 300));
show(WW, 1);
L := new(ListBox, 375, WW);
setCRect(L, &(0, 0, 153, 223));
moveWindow(L);
addString(L, "Alpha");
show(L, 1);
addString(L, "Beta");
addString(L, "Gamma");
addString(L, "Delta");
addString(L, "Epsilon");
addString(L, "Zeta");
addString(L, "Eta");
addString(L, "Theta");
addString(L, "Iota");
addString(L, "Kappa");
addString(L, "Lambda");
addString(L, "Mu");
addString(L, "Nu");
addString(L, "Xi");
addString(L, "Omicron");
addString(L, "Pi");
addString(L, "Rho");
addString(L, "Sigma");
addString(L, "Tau");
288
Пример приложения в Windows 3.0
addString(L, "Upsilon")
addString(L, "Phi1');
addString(L, "Chi")
addString(L, "Psi");
addString(L, "Omega");
}
1 i
^***************************************************
Модуль командных методов меню Desk Accessory —
настольные принадлежности
*************************************************** j
Def deskCommand(self, wP)
{
select
case wP == CLC
is calc(self, wP);
endCase;
case wP == CLK
is clock(self, wP);
endCase;
case wP == NPD
is note(self, wP);
endCase;
case wP == CNP
is cpanel(self, wP);
endCase;
case wP == CDF
is card(self, wP);
endCase;
case wP == CLD
is cal(self, wP);
endCase;
case wP == WRT
is writer(self, wP);
endCase;
case wP == PTB
is pbrush(self, wP);
endCase;
case wP == PRM
is pnntman(self, wP);
endCase;
case wP == RCR
is record(self, wP);
endCase;
case wP == STP
is setup(self, wP);
endCase;
case wP == CLB
is clipbrd(self, wP) ;
endCase;
case wP == TRM
is terminal(self, wP);
endCase;
endSelect;
}
Факторизация командных методов
289
/* выполняет утилиту SDK Zooming */
Def zooming(self, wp)
{
exec ("D: \WINDEV\ZOOMIN.EXE11) ;
}
* j
/* выполняет Zing */
Def zing(self, wp)
{
exec("E:\ZING\ZING.EXE");
}
* j
/* выполняет Xtree */
Def xtree(self, wp)
{
exec(”С:\XTREE\XTG.EXE");
}
» j
/* выполняет MS write */
Def writer(self, wp)
{
exec("E:\WINDOWS\WRITE.EXE");
}
i j
/* выполняет MS Word */
Def word(self, wp)
{
exec(МС:\WINWORD\WINWORD.EXE");
}
I j
/* запускает терминальную программу */
Def terminal(self, wp)
{
exec("E:\WINDOWS\TERMINAL.EXE");
}
i i
/* выполняет утилиту SDK spy */
Def spy(self, wp)
{ '
exec("D:\WINDEV\SPY.EXE");
}
! !
/* запускает утилиту Shaker */
Def shaker(self, wp)
{
exec("D:\WINDEV\SHAKER.EXE");
}
1 j
/* вызывает setup Windows */
Def setup(self, wp)
{
exec("E:\WINDOWS\SETUP.EXE");
}
J0~857
290
Пример приложения в Windows 3.0
/* выполняет утилиту SDK sdkpaint */
Def sdkpaint(self, wp)
{
exec("D:\WINDEV\SDKPAINT.EXE");
}
* j
/* выполняет программу записи */
Def record(self, wp)
{
exec("E:\WINDOWS\RECORDER.EXE");
}
I j
/* выполняет программу-менеджер печати */
Def pnntman(self, wp)
{
exec("E:\WINDOWS\PRINTMAN.EXE" ;
}
J !
/* выполняет программу Windows рисования кистью */
Def pbrush(seIf, wo)
{
exec("E:\WINDOWS\PBRUSH\ZXE" ) ;
}
» j
/* выполняет объектное рисование */
Def objdraw(self, wp)
{
exec("D:\DRAW\OBJDRAW.EXE");
}
1 j
/* выполняет программу Windows notepad accessory */
Def note(self, wp)
{
exec("E:\WINDOWS\NOTEPAD.EXE”);
}
i j
/* выполняет Norton Integrator */
Def norton(self, wp)
{
exec("E:\NORTON\NI.EXE”);
}
t j
/* метод, который выполняет программу Heapwalker из
MS Windows SDK */
Def heapwalk(self, wp)
{
exec("D:\WINDEV\HEAPWALK.EXE");
}
i *
/* выполняет редактор шрифтов SDK */
Def fontedit(self, wp)
{
exec("D:\WINDEV\FONTEDIT.EXE”);
}
Факторизация командных методов
291
i ]
/* выполняет электронную таблицу excel */
Def excel(self, wp)
{
exec("D:\EXCEL\EXCEL.EXE");
}
i i
/* выполняет редактор диалогов SDK */
Def dlgedit(self, wp)
{
exec("D:\WINDEV\DIALOG.EXE");
}
I !
/* запускает броузер С++ Views */
Def cppv(self, wp)
{
exec("D:\CTV\CB.EXE");
}
! !
/* выполняет утилиту Windows для работы с управляющими
панелями */
Def cpanel(self, wp)
{
exec("E:\WINDOWS\CONTROL.EXE");
}
» i
/* comment */
Def clock(self, wp)
{
exec ('*E: \WINDOWS\CLOCK.EXE1') ;
}
j I
/* открывает clipboard-буфер */
Def clipbrd(self, wp)
{
exec("E:\WINDOWS\CLIPBRD.EXE”);
}
I f
/* выполняет утилиту Windows cardfile */
Def card(self, wp)
{
exec(”Е:\WINDOWS\CARDFILE.EXE");
}
I »
/* выполняет утилиту Windows calculator */
Def calc(self, wp)
{
exec ("E: \WINDOWS\CALC.EXE11) ;
}
i j
/* выполняет утилиту Windows calendar */
Def cal(self, wp;
{
292
Пример приложения в Windows 3.0
exec("E:\WINDOWS\CALENDAR.EXE");
}
i f
/* выполняет текстовый процессор ami */
Def ami(self, wp)
{
exec(MD:\AMI\AMI.EXE");
}
i j
^***********************************************************
Главный модуль командных методов, вызывающий все другие
командные методы, такие как childCommand, знающий,
как организовать создание нового дочернего MDI-объекта
***********************************************************/
Def command(self, wP, lP)
{
childCommand(self, wP, lP);
windowsCommand(self, wP);
systemCommand(self, wP);
deskCommand(self, wP);
appCommand(self, wP);
optionCommand(self, wP);
}!!
/* Класс ExecApp */
inherit(Application, #ExecApp, nil, 2, nil)!!
now(class(ExecApp))!!
now(ExecApp)!!
/* Создать и показать приложение mainWindow.
Загрузить файл данных, если необходимо */
Def init(self, cmdLine dlg fName)
{
init(self:ancestor, cmdLine);
mainWindow := newMainExecWin, "ExecMenu", "Executive
Control", nil);
setClassType(mainWindow, #FileChildMDI);
show(mainWindow, CmdShow);
}!!
/* Класс SysDialog создает различные типы файловых диалогов */
inherit(FileDialog, #SysDialog, nil, 2, nil)!!
now(class(SysDialog))!!
now(SysDialog)!!
/* Метод, который создает традиционный диалог
для указания имен файлов перед их уничтожением */
Def deleteDlg(self dlg)
{
dlg := new(DialogDesign);
setText(dlg, "Delete File");
addItem(dlg, newEdit(DlgItem, "", 101, 30020, 60015, 0));
runModal(dlg, nil, ThePort); -
}!!
Факторизация командных методов
293
/* Метод, который создает традиционный диалог
для указания имен файлов перед их переименованием */
Def renameDlg(self dlg)
{
dlg := new(DialogDesign);
setText(dlg, "Rename File");
setSize(dlg, 000, 200070);
addItem(dlg, newStatic(DlgItem, "From:", 925, 10@10,
60016, 0));
addItem(dlg, newStatic(DlgItem, "To:", 925, 15@40,
60016, 0));
addItem(dlg, newEdit(DlgItem, м", 101, 40010,
150010, 0));
runModal(dlg, nil, ThePort);
}!!
/* Метод, который создает традиционный диалог
для указания имен файлов перед их копированием */
Def copy(self dlg)
{
dlg := new(DialogDesign);
setText(dlg, "Copy File");
setSize(dlg, 000, 200070);
addItem(dlg, newStatic(DlgItem, "From:", 925, 10010,
60016, 0));
addItem(dlg, newStatic(DlgItem, "То:”, 925, 15040,
60016, 0));
addItem(dlg, newEdit(DlgItem, им, 101, 40010,
150010, 0));
runModal(dlg, nil, ThePort);
}!!
/* Управляет событиями файлового диалога (OK, Cansel и т.д.).
При выборе Open всегда что-то выполняется, если
перезагрузить список, соответствующий текущему фильтру */
Def ,ommand(self, wP, lP action)
{ action := high(lP);
select
case wP == IDCOPY
is end(self);
copyDlg(self) ;
endCase
case wP == IDRN
is renameDlg(self);
endCase
case wP == IDDL
is deleteDlg(self);
endCase
case wP == IDCANSEL
is resetDir(self);
end(self, 0);
endCase
case wP == FILE_LB and action = LBN_SELCHANGE
is sendDlgItemMessage(self, FILE_DIRLB,
LB_SETCURCEL, -1 , 0) ;);
endCase
case wP == FILE DIRLB and action = LBN SELCHANGE
294
Пример приложения в Windows 3.0
is sendDlgItemMessage(self, FILE_LB,
CB_SETCURCEL, -1, 0);
setItemText(self, FILE_LB, getLoadDir(self)
+ fileSpec);
endCase
case (wP == FILE_DIRLB and action = LBN_DBLCLK)
car(wP == IDOK and getLBSel(sel, FILE_DIRLB))
i.s newDir(self);
endCase
case wP == FILE_LB and action = LBN_DBLCLK
is
if getLoadFile(self)
then resetDir(self);
end(self, IDOK);
endif;
endCase
case wP == IDOK
is open(self)
endCase
endSelect;
A1;
}!!
Листинг 2. Ресурсный файл сценариев для Executive Control
#include "exec.h"
#include "actor.h"
ExecMenu MENU
BEGIN
POPUP "&File"
BEGIN
MENUITEM "&New", FILE_NEW
MENUITEM "&Open...", FILE_OPEN
MENUITEM "&Insert File...", FILE_READ
MENUITEM "&Save", FILE_SAVE
MENUITEM "Save &As...'\ FILE_SAVEAS
END
POPUP "&System"
BEGIN
MENUITEM ’’C&urrent Directory", CUD
MENUITEM "C&hange Directory", CHD
MENUITEM "C&reate Directory", CRD
MENUITEM "Co&py File", CF
MENUITEM "Re&name File", RF
MENUITEM "Delete File", DF
END
POPUP "&Edit"
BEGIN
MENUITEM "Cu&t\tShift+Del", EDIT_CUT
MENUITEM "&Copy\tCtrl+Ins", EDIT_COPY
MENUITEM "Paste\tShift+Ins", EDIT_PASTE
MENUITEM "C&lear", EDIT_CLEAR
MENUITEM SEPARATOR
MENUITEM "Select &All\tCtrl+A", EDIT_SELALL
END
POPUP "&Applications"
BEGIN
Факторизация командных методов
295
POPUP "&Word Processors"
BEGIN
MENUITEM "Word for Windows”, WFW
MENUITEM "Ami", AMI
MENUITEM ”Word Perfect", WPF
END
POPUP "&Spreadsheets"
BEGIN
MENUITEM "Excel", EXL
MENUITEM "WingZ", WNZ
MENUITEM "Quattro Pro", QTP
END
POPUP "&Graphics"
BEGIN
MENUITEM "Zing", ZNG
MENUITEM "Designer", DSR
MENUITEM "Object Draw", OBD
END
POPUP "Miscellaneous"
BEGIN
MENUITEM "Toolbook", TBK
MENUITEM "Macrocalc", MCLC
MENUITEM "Almanac", ALM
END
POPUP "Utilities"
BEGIN
MENUITEM "Norton", NRT
MENUITEM "Xtree Gold", XTG
MENUITEM "Archive", ARC
MENUITEM "Other", ETC
END
POPUP "Developments Tools"
BEGIN
MENUITEM "Actor", ACT
MENUITEM "C++ Views", CPV
MENUITEM "Spy", SPY
MENUITEM "Heapwalker", HPW
MENUITEM "Zoomin", ZMN
MENUITEM "SDK Paint", SDP
MENUITEM "Font Editor", FED
MENUITEM "Shaker", SHK
MENUITEM "Dialog Editor", DED
END '
END
POPUP "&Desktop"
BEGIN
MENUITEM "&Calculator", CLC
MENUITEM "Cloc&k", CLK
MENUITEM "&Notepad", NPD
MENUITEM "C&ontrol panel", CNP
MENUITEM "C&ardfile", CF
MENUITEM "Ca&lendar", CLD
MENUITEM "&Write", WRT
MENUITEM "Paint&brush", PTB
MENUITEM "&Print Manager", PRM
MENUITEM "&Recorder", RCR
MENUITEM "&Setup", STP
296
Пример приложения в Windows 3.0
MENUITEM "Cl&ipboard", CLB
MENUITEM "&Terminal", TRM
END
POPUP "&Options"
BEGIN
MENUITEM "&Glossary...", GLS
MENUITEM "&Menus", MNS
MENUITEM "&Child Windows", CWS
MENUITEM "Buttons", BTS
MENUITEM "Me&ssages", MSS
END
END
CF_BOX DIALOG DISCARDABLE 27, 23, 170, 116
STYLE WS_DLGFRAME WS_POPUP DS_ABSALIGN
BEGIN
CONTROL "" FILE_LB, "ComboBox", CBS_SIMPLE CBS_SORT
WS_VSCROLL WS_TABSTOP WS_CHILD, 4, 30, 55, 80
CONTROL "Files:" 3, "static", SS_LEFT WS_CHILD, 4,
19, 31, 10
CONTROL "" FILE_DIRLB, "ListBox", LBS_STANDARD
WS_TABSTOP WS_CHILD, 65, 42, 55, 68
CONTROL "Directories:" 3', "static", SS_LEFT WS_CHILD,
65, 31, 38, 10
DEFPUSHBUTTON "&Copy", IDCOPY, 130, 37, 30, 1 5,
WS_CHILD
PUSHBUTTON "&Cansel", IDCANCEL", 130, 63, 30, 15,
WS_CHILD
CONTROL "Directory:" 3, "static", SS_LEFT WS_CHILD,
4, 7, 32, 11
CONTROL "" FILE_DIR, "static", SS_LEFT WS_CHILD, 39,
7, 146, 11
END
RF_BOX DIALOG DISCARDABLE 27, 23, 170, 116
STYLE WS_DLGFRAME WS_POPUP DS_ABSALIGN
BEGIN
CONTROL "" FILE_LB, "ComboBox", CBS_SIMPLE CBS_SORT
WS_VSCROLL WS_TABSTOP WS_CHILD, 4, 30, 55, 80
CONTROL "Files:" 3, "static", SS_LEFT WS_CHILD, 4,
19, 31, 10
CONTROL "" FILE_DIRLB, "ListBox", LBS_STANDARD
WS_TABSTOP WS_CHILD, 65, 42, 55, 68
CONTROL "Directories:" 3, "static", SS_LEFT WS_CHILD,
65, 31, 38, 10
DEFPU SHBUTTON "&Rename", IDRN, 1 30, 37, 34, 1 5,
WS_CHILD
PUSHBUTTON "&Cansel", IDCANCEL", 130, 63, 30, 15,
WS_CHILD
CONTROL "Directory:" 3, "static", SS_LEFT WS_CHILD,
4, 7, 32, 11
CONTROL "" FILE_DIR, "static", SS_LEFT WS_CHILD,
39, 7, 146, 11
END
DF_BOX DIALOG DISCARDABLE 27, 23, 170, 116
STYLE WS_DLGFRAME WS_POPUP DS_ABSALIGN
BEGIN
CONTROL "" FILE_LB, "Combo Box", CBS_SIMPLE CBS_SORT
WS VSCROLL WS TABSTOP WS CHILD, 4, 30, 55, 80
Факторизация командных методов
297
CONTROL "Files:" 3, "static”, SS_LEFT WS_CHILD, 4,
19, 31, 10
CONTROL "" FILE_DIRLB, "ListBox", LBS_STANDARD
WS_TABSTOP WS CHILD, 65, 42, 55, 68
CONTROL "Directories:"- 3, "static", SS_LEFT WS CHILD,
65, 31, 38, 10
DEFPU SHBUTTON "&Delete", IDDL 130, 37, 34, 1 5,
WS CHILD
PUSHBUTTON~*'&Cansel", IDCANCEL", 130, 63, 30, 15,
WS_CHILD
CONTROL "Directory:" 3, "static", SS_LEFT WS_CHILD,
4, 7, 32, 11
CONTROL "" FILE_DIR, "static", SS_LEFT WS_CHILD, 39,
7, 146, 11
END
Листинг 3. Файл-заголовок (header file) для Executive Control
#define
CHD
800
#define
CRD
801
#define
CUD
802
#define
CF 803
#define
RF 804
#define
DF 805
#define
WFW
810
#define
AMI
811
#define
WPF
812
#define
EXL
820
#define
WNZ
821
#define
QTP
822
#define
ZNG
830
#define
DSR
831
#define
OBD
832
#define
TBK
840
#define
MCLC 841
#define
ALM
842
#define
NRT
850
#define
XTG
851
#define
ARC
852
#define
ETC
853
#define
ACT
860
#define
CPV
861
#define
SPY
862
#define
HPW
863
#define
ZMN
864
#define
SDP
865
#define
FED
866
#define
SHK
867
#define
DED
868
#define
CLC
870
#define
CLK
871
#define
NPD
872
#define
CNP
873
#define
CDF
874
#define
CLD
875
#define
WRT
876
#define
PTB
877
#define
PRM
878
298
Пример приложения в Windows 3.0
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
RCR 879
STP 880
CLB 881
TRM 882
GLS 890
MNS 891
CWS 892
BTS 893
MSS 894
HLP 895
CF_BOX 896
RF_BOX 397
DF_BOX 898
IDCOPY 899
IDRN 900
IDDL 901
MDIFrame Window
Исходный файл: MDIFRAME.CLS
Происходит из: Window
Переменные экземпляров:
clientWindow
childList
activeHWnd
childClass
windowMenu
Методы объектов:
add
close
command
createMDIChild
defWndProc
Указывает на оконное пространство клиента
Словарь дочернего объекта
Хранит цепочку активных дочерних объектов
Хранит цепочку типов дочерних классов
Хранит меню ”Window”
Добавляет новое дочернее окно к
экземплярной переменной childList
Перед закрытием MDIFrameWindow пробегает
по всем дочерним окнам приложения и
удостоверяется, что они готовы к закрытию
Обрабатывает все особые командные методы
MDIFrameWindow
При вызове этого метода с указанием имени
класса, текста заголовка и спецификацией
прямоугольника для окна будет создано
новое дочернее MDI-окно. Затем метод
добавит указатель на это дочернее окно в
словарь childList, в качестве ключа взяв
значение hWnd данного дочернего объекта
По умолчанию обеспечивается для диалогов,
которые ничего не делают. Возвращает 0 для
нормального функционирования Windows.
ПРЕДУПРЕЖДЕНИЕ: Не уничтожайте этот
Факторизация командных методов
299
execWindowProc
getActiveHW nd
getClient
init
processMenu
remove
setActiveHWnd
setClassType
FileChildMDI
Исходный файл:
Происходит из:
Методы объектов:
close
command
метод! Он может быть переопределен у
потомков, если они при помощи Windows
будут регистрировать собственные классы
для их диалогов
Удостоверяет, что все сообщения, которые
не были обработаны методом Актора,
переданы в DefFrameProc в Windows, тем
самым фреймовое окно находится под
надлежащим управлением
Возвращает дескриптор окна активному
MDIChild
Берет указатель на пространство
Frame-окна клиента
Определяет фреймовое окно таким образом,
что создает MDIClient для работы в нем.
Кроме того, динамически создает меню
"Windows”, которое инициализируется
опциями ’’Cascade”, ”Tile” и ’’Arrange
Icons” для управления MDIChild
Поскольку текст заголовка в созданном окне
многократно меняется, этот метод берет на
себя заботу о корректировке
соответствующего входа дочернего MDI
в меню ’’Windows”
Исключает дочерний MDI-объект из
словаря childList
Присваивает дескриптору активное
дочернее окно MDI-приложения
Устанавливает тип класса, который будет
использоваться данным MDI-приложением
при создании нового дочернего объекта
в его пространстве клиента
FILECHIL.CLS
FileWindow
Удостоверяет, окно готово к закрытию
Управляет событиями меню. Он отличается
от одноименного метода в классе FileWindow.
Вместо использования командного метода
300
Пример приложения в Windows 3.0
create
defWndProc
destroy
execWindowProc
setText
своего предка (WorkEdit) он будет
использовать командный метод из EditWindow.
Это по-существу устраняет возможность
выполнения программной строки Актора или
инспектирования среды (ни то ни другое
невозможно в распечатанных (sealed off)
приложениях). Эти опции меню можно
удалить из FileEditMenu в ресурсном файле,
если в stand-alone приложении используется
FileChildMDI. Этот командный метод также
можно удалить, если объект FileChildMDI
должен использоваться только в среде Актора
Создает дочернее MDI-окно в соответствии
с указанными спецификациями. Значение
параметра par — это родитель данного
дочернего объекта. Ему должно быть
присвоено фреймовое окно, которое управляет
данным MDI-приложением. Параметр
wName — это текст, который появится в
полоске для заголовка; rect — прямоугольник,
представляющий область, в которой должно
появиться окно; style определяет, какими
атрибутами должно обладать данное окно.
Следует отметить, что окно будет создано
и показано, как только сообщение
WM_MDICREATE будет послано в MDICleint
По умолчанию обеспечивается для диалогов,
которые ничего не делают. Возвращает 0
для нормального функционирования
Windows.
ПРЕДУПРЕЖДЕНИЕ: Не уничтожайте этот
метод! Он может быть переопределен у
потомков, если они при помощи Windows
будут регистрировать собственные классы
для их диалогов
Уничтожает дочернее MDI-окно, посылая
сообщение WM_MDIDESTROY
MDI Client-окну
Определяет, что по умолчанию данная
процедура окна будет выполнять функцию
DefMDIChildProc, если сообщение не будет
обработано системой Актор
Записывает в заголовок окна данную строку
Факторизация командных методов
301
WM MOVE
WM SETFOCUS
WM SIZE
MDIChild
Исходный файл:
Происходит из:
Методы класса:
new
newStyle
Методы объектов:
close
Управляет перемещением дочернего окна
внутри фреймового окна.
Примечание. Это сообщение должно
посылаться процедуре дочернего окна,
определенной по умолчанию
Вызывается каждый раз, когда фокус
перемещается данный экземпляр этого
дочернего окна. Таким образом, фреймовое
MDI-окно извещает свое дочернее MDI-окно
о том, что оно сейчас становится активным.
Примечание. Это сообщение должно
посылаться процедуре дочернего окна,
определенной по умолчанию
Вызывается всегда, когда у данного окна
изменяется размер.
Примечание. Это сообщение должно
посылаться процедуре дочернего окна,
определенной по умолчанию
MDICHILD. CLS
Window
Создает и возвращает новый оконный объект.
В случае дочернего MDI-окна окно после
создания автоматически будет показано на
экране. При вызове этого метода указывается
родитель данного дочернего окна (он должен
быть фреймовым MDI-окном), имя для
полоски заголовка и прямоугольник, в
котором будет показано данное окно
Создает и возвращает новое окно. Параметр
par — родительское окно. wName — строка,
содержащая заголовок для окна, rect —
определяет, где и какого размера будет окно.
Если rect равно nil, то используется
умолчание, style — численная величина,
которая является комбинацией величин,
определяющих стиль окна. Если style равен
nil, то стиль выбирается по умолчанию
Гарантирует, что окно готово быть закрытым
302
Пример приложения в Windows 3. О
create
defWndProc
destroy
execWindowProc
setText
WM MOVE
WM SETFOCUS
Создает дочернее MDI-окно в соответствии
с указанными спецификациями. Значение
параметра par — это родитель данного
дочернего объекта. Ему должно быть
присвоено фреймовое окно, которое
управляет данным MDI-приложением.
Параметр wName — это текст, который
появится в полоске для заголовка; fect —
прямоугольник, представляющий область, в
которой должно появиться окно; style
определяет, какими атрибутами должно
обладать данное окно. Следует отметить,
что окно будет создано и показано, как
только сообщение WM_MDICREATE будет
послано в MDIClient
По умолчанию обеспечивается для диалогов,
которые ничего не делают. Возвращает 0
для нормального функционирования
Windows. Метод может быть переопределен
у потомков, если они при помощи Windows
будут регистрировать собственные классы
для их диалогов
Уничтожает дочернее MDI-окно, посылая
сообщение WM_MDIDESTROY
MDIClient-окну
Определяет, что по умолчанию данная
процедура окна будет выполнять функцию
DefMDIChildProc, если сообщение не будет
обработано системой Актор
Записывает в заголовок окна данную строку
Управляет перемещением дочернего окна
внутри фреймового окна.
Примечание. Это сообщение должно
посылаться процедуре дочернего окна,
определенной по умолчанию
Вызывается каждый раз, когда фокус
перемещается в данный экземпляр этого
дочернего окна. Таким образом фреймовое
MDI-окно извещает свое дочернее MDI-окно,
о том, что оно сейчас становится активным.
Примечание. Это сообщение должно
Факторизация командных методов
303
посылаться процедуре дочернего окна,
определенной по умолчанию
WM_SIZE Вызывается всегда, когда у данного окна
изменяется размер.
Примечание. Это сообщение должно
посылаться процедуре дочернего окна,
определенной по умолчанию
MDICIient
Исходный файл: MDICLIEN.CLS
Происходит из: Control
Переменные класса:
$NativeLiterals
Переменные экземпляров:
Методы класса:
new Создает новый экземпляр класса MDIClient.
В качестве аргументов получает указатель на
родительское окно (это должно быть
MDIFrame-oKHo) и структуру клиента. Эта
структура должна быть четырехбайтовой
конструкцией, содержащей дескриптор
popup-меню в первом слове и ID для первого
дочернего MDI-окна во втором слове.
ID для дочернего MDI-окна обязательно
должен быть достаточно большим, чтобы не
было конфликтов с числами, которые
передаются в командные методы
MDIFrame-0K0H
newStyle Занимается реальным созданием объекта
MDIClient. Все параметры, за исключением
style, можно посмотреть в методе new.
Если в операторе newStyle никакой стиль не
указан, то он определяется при помощи
метода style MDIClient-объекта. Этот стиль
должен подходить для всех MDI-приложений
style Возвращает стиль окна, определяемый
по умолчанию
Методы объектов:
create Создает новое MDIClient-окно. Определенные
параметры должны быть такими же, как и в
new, окно-клиент уже создано, и эти величины
304
Пример приложения в Windows 3.0
уже ’’запаяны” в систему. Например,
MDIClient не должен иметь меню, оно всегда
будет регистрироваться у ”mdiclient” класса
и иметь свою позицию и размер, которые
определяются при помощи MDIFrame-oKHa
defWndProc По умолчанию обеспечивается рля диалогов,
которые ничего не делают. Возвращает 0
для нормального функционирования Windows.
Метод может быть переопределен у потомков,
если они при помощи Windows будут
регистрировать собственные классы для
их диалогов
destroy MDIClient-окно должно уничтожаться только
через свое фреймовое окно, поэтому
обычный метод уничтожения здесь
неприемлем
WM_DESTROY Окно MDIClient будет уничтожаться через
его фреймовое окно фреймовой процедурой,
определяемой по умолчанию
WM_MDroESTKOY Сообщение, которое вызывается каждый раз,
когда требуется уничтожить MDIChild.
Процедура, определяемая по умолчанию для
MDIClient, организует уничтожение дочернего
окна. Затем указатель на дочернее окно
будет исключен из словаря
дочерних окон во фреймовом окне
WM_NCDESTKOY При уничтожении окна-клиента удаляет
структуры данных, ранее созданные
для этого окна
Глава 10
Я ВОПРОСЫ
ПРОЕКТИРОВАНИЯ В
ОБЪЕКТНО-ОРИЕНТИРОВАННОЙ
СРЕДЕ WINDOWS
Эта глава посвящена нескольким вопросам объектно-ориентиро-
ванного проектирования в среде Windows. Существуют две основ¬
ные точки зрения на проектирование: проектирование, дизайн
интерфейса для пользователя и проектирование схемы использо¬
вания ресурсов компьютера. Соответственно, первая часть посвя¬
щена объектно-ориентированному дизайну вообще и среде
Windows в частности. Вопрос рассматривается с позиций дизайна
приложения, безотносительно к проблемам управления памятью
компьютера. Вторая часть затрагивает другую группу вопросов и,
в частности, такие темы, как: 1) управление памятью в
Windows 3.0; 2) объектно-ориентированное управление памятью;
3) управление памятью в Windows при использовании Актора.
■ ДИЗАЙН ДЛЯ ПОЛЬЗОВАТЕЛЕЙ
Первый принцип проектирования программ состоит в том, что
проектировать надо программу, которая кем-то будет использо¬
ваться, и поэтому нужно иметь четкое представление о том, кто
же будет потенциальным пользователем системы. Хотя каждый
согласится с этим принципом, он по-прежнему нарушается гораздо
чаще, чем следовало бы. Нельзя утверждать, что принцип не
выдерживается из-за полной некомпетентности проектировщиков
программных систем. Дело в том, что при проектировании для
пользователей надо обладать большим объемом знаний и учиты¬
вать огромное число факторов. Как было бы хорошо получить
книгу, которая дает детальные ответы на все вопросы изучаемой
темы, именно такова наша цель — рассказать о нескольких
306
Вопросы проектирования в объектно-ориентированной среде Windows
важных моментах, которые подскажут читателю дальнейшие на¬
правления изучения и развития.
Как известно всем программистам, перед тем как начать выпол¬
нение инструкций, в компьютер надо загрузить данные. Человек
же, приступив к некоторой деятельности, может при необходимо¬
сти переключиться на поиск нужных данных, а затем продолжить
или завершить начатую деятельность. Очевидно, что в определен¬
ной сфере деятельности человека очень важно — иметь возмож¬
ность работать в таком стиле. Программные приложения
приходится проектировать примерно в такой же манере. Хотя это
рассуждение носит весьма общий характер, у меня есть несколько
конкретных примеров.
Большинство читателей со временем познакомится с распростра¬
ненными программами для рисования типа приложения
Paintbrush, которое поступает вместе с системой Windows 3.0 и ее
более поздними версиями. В этих программах полоска toolbox
традиционно составляется из пиктограмм, которые представляют
различные доступные для пользователя операции. Пытливый ком¬
пьютерный художник чувствует себя здесь почти как дома. Только
вместо кисти он, как правило, использует мышь. Графические
инструменты — это, если хотите, ”глаголы”, а графические линии
и формы — ’’существительные” в предложениях этого вида про¬
граммной речи. Иногда сначала перед выбором ’’глагола” вы
должны выбрать ’’существительное”, но типичным вариантом бу¬
дет обратный порядок: вы сначала будете выбирать ’’глагол” и
лишь затем создавать или добавлять ’’существительное”. В целом
такая схема подходит для создания картинок, здесь не имеет
значенця, в каком из мест картинки будет строиться следующая
часть изображения. Вы можете взять один инструмент и перехо¬
дить от одной области к другой до тех пор, пока вам нужен этот
инструмент. В конце концов не имеет никакого значения, какой
последовательностью действий вы дошли до того результата, ко¬
торый вам требовался.
Интересно сравнить эту ситуацию с приложениями, где мы имеем
дело с последовательными, линейными, а не пространственными
композициями, например музыкальными произведениями, дви¬
жением и компьютерными программами. Здесь не имеет смысла
предоставлять полную свободу в просмотре результатов работы.
Она разворачивается в некоторую непрерывную последователь¬
ность, так, что обычно можно указать следующее место, где вы
должны что-то сделать. Этим естественным концептуальным фо¬
кусом (фокальной точкой) может быть следующий кадр, следую¬
щий такт в музыкальном произведении или следующая строка.
Здесь ’’существительное” главенствует над ’’глаголами”. Важно то,
как ведет себя следующее ’’существительное”, а не то, какой
инструмент придется использовать. Фактически, если попытаться
Разделение процедур и протоколов
307
применить некоторые изощренные инструменты в такого рода
приложениях, это может кончиться тем, что будет получен резуль¬
тат, непригодный для большинства. Причиной этого является то,
что вы навязали пользователю чуждый ему стиль работы, и многие
откажутся от использования такой системы.
■ ЧТО ЖЕ РЕАЛЬНО ДЕЛАЕТ КОМПЬЮТЕР?
Второй принцип проектирования программного обеспечения со¬
стоит в том, что программа должна настолько, насколько это
возможно, отвечать главной функции, задаче, ради которой ис¬
пользуется машина. При проектировании любой разработки, ко¬
торая будет использоваться многократно, например при
проектировании компьютерных программ, вам всегданеобходимо
задаваться вопросом о главной функции, которую выполняет
машина для пользователя. Как ни банально это звучит, но об этом
часто забывают. Какова же главная функция персонального ком¬
пьютера в наши дни? По моему мнению, она состоит в следующем:
Одной из наиболее важных функций настольного компьютера
является то, что он позволяет быстро визуализировать чрезвы¬
чайно большие объемы информации и при этом способ визуали¬
зации может быть легко модифицирован в соответствии с
нашими желаниями.
Ключевое слово здесь — визуализация. Компьютеры превосходно
умеют делать рисунки, текстовую и числовую информацию види¬
мыми. Если это так, то, следовательно, наилучшими программны¬
ми приложениями будут те, которые успешнее других
справляются с данной функцией. Конечно, это словесный трюк,
но как решить, какое из приложений является наилучшим?
■ РАЗДЕЛЕНИЕ ПРОЦЕДУР И ПРОТОКОЛОВ
Первый шаг в разработке объектно-ориентированной системы —
составление списка основных элементов данных и процедур, которые
потребуются в данном приложении. Вероятно, список не будет
оставаться в неизменном виде в процессе жизни программы, но это
позволит взять хороший старт посредством разделения на части
основных сущностей, с которыми данная программа будет работать.
Если эти сущности ясны и понятны, то детали потом можно будет
добавить. Были бы кости, а мясо нарастет! Теперь, когда вы имеете
представление о том, из каких основных классов будет состоять ваше
приложение, необходимо переходить к проектированию протоколов
обмена сообщениями. Эти протоколы включают в себя схему того,
кто (какие объекты) и как будет интерпретировать, понимать эти
сообщения, как они будут циркулировать по программе и как их
будут использовать классы-потомки.
308
Вопросы проектирования в объектно-ориентированной среде Windows
В разработке объектно-ориентированных систем важную роль
играет использование формальных классов. Это средство, позволя¬
ющее описать функциональную сторону предмета, т.е. его наибо¬
лее общие свойства без специфических особенностей, которые
будут бесполезны для многих приложений. Когда класс-предок
обладает нежелательными свойствами, их приходится подавлять
в классах-потомках путем переопределения. Это означает, что
появляются дополнительная работа и потенциальные проблемы.
В идеальном случае набор формальных классов должен быть
разработан таким образом, чтобы никакой необходимости подав¬
лять не требовалось. Хотя это не всегда возможно, но является
одной из целей объектно-ориентированного проектирования.
Для объектно-ориентированных систем типично, что для проблем
создания системы объектов, отвечающей требованиям к програм¬
ме, имеется более чем одно возможное решение. Довольно часто
этих решений несколько. На каком из них остановиться, зависит
от главных целей и используемых критериев. Имеются по крайней
мере два следующих друг за другом уровня проектирования:
уровень стратегии и уровень тактики. На тактическом уровне
нельзя уклоняться от проблем собственно программирования. В
первую очередь результат данного уровня зависит от тщательной
проверки исходного проекта, которая может привести к перепро¬
ектированию и перепрограммированию, в результате должен поя¬
виться окончательный, наилучший вариант проекта. На
стратегическом уровне внимание должно быть нацелено на скру¬
пулезный общий дизайн, здесь нет места строкам программы.
Везде, где программа может быть повторно используемой, насле¬
дование предпочтительнее переписывания как внутри одного при¬
ложения, так и для многих подобных программ.
■ ПРОЕКТИРОВАНИЕ КЛАССОВ
Имеются несколько точек зрения, в соответствии с которыми
можно рассматривать процесс проектирования классов. Подходя
с формальных позиций, вы можете рассмотреть все переменные,
которые обычно используются совместно с соответствующими
процедурами*и объединить их в классы. При другом подходе вы
можете рассматривать не только данное единичное приложение,
но и всю сферу подобных приложений и проектировать такой
класс, который будет играть желаемую роль в функциональной
иерархии. Последний из подходов особенно понятен при разработ¬
ке формальных или абстрактных классов. Для специфической
работы часто гораздо лучше создавать новый специализированный
подкласс, поскольку при этом и проектирование, и программиро¬
вание существенно упрощаются.
Объектно-ориентированное проектирование графического интерфейса
309
Квалифицированный проектировщик объектно-ориентированных
систем при разработке системы классов держит в поле зрения
проблемы не только создаваемого приложения, но и многих других
приложений, которые могли бы быть созданы в данной предметной
области. Объектно-ориентированную систему вполне естественно
представлять в виде некоего владельца нескольких прикладных
программ, которые можно собирать из развивающихся наборов
автономно функционирующих классов. Для того чтобы построить
комплекс, обладающий таким качеством, проектировщик должен
иметь в виду и другие приложения, которые могут быть созданы,
пока идет данная разработка. В идеальном случае должно форми¬
роваться ядро классов, образующих базис как для создаваемого,
так и для многих других приложений, которые будут появляться
в будущем.
■ ЭКЗЕМПЛЯРЫ ДУБЛИРУЮЩИХСЯ ПРИЛОЖЕНИЙ
Часто требуется разработать приложение так, чтобы в один момент
времени его можно было иметь в нескольких экземплярах.
Когда приложение состоит из нескольких взаимодействующих
объектов, организация посылки сообщений между ними требует
дополнительного рассмотрения. Причина этого в том, что каждому
объекту приходится знать имя экземпляра объекта в конкретной
копии приложения. Для того чтобы система из нескольких экзем¬
пляров мультиобъектных приложений работала корректно, имена
соответствующих объектов должны различаться и каждый объект
внутри приложения должен знать имена объектов в своей копии
приложения. Отсюда следует, что имена экземпляров не могут
появиться в текстах программ непосредственно. Решение этой
проблемы может быть простым и элегантным. Во-первых, необхо¬
дим некоторый механизм, генерирующий для экземпляров уни¬
кальные имена. Во-вторых, каждый объект должен иметь
экземплярную переменную, хранящую список имен объектов,
которым он должен передавать сообщения. При посылке сообще¬
ния нужно обращаться к этой экземплярной переменной для того,
чтобы определить, какому из объектов оно должно отправляться.
Это еще одна важная функция, которую выполняют экземплярные
переменные.
■ ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ
ПРОЕКТИРОВАНИЕ ГРАФИЧЕСКОГО
ПОЛЬЗОВАТЕЛЬСКОГО ИНТЕРФЕЙСА
Последующие разделы представляют некоторые весьма распрост¬
раненные проблемы, которые возникают практически перед всеми
разработчиками графических пользовательских интерфейсов
(GUI). Мы должны обратиться к этим проблемам и попытаться
310
Вопросы проектирования в объектно-ориентированной среде Windows
показать, как объектно-ориентированная техника может помочь
в их решении. Перед тем как углубиться в детали, ответим на
один важный вопрос: кого касаются различия, связанные с исполь¬
зованием объектно-ориентированного проектирования, — конеч¬
ного пользователя или только разработчиков приложений? На
первый взгляд может показаться, что преимущества объектно-ори-
ентированных систем видны только программистам, которые раз¬
вивают уже имеющиеся приложения. Однако если
последовательно следовать объектно-ориентированной методоло¬
гии, то становится ясно, что конечный пользователь, как и следо¬
вало ожидать, увидит результаты этого в полученном приложении.
В будущем появится ряд программ различного типа, из которых
пользователи извлекут немалые выгоды. Чтобы проиллюстриро¬
вать этот вывод, давайте рассмотрим достаточно известный тип
программного обеспечения: электронные таблицы. Будет ли объ-
ектно-ориентированная электронная таблица отличаться от табли¬
цы, разработанной по традиционной схеме? В следующем разделе
мы попытаемся ответить на этот вопрос.
В коммерческом программировании требует рассмотрения еще
один важный вопрос — переносимость разрабатываемых программ
и пользовательских интерфейсов на различные компьютерные
платформы. Например, если программист хочет перенести про¬
грамму, написанную для Windows, на Macintosh или X Windows,
то для того, чтобы облегчить эту работу, надо решить ряд типич¬
ных проблем. Например, случай управления при помощи мыши.
Macintosh имеет на мыши только одну кнопку. В связи с этим,
разрабатывая класс для управления при помощи мыши, важно
помнить, что должна быть возможность создавать версию програм¬
мы, которая сможет работать и при одной кнопке.
■ ПРИМЕР ПРОЕКТИРОВАНИЯ
ОБЪЕКТНО-ОРИЕНТИРОВАННОЙ
ЭЛЕКТРОННОЙ ТАБЛИЦЫ
Если бы электронная таблица была построена целиком на прин¬
ципах объектно-ориентированного программирования, она была
бы устроена совершенно иначе. Существующие электронные таб¬
лицы являются клеточно-ориентированными системами для редак¬
тирования, запоминания и перевычисления массивов зависимых
друг от друга чисел. Их называют клеточно-ориентированными
потому, что все формулы и операции определяются в терминах
клеточек (или ячеек) прямоугольной сетки. Но что такое клетка?
Она имеет двойственную природу. С одной стороны, это структура
данных, а с другой, — структура визуализации. В клетке вы храните
данные и, вместе с тем, клетка — это нечто, что определяется
ви тально при помощи координат, которые указывают вам местопо¬
Открытые цепочки действий
311
ложение клетки на экране. Преимущества такой схемы подтвер¬
ждаются годами практического использования этих систем, но
объектно-ориентированный подход мог бы революционизировать
разработку электронных таблиц.
Если электронная таблица разрабатывается исключительно на
объектно-ориентированных принципах, нет никакой необходимо¬
сти быть строгим приверженцем клеточной ориентации. Это ут¬
верждение основывается на том, что модульные концепции модели
разработки и визуализации должны быть независимыми, отдель¬
ными составляющими системы. В объектно-ориентированной таб¬
лице модели чисел можно было бы представить как формулы,
которые применяются к числовым объектам, и не останавливать
внимание на том, как визуализируются клеточки массива. В
идеальном случае вы могли бы присвоить число клеточке и редак¬
тировать его так же, как и сейчас. И в результате вы смогли бы
отделить модели чисел от проблем визуализации клеточек, а все
формулы остались бы в нетронутом виде, сохранились бы различ¬
ные возможности для выдачи их на экран, включая и вариант
упорядочения в форме таблицы.
Другое важное следствие использования модульности при объек¬
тно-ориентированной разработке состоит в том, что объект любого
типа можно заслать в клеточку для его визуализации в ней. Кроме
того, туда можно заслать и несколько объектов. Хотя преимуще¬
ства этого шага могут быть видны не сразу, но доступ к значениям
клетки как к собственно данным позволяет включать эти значения
в формулы или исключать их оттуда, когда возникает необходи¬
мость. Это позволяет создавать модели, в которых может присут¬
ствовать или отсутствовать зависимость от количества клеточек.
Модели такого рода полнее, чем современные электронные табли¬
цы, поскольку являются многоцелевыми. Из сказанного должно
быть ясно, что в настоящее время большая часть явных достоинств
объектно-ориентированного GUI обращена на программиста, одна¬
ко вскоре должно появиться новое поколение прикладных про¬
грамм, которые передадут эти преимущества пользователям, так
как принципы объектно-ориентированного проектирования одно¬
временно просты в понимании и подталкивают к эффективной
работе.
■ ОТКРЫТЫЕ ЦЕПОЧКИ ДЕЙСТВИЙ
Понимание разработчиком того, что же ожидает пользователь от
его программы, в огромной степени определяет успех разработки
объектно-ориентированного программного обеспечения. Эта
мысль всегда находит горячий отклик, и я хотел бы предложить
некоторые принципы, которые смогут помочь вам. Полезным
312
Вопросы проектирования в объектно-ориентированной среде Windows
принципом является принцип открытой цепочки действий. Наи¬
лучшим вариантом его объяснения будет ряд примеров.
В одной из предыдущих глав описывалась архитектура HP New
Wave. Есливы помните, проект этой системы базировался на идее
объектов-приложений. Они могут состоять из нескольких различ¬
ных файлов, специфических для различных прикладных про¬
грамм и интегрированных в один документ-хозяин в
пользовательской среде таким образом, что пользователю не при¬
ходится иметь дело ни с какими рутинными объяснениями того,
как все это строится. В этом подходе имеется ряд явных досто¬
инств, но есть и несколько потенциальных проблем. Результаты
работы пользователя собираются из многих файлов при помощи
программного обеспечения высокого уровня и пользователю не
важно, как это происходит, однако возможна ситуация, когда
приложение будет себя вести как черный ящик, который выходит
из-под его контроля. Это представляет здесь наибольшую опас¬
ность. Если ключевым моментом организации приложения явля¬
ется способ, которым множество файлов интегрируются в единое
целое и процесс связывания для пользователя имеет неизвестную
природу, это может привести к различным трудностям. Во-пер-
вых, весьма вероятно, что приложение окажется непереносимым
куда-либо. Если пользователь чувствует преимущества в возмож¬
ности переноса приложения в различные среды или на различные
платформы, то он столкнется с неудобствами, пока для этого не
будет сделано соответствующее обеспечение. Во-вторых, когда
что-то испортится и приложение по той или иной причине пере¬
станет работать, хотя в распоряжении пользователя будут все
файлы, но и тогда, вероятно, у него не будет никакого представ¬
ления, как разрешить возникшие трудности.
Помимо этих общих опасностей, подстерегающих пользователя,не
исключены и некоторые мелкие неприятности. Они связаны с тем,
что часто прикладные программы, использующие один и тот же
формат файла, могут вносить свой специфический для каждого
инструмента вклад в один и тот же файл-накопитель результатов.
Например, пользователь может использовать более чем один
текстовый редактор, электронную таблицу или программу для
рисования изображений для создания некоторого результирующе¬
го продукта, поскольку каждый из инструментов имеет некоторое
средство, которого нет в других. Рабочий процесс напоминает здесь
конвейерную линию. Прежде чем приобрести завершающую фор¬
му, один и тот же файл проходит через ряд подобных программ.
В ходе этого процесса формат файла может измениться. Тогда
может возникнуть необходимость в нескольких подобных резуль¬
тирующих файлах, которые интегрируются в объект того же типа,
что имеется в системе HP New Wave.
Объектно-ориентированное проектирование для MS-Windows
313
Перечисленные трудности не фатальны для архитектуры систем
типа New Wave. Однако этот вопрос приходится рассматривать,
когда программная среда проектируется в целом. Возможно самая
интересная проблема разработки современных сред GUI состоит в
том, как облегчить использование программного обеспечения но¬
вичками, не превращая систему в абсолютно ”черный ящик”,
который невозможно вскрыть, когда он неисправен. Одним из
возможных решений здесь является идея нескольких уровней
интеграции и доступа (не следует путать с несколькими уровнями
объектно-ориентированного проектирования как такового). То,
что здесь имеется в виду, сводится к пожеланию открывать
”черный ящик” или держать его открытым для тех, кто хочет
получить к нему доступ. В идеале пользователь должен иметь
возможность игнорировать все преграды, скрывающие от него что
бы то ни было, если на то нет особых причин. Однако, если такая
причина имеется, должен быть способ перехода на более низкий
уровень интерфейсов, который разрешает данные проблемы и позво¬
ляет переносить приложение достаточно корректным способом.
И ФАКТОРИЗАЦИЯ КОМАНДНЫХ МЕТОДОВ
Как отмечалось в предыдущей главе, одной из неприятностей в
сложных системах меню в GUI является появление очень длинных
последовательностей сазе-операторов, которые крайне трудно от¬
лаживать. Стратегия объектно-ориентированного программирова¬
ния (ООП) базируется на построении функциональных пакетов,
которые будут работать всегда, даже при появлении в системе
любого количества новых пакетов. Из этого следует, что в хорошем
объектно-ориентированном проекте надо уклоняться от больших
монолитных командных методов для обработки всех меню, какие
могут использоваться в системе. Как было показано ранее, при
использовании техники факторизации, разделении обработки ко¬
мандных сообщений Windows между отдельными командными
методами, каждый из которых может работать независимо друг от
друга, а также в кооперации с другими меню, мы получаем
следующие преимущества: программы компилируются быстрее,
их проще отлаживать, они требуют меньший объем памяти и их
гораздо легче модифицировать.
■ ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ
ПРОЕКТИРОВАНИЕ ДЛЯ MS-WINDOWS
Одной из оригинальных моделей объектно-ориентированного про¬
ектирования является архитектура Model-View-Control (Модель-
Изображение-Управление), или коротко — MVC. Ключевым
моментом в этой архитектуре служит идея о том, что очень важно
разделить описание способов организации информации и ее xpa-
314
Вопросы проектирования в объектно-ориентированной среде Windows
нения и описание способов организации доступа к информации и
ее визуализации. Другими словами, данные приложений хранятся
в подходящем наборе объектов, и модель данных не зависит от
того, в какой форме их в конечном счете увидит пользователь. Для
визуализации одних и тех же данных можно разработать целый
ряд различных способов. Для показа определенного набора дан¬
ных, как правило, разрабатывается некоторый стандартный про¬
токол. Хотя эта идея может показаться очевидной или
элементарной, но она отнюдь не лежит в основном русле современ¬
ного программирования. Более того, правилом, а не исключением
является скорее противоположный подход.
Представляется полезным рассмотреть некоторый конкретный
пример. В областях систем обработки документов и настольных
издательских систем стало нормой использовать специальные фай¬
лы, известные как style sheets (полосы описания стиля). Хотя не
обязательно разрабатывать приложения, которые их используют,
с применением объектно-ориентированного программирования,
имеется аналогия между использованием style sheets и обсуждае¬
мой нами MVC-архитектурой. Аналогия базируется на идее хра¬
нения текстовых данных в одном файле и формата-стиля, при
посредстве которого эти данные будут визуализироваться или
печататься, в другом. Таким образом различные стили визуализа¬
ции можно легко и быстро связать с одними и теми же текстовыми
данными. Преимущества этого подхода уже оценили производи¬
тели программных систем, и многие приложения для обработки
документов уже используют эту технику.
Модульность архитектуры MVC, конечно, имеет гораздо более
фундаментальный характер и далеко идущие последствия, чем
собственно техника style sheets. В сферу проектирования здесь
включаются не только файлы на дисках, но и использование
оперативной памяти приложений. Сюда относятся различные
уровни модуляризации, которые могут весьма заметно сказаться
на использовании приложений.
Остановимся на трех основных объектно-ориентированных систе¬
мах программирования в Windows: Актор, Views и Common View.
В их подходах наблюдается разительная разница, особенно замет¬
ная в отношении к иерархии классов. В подходе С++ Views явно
реализуется архитектура MVC. Views в качестве базы слоя управ¬
ления использует класс Notifier (”извещатель”), прямой потомок
корневого класса Object. В любом приложении всегда имеется
ровно один экземпляр класса Notifier, за которым навсегда закреп¬
лено имя notifier. Объект notifier включается в основной цикл
обработки событий. Кроме того, все приложения в качестве самого
верхнего визуализируемого объекта имеют экземпляр подкласса
из AppView. Все сообщения между объектной системой Views и
системой Windows проходят через notifier, ожидающий стартовое
Иерархические меню
315
сообщение, вслед за которым начинается обработка события. Фор¬
мальный класс Window предоставляет методы, обеспечивающие
обработку события любого типа.
Классы ViewsWindows и View подобны классам WindowsObject и
Window в Акторе соответственно. View имеет экземплярную пере¬
менную, названную model, которая используется для хранения
ID-объектов, предоставляющих модель данных для приложения.
Views использует три класса для реализации систем меню: Menu,
PopupMenu и MenuItem.
Views отличается от Актора в формировании отдельных классов
для различных типов кнопок, таких как PushButton, CheckBox,
RadioButton и TriState. Другим важным отличием служит то, что
класс TextEditor является потомком Control, а не View, как это
было бы, если бы проектирование следовало стилю Актора. Еще
одно существенное отличие состоит в том, что класс ViewsDialog —
потомок View. Если бы проектирование придерживалось линии
Актора, то Dialog был бы не прямым потомком Window, а нахо¬
дился на одном уровне с View. В системе Views полоски меню
конструируются при помощи создания экземпляра класса Menu и
добавления к нему экземпляров класса PopupMenu.
Различие в проектных подходах, используемых в Акторе и Views,
легко увидеть в способе, которым эти две объектно-ориентирован-
ные Windows-системы реализуют текстовое редактирование. Views
предоставляет класс, называемый TextEditor, который является
потомком Control и непосредственным подклассом EditBox. Сам
по себе объект TextEditor в действительности является окном,
способным хранить текст, выравнивать и проверять. Объекты
TextEditor спроектированы как часть в композиции объектов,
куда, кроме того, входят классы String, Stream и FileStream. Эта
композиция интегрирована в еще более крупную композицию,
которая образует главное окно. Класс ControlView спроектирован
таким образом, чтобы обеспечить возможность интегрировать эк¬
земпляры класса ControlWindow в качестве дочерних элементов
изображения полного приложения. Наряду с прочим он организу¬
ет списковые окна элементов управления.
■ ИЕРАРХИЧЕСКИЕ МЕНЮ
Как было показано в примере из предыдущей главы, иерархиче¬
ские или каскадные меню предлагают готовое решение, позволя¬
ющее большие массивы функциональных возможностей втиснуть
в одну полоску меню и при этом избежать замусоривания экрана.
Однако и здесь можно переусердствовать. Похожим и эквивален¬
тным иерархическим меню является вариант с дополнительными
полосками опций меню. В этом случае пространство экран*, необ¬
ходимое для опций, располагается вне основного прямоуголь**ш<
316
Вопросы проектирования в объектно-ориентированной среде Windows
меню. На мой взгляд, два иерархических меню — это максимум
того, что можно размещать в одном окне. В программе я решил
все множество опций уместить в одном меню. Хотя иерархические
меню можно создавать и динамически, и статически, оказывается,
что подход со статическим описанием ресурса-сценария для созда¬
ния меню настолько прост, что очень часто этим способом имеет
смысл пользоваться с самого начала разработки.
■ УПРАВЛЕНИЕ БОЛЬШОЙ И СЛОЖНОЙ
СИСТЕМОЙ МЕНЮ
Как отмечалось в предыдущей главе, у двух разных уровней проек¬
тирования, имеющих целью найти оптимальный дизайн пользова¬
тельского интерфейса, имеется ряд важных сторон. Прежде всего
это планировка, разбивка полоски меню. Как упоминалось выше,
традиционная разбивка вполне может дать спектр пользовательских
интерфейсов от отличных до почти непригодных. Пока в разработке
оптимальных пользовательских интерфейсов, так жекак и в других
областях объектно-ориентированного программирования, еще нет
однозначных методов, приводящих к успеху.
■ COMMONVIEW
Актор и Views похожи между собой гораздо больше, чем каждый
из них похож на CommonView. В отличие от Актора и Views
CommonView не имеет единого корневого класса типа Object. Для
создания интерфейса со средой Windows он предоставляет набор
независимых базисных классов: Control, Event, Menu, Window и
т.д. CommonView близок с Views в том, что DialogWindow является
подклассом Window, а Edit является потомком Control. Базис для
главных окон обеспечивается классом AppWindow и его прямыми
потомками TopAppWindow и ChildAppWindow. Вообще говоря,
CommonView транслирует сообщения Window в свои собственные
внутренние дескрипторы событий и Event-объекты.
Одной из наиболее оригинальных сторон проектирования
CommonView является класс Event и его потомки. CommonView
использует диспетчер, который отправляет Event-объекты к де¬
скрипторам событий в объектах Window. Event-объекты создаются
лишь временно, для передачи возникших событий в Windows.
Различные потомки класса Event связаны с соответствующими
типами событий. Хотя вопрос, чем по существу различаютсяАктор
и View, с одной стороны, и CommonView, с другой, — интересен,
архитектура последней из систем не может быть признана удач¬
ной, поэтому в последующих версиях вероятно предстоит корен¬
ная перестройка этой системы.
Классы для изображений в С++
317
■ КЛАССЫ ДЛЯ ИЗОБРАЖЕНИЙ В С++
Class
Object
Assoc
Clipboard
Container
Collection
OrdCollect
Stack
Set
Dictionary
Tokens
Display
BitMap
Window
Control
Button
PushButton
CheckBox
RadioButton
TriState
Group
InclusiveGroup
ExclusiveGroup
ListBox
ScrollBar
TextBox
EditBox
EditLine
TextEditor
View
AppView
PopupWindow
Dialog
ListSelect
FileSelect
Input
Report
YesNo
String
Stream
File
Archiver
ToFMStream
TagStrm
318
Вопросы проектирования в объектно-ориентированной среде Windows
TokenStrm
Menu
PopupMenu
MenuItem
Serial
Timer
Notifier
Port
Printer
Region
Polygon
Rectangle
RoundRect
Ellipse
■ КЛАССЫ COMMONVIEW
Accel
App
Bitmap
Brush
Color
Control
FixedIcon
TextControl
Button
CheckBox
PushButton
RadioButton
Edit
MultiLineEdit
SingleLineEdit
FixedText
ListBox
FileListBox
Scrollbar
HorizScrollbar
WndHorizScrollbar
VertScrolloar
W ndV ertScrollbar
Cursor
DrawObject
LineObject
ShapeObject
Классы CommonView
319
EllipseObject
RectangleObject
TextObject
Event
ControlEvt
ExposeEvt
FocusChangeEvt
KeyEvt
MenuCommandEvt
MenuCommandEvt
MenuInitEvt
MenuSelectEvt
MouseEvt
MoveEvt
ReSizeEvt
ScrollEvt
EventContext
Font
Icon
Menu
SysMenu
MessBox
ErrorBox
Pair
Point
Dimension
Range
Selection
Pen
Pointer
Rectangle
ResID
ResString
Window
AppWindow
ChildAppWindow
TopAppWindow
ControlWindow
EditWindow
DialogWindow
ModeLessDialog
FreeStore
GlobalAllocator
LocalAllocator
320
Вопросы проектирования в объектно-ориентированной среде Windows
Container
Lock
Stack
Ring
■ ПРОЕКТИРОВАНИЕ УПРАВЛЕНИЯ ПАМЯТЬЮ
В WINDOWS
В мультизадачных системах типа Windows часто в ходе одного
сеанса несколько приложений будут запрашивать память. Для
того чтобы быть уверенным в том, что доступ к памяти макси¬
мально эффективным образом получают все приложения, предо¬
ставляются соответствующие средства для управления памятью.
Чтобы гарантировать своим приложениям в Windows по крайней
мере такой объем памяти, какой им необходим, разработчики
должны пользоваться именно этими средствами. Всего в Windows
имеются около сорока различных функций по управлению па¬
мятью, которые позволяют разрешить как вопросы эффективно¬
сти, так и адресации к оперативной памяти. В следующих
разделах я приведу сжатый обзор способов управления памятью
при работе под управлением MS-Windows.
Память в Windows может выделяться двумя способами: из гло¬
бальной или локальной кучи (heap). Глобальная куча содержит
память, которая доступна всем приложениям. Локальная куча
предоставляет память только для отдельного приложения. Память
в Windows выделяется блоками и может выделяться повторно. Ее
можно переместить или даже сбросить, отказаться от нее. Пере¬
мещаемые блоки памяти не имеют фиксированных адресов. В
любой момент времени Windows может переместить их в другое
место. Наличие перемещаемых блоков позволяет сливать участки
свободной памяти для размещения весьма больших блоков. Ёсли
выделенный блок памяти лежит между двумя свободными участ¬
ками, его можно передвинуть так, что эти два участка сольются
в один блок с последовательной, непрерывной адресацией. Сбро¬
шенная память — это память, которая может быть освобождена
или выделена, размещена вновь. Естественно это требует уничто¬
жения данных, которые в ней содержатся. Когда в Windows
выделяется блок памяти, приложению, запросившему его, возвра¬
щается дескриптор блока (handle). Этот дескриптор представляет
собой не адрес, а скорее средство для поиска текущего адреса
данного блока в любой момент времени.
Доступ к блокам памяти использует механизм ’’запирания” —
блокировки дескрипторов памяти. Пока дескриптор заблокирован,
Windows не может перемещать или сбрасывать его. При этом
приложению выдается указатель на начало данного блока и оно
получает надежный доступ к памяти. ’’Отпирание” — разблоки¬
Типы хранения данных
321
ровка дескрипторов памяти выполняется самим приложением.
Эти средства нацелены на обеспечение максимально эффективного
управления памятью, пользователи должны следовать правилу, в
соответствии с которым по завершении использования блока его
дескриптор должен сразу же разблокироваться.
Большинство приложений в Windows использует смешанные мо¬
дели памяти. Рекомендуется работать с небольшими сегментами
памяти длиной около 4K для того, чтобы Windows было легче
перемещать эти сегменты в памяти. Приложения могут получать
память из глобальной и локальной кучи. Главное соображение,
определяющее, какую из куч следует использовать, обычно связа¬
но с объемом необходимой памяти. Блоки большего размера
обычно размещаются в глобальной куче, где один блок может
иметь размер больше 64K. Основными функциями Windows по
управлению глобальной кучей являются GlobalAlloc, GlobalLock,
GlobalUnlock, GlobalCompact и GlobalFree. Локальная куча прило¬
жения — это свободная память в сегменте данных, она может
выделяться для различных целей. Локальная куча предоставля¬
ется системой Windows не автоматически, он запрашивается при
помощи оператора HEAPSIZE. Естественно, локальная куча не
может быть более 64K, что является размером сегмента данных
приложения.
■ ТИПЫ ХРАНЕНИЯ ДАННЫХ
В Windows можно использовать семь типов хранения данных.
Используются для статических
переменных, таких как переменные>
объявляемые в С при помощи
ключевых слов static и extern
Используются для переменных,
размещаемых при вызове функции
в стеке
Любые данные в областях памяти,
которые размещаются при помощи
LocaLAJloc
Любые данные в областях памяти,
которые размещаются при помощи
GlobalAlloc
Используются как дополнительное
хранилище, которое может
потребоваться для оконного класса
Используются как дополнительное
хранилище, которое может
выделяться для структур WNDCLASS
Память, используемая для ресурсов
в в ЕХЕ-файлах приложений,
ii-857
Статические данные
Автоматические данные
Локальные динамические
данные
Глобальные динамические
данные
Дополнительные
байты окна
Дополнительные
байты класса
Ресурсы
322
Вопросы проектирования в объектно-ориентированной среде Windows
которые могут быть загружены
в память
Приложения, которые используют средства управления памятью
повышенной эффективности, должны обеспечивать реакцию на
сообщение WM_COMPACTING.
■ СБРАСЫВАЕМАЯ ПАМЯТЬ
Создание приложения в Windows со сбрасываемой памятью долж¬
но происходить явно. Для того чтобы создать сбрасываемый блок
памяти, необходимо использовать оба варианта функции
GlobaLAiloc: GMEM_MOVEABLE и GMEM_DISCARDABLE. Напри¬
мер, декларация имела бы такой вид:
hMem = GlobalAlloc (GMEM_MOVEABLE GMEM_DISCARDABLE) , 4096L) ;
Собственно отказ от сбрасываемой памяти в Windows произойдет
тогда, когда этого потребует заказ на выделение памяти. От какого
из сброшенных блоков в действительности отказаться, Windows
определяет на основе алгоритма LRU (least recently used — наибо¬
лее долго не используемый). Функция Windows GlobalDiscard от¬
казывается от данных, хранящихся в блоке, но сохраняет его
дескриптор. Функция GlobalReAlloc делает несброшенные блоки
памяти сброшенными и наоборот.
■ УПРАВЛЕНИЕПАМЯТЬЮ
ПОВЫШЕННОЙ ЭФФЕКТИВНОСТИ
В следующих двух разделах описываются две конфигурации па¬
мяти Windows, обеспечивающие повышенную эффективность. Эго
стандартная конфигурация и конфигурация для усовершенство¬
ванного режима 386 процессора.
■ СТАНДАРТНЫЙ РЕЖИМ
Конфигурация стандартного режима памяти Windows устанавли¬
вается по умолчанию в компьютерах с 286-м процессором, имею¬
щих по крайней мере 1 Мбайт памяти, и в компьютерах с 386-м,
имеющих более одного и менее двух мегабайтов памяти. Windows
использует защищенный режим процессоров 80286 и 80386 со
стандартным режимом памяти. Когда Windows запускается в этом
режиме, глобальная куча обычно разбивается на три отдельные
блока памяти. Первый сегмент — это обычно сегмент DOS на 640
Кбайт. Второй блок — это расширенная память, которая выделя¬
ется при использовании драйвера расширенной памяти, но потом
используется непосредственно. И наконец, третий блок в стандар¬
тном режиме — это область старших адресов памяти (high memory
area — HMA), которая может доступна только при условии, что
Библиотека WINMEM32. DLL
323
никакие другие программы до запуска Windows в область не
загружались. Глобальная куча Windows формируется при связы¬
вании этих трех областей памяти между собой. Сбрасываемые
сегменты памяти выделяются с верхушки кучи, закрепленные —
начиная с нижней границы кучи, перемещаемые программы и
данные — выше закрепленных сегментов.
■ УСОВЕРШЕНСТВОВАННЫЙ РЕЖИМ
386-ГО ПРОЦЕССОРА
В компьютерах с 386-м процессором с двумя мегабайтами памяти
и больше Windows может быть запущена в усовершенствованном
режиме 386-го процессора. В этом режиме Windows предоставляет
виртуальную память, которая использует и расширенную память,
и пространство жесткого диска, что позволяет обеспечить про¬
странство памяти, превышающее 64 Кбайт. В усовершенствован¬
ном режиме глобальная куча Windows состоит из большого
единого виртуального адресного пространства. Размер пространст¬
ва определяется объемом расширенной памяти и объемом доступ¬
ной памяти на диске. Структура этого пространства виртуальной
памяти, представляющая собой один большой блок, напоминает
базисную конфигурацию памяти, и ее разбивка является точным
аналогом базисной конфигурации, хотя размеры пространства
существенно больше.
■ НЕСКОЛЬКО ПРАВИЛ "ХОРОШЕГО ТОНА”
1. Не используйте длинные указатели на статические данные
в малых и средних моделях.
2. Не передавайте данные в другие приложения через гло¬
бальный дескриптор.
3. Не рассчитывайте на какую-либо взаимосвязь между де¬
скриптором и длинным указателем в любой из моделей.
4. Не выполняйте арифметических действий над сегментами.
5. Не сравнивайте адреса сегментов.
6. Не считывайте и не записывайте в объекты памяти после
их уничтожения.
■ БИБЛИОТЕКА WINMEM32.DLL
В качестве одной из составляющих пакета Windows 3.0 SDK фирма
Microsoft поставляет библиотеку динамического связывания
(dynamic link library), называемую WINMEM32.DLL, которая пре¬
доставляет функции для использования возможностей 32-битовой
адресации процессоров 80386 и 80486. Притягательность DLL в
том, что она потенциально предоставляет долгожданные возмож¬
324
Вопросы проектирования в объектно-ориентированной среде Windows
ности адресации, выходящие за рамки адресации сегментирован¬
ной памяти в мире MS-DOS. В ответ на вопрос, позволяет ли в
конце концов DLL использовать однородную модель памяти с
простой адресаций (flat memory model), к сожалению, приходится
отвечать и да и нет. Это правда, что функции, представленные
здесь, используют 32-битные регистры чипов 80386 и 80486.
Правда и то, что эти схемы адресации можно использовать при
определении единых сегментов памяти с длиной, большей, чем
может потребоваться какому-нибудь из них, Однако собственно
Windows — это программа, написанная в сегмейтириванной моде¬
ли памяти, как и MS-DOS. Поэтому каждое приложение в Windows
имеет по крайне мере один 16-битовый сегмент в старой 16-битовой
сегментированной модели. При этом, несмотря на данное ограни¬
чение, львиную долю программ и данных приложений в Windows
можно размещать с использованием новой 32-битовой адресации.
Главным в DLL является то, что она реализует стандарт для модели
памяти с простой адресацией» которого Microsoft обещает твердо
придерживаться в будущих версиях Windows. Однако в отноше¬
нии уверений в том, что программы, написанные с использованием
WINMEM32.DLL, будут поддерживаться во всех последующих
версиях Windows, есть некоторые сомнения. Можно ожидать, что
в использовании гибридной модели памяти, которая состоит час¬
тично из 16, а частично из 32-битовой адресации, появится ряд
проблем. И не удивительно, что трудности становятся особенно
острыми, когда вызываются прерывания. Все программы, напи¬
санные в 32-битовой модели, должны отвечать определенным
ограничениям, они не должны содержать программ или данных,
к которым возможен доступ во время прерывания. Для разработ¬
чика главным следствием этого ограничения является фактиче¬
ская невозможность использовать языки высокого уровня для
написания программ использующих WlNMEM32.DLL. Только
программисты, пишущие на ассемблере, могут попытаться исполь¬
зовать эту библиотеку. Это означает, что вероятно треть пакетов
поставят ассемблерный код, который можно будет загружать
вместе с программами на Си или на других языках для того, чтобы
обеспечить доступ к 32-битовой памяти для разработчиков, не
принятых еще в члены клуба искусного рукоделия на языке
ассемблера.
■ УПРАВЛЕНИЕ ПАМЯТЬЮ В АКТОРЕ
Одним из главных отличий между Актором и языком типа Си
является то, что Актор предоставляет автоматическую систему
управления памятью, а в Си приходится при необходимости
заниматься выделением и освобождением памяти. Актор выпол¬
няет инкрементальную сборку мусора в рамках фонового процесса,
что является одной из уникальных и важных возможностей среды
Управление памятью в Акторе
325
Windows. Не раз вы могли бы подумать, что Windows и самой
нужен хороший сборщик мусора.
Актор разделяет ресурсы памяти на статическую и динамическую
память. В статической памяти хранятся постоянные объекты,
такие как классы и методы. Динамическая память используется
для изменчивых конструкций строк и длинных целых. Смысл
введения двух типов памяти в том, что при этом мевьше работы
остается для сборщика мусора. Сборщик мусора оставляет стати¬
ческую память без внимания я заботиться только о мусоре в
динамической памяти. Его работа состоит в кодировании ,чжявых*
объектов в новую область памяти, а все остальное остается в старой
области.
Функция ShoeRoom! в меню рабочего окна показывает три харак¬
теристики: объем статической памяти Актора, объем динамиче¬
ской памяти и объем свободной памяти в Windows. После того
как вы запустили Актор, посмотрите на объем памяти Windows.
Для этого воспользуйтесь сообщением:
Call GlobalCompact(-1);
Затем сравните новое и старое значения свободной п&мяти
Windows. Таким же образом поступите и с локальной иамятью,
используя такое сообщение:
Call LocalCompact(-1);
Если на вашем компьютере вы располагаете изрядным объемом
памяти и имеете собственный MS-Windows 3.0 SDK, вы можете
попробовать поставить такой эксперимент. Загрузите программу
HeapWalker. Переместитесь в меню Alloc и выберите позицию
Allocate All of Memory (выделить всю память). Теперь вся память
Windows выделена. Вернитесь назад в меню Alloc и выберите
опцию Free 50 Кбайт (освободить 50 Кбайт). Не выходя из
HeapWalker, переключитесь на Актор и посмотрите в ShowRoom!
Если хотите, выполните
Call GlobalCompact(-1);
и сравните. Теперь вернитесь в HeapWalker и выберите из меню
Alloc Free All (освободить все). После этого можете вернуться назад
и сравнить данные функции ShowRoom! в Акторе.
Другой способ использования программы HeapWalker в качестве
источника справочных сведений состоит в том, чтобы запустить ее
в начале сеанса Windows и затем следить, сколько памяти выде¬
ляется для различных средств Windows. Кроме того, в любой
момент вы можете при помощи HeapWalker сохранять фиксацию
выборочных данных из карты памяти. Затем загрузите несколько
различных программ и сравните. Если необходимо,, сохраните
одну или несколько фиксаций состояния памяти Windows в раз¬
326
Вопросы проектирования в объектно-ориентированной среде Windows
личные моменты сеанса. Таким способом в результате небольших
упражнений вы познакомитесь поближе с методами управления
памятью в Windows в ситуациях, когда несколько задач конкури¬
руют из-за определенных ресурсов.
Опыт управления памятью в Акторе позволяет сделать ряд прак¬
тических выводов, которые условно можно объединить в две
группы. Одни касаются управления памятью в среде разработки,
а другие — управления памятью для пользователя приложения,
которое вы написали. И те и другие тем не менее тесно связаны
между собой. В целом то, что сохраняет память в программе,
которую вы написали, сохраняет ее и в среде разработки. Неплохо
знать, что поскольку сборщик мусора переписывает объекты меж¬
ду двумя динамическими областями памяти то в одну, то в другую
сторону, вам требуется вдвое больше пространства, чем вы, каза¬
лось бы, используете. По этой причине всегда, когда вы можете
разместить в статической памяти что-то, что до этого находилось
в динамической, вы экономите пространство.
Вы могли бы предположить, что в статической памяти тоже может
накапливаться мусор. Так оно и есть, поэтому в Актор входит и
статический сборщик мусора. Эту роль выполняет функция
Cleanup!, которую можно найти в рабочем окне Workspace. В
реальном режиме размер динамической памяти для Актора уста¬
навливается в диапазоне 25 — 30 Кбайт. Для усовершенствован¬
ного режима 386-го процессора верхняя граница устанавливается
в 1 мегабайт и определяется требованиями приложения и процесса
разработки. Приложения, написанные на Акторе, не могут извле¬
кать пользу из использования статического сборщика мусора, пока
это средство не встроено в них соответствующим образом, однако
динамическая сборка мусора является встроенной и автоматиче¬
ской и представляет собой одну из наиболее привлекательных черт
приложений, созданных при помощи Актора.
■ СВОППИНГ СТАТИЧЕСКОЙ ПАМЯТИ
Один из способов сократить количество памяти, которое програм¬
ма Актора использует в Windows, — это использовать технику
своппинга (подкачки) статической памяти. Своппинг статической
памяти работает постранично, отправляя наиболее долго неис¬
пользуемые объекты на диск и считывая их обратно, если они
понадобятся.
Практические методы управления памятью, предоставляемые
классом System, включают в себя: checkDynamic, cleanup,
dynamicGC, initMemory, staticRoom, staticSwapOff и
staticSwapOn. Последние два класса обеспечивают то, что
записано в их названии — выключение и включение своппин¬
га. Имеются дополнительные средства для управления своп-
Описания классов для работы с памятью
327
пингом статической памяти — при помощи использования клю¬
чевых слов SwapFlags и SwapFile в файле инициализации Windows
WIN.INI.
■ КЛАССЫ ПАМЯТИ АКТОРА
Двумя основными классами для управления памятью в Акторе
являются MemoryObject и его подкласс Handle. Классы
MemoryObject и Handle обеспечивают выделение памяти под объ¬
екты AjCTopa класса Struct из глобальной кучи Windows. В классе
Struct имеются несколько методов, используемых для преобразо¬
вания объектов в двоичный формат и обратно. Это дает Актору
возможность, если необходимо, интерпретировать блоки памяти
как объекты. Методы класса Handle выделяют закрепляемую,
перемещаемую или разделяемую память.
Сюда же относится подкласс ClassDescription класса MemoryBlock,
который заключает в себе ряд интересных возможностей.
MemoryBlock предоставляет доступ к блокам памяти Windows
размером более 64 Кбайт. Прежде всего в его реализации интерес¬
на переменная класса $LiveBlock, которая хранит дескрипторы
всех своих экземплярных блоков памяти. Схема MemoryBlock в
чем-то аналогична MemoryObject и Handle, скомбинированным
между собой. MemoryBlock использует такие же три экземплярные
переменные, как и Handle: handle, address и length.
■ ОПИСАНИЯ КЛАССОВ ДЛЯ РАБОТЫ С ПАМЯТЬЮ
Класс MemoryObject
Исходный файл: MEMORYOB.CLS
Происходит из: Struct
Имеет потомков: Handle
Методы класса:
new Возвращает новый экземпляр MemoryObject
Методы объектов:
copyFrom Обычный метод для копирования
copyFromLong Копирует данные из Long в себя. Полезен
для возвращения данных из Windows после
того, как они переписывались в область
памяти, начинающуюся с Long
fill Заполняет полученный Struct-объект словами
указанного вида
freeHandle Освобождает блок. Предполагает, что
соответствующая разблокировка
уже сделана
getData Возвращает данные self в форме struct
328
Вопросы проектирования в объектно-ориентированной среде Windows
getText
initCall
isIdx
keyDo
longAt
IP
putLOng
species
Возвращает строку из self с удаленными
пустышками
Вызывает программу обработки прерывания
со списком параметров, указанных в self
Возвращает true — индексированную очередь
инспектора
Выполняет одноаргументный блок
посредством ключей получателя. В случае
IndexedCollection ключи являются целыми
индексами коллекции и, вероятно,
представляют незначительный интерес.
Однако, Do сделан таким образом, что
kyeDo может работать с любой коллекцией
Возвращает число типа Long для данного
смещения
Получает длинный указатель закрывая,
блокируя блок
Запоминает число типа Long по данному
смещению
Возвращает родовой тип
Класс Handle
Исходный файл: HANDLE.CLS
Происходит из: MemoryObject
Переменные экземпляров:
handle
address
length
Методы класса:
set
Методы объектов:
addr
alloc
asHandle
asString
asStruct
Дескриптор глобальной кучи
Не равен nil, если заблокирован
Реальная длина выделенной памяти
Создает новый экземпляр Handle с данным
значением дескриптора
Возвращает адрес блока. Блокирует его, если
он еще не заблокирован
Выделяет в глобальной памяти блок данного
типа и данной длины и запоминает его
информацию
Возвращает дескриптор глобальной кучи и
возвращает nil, если память не выделена
Возвращает объект Актора, представляющий
содержимое этого блока памяти в форме
строки
Возвращает объект Актора, представляющий
содержимое этого блока памяти как struct
Описания классов для работы с памятью
329
byteAt
checkHandle
copyInfo
fastPutWord
fastWordAt
fixed
free
handle
length
lock
lower
movable
putByte
putWord
reAlloc
setHandle
shared
Возвращает значение байта по указанному
смещению. Временно, если блокировки еще
не было, блокирует дескриптор
Проверяет, что память выделена. Если нет,
то возбуждается сообщение об ошибке
Переписывает ByteCollection получателю.
Если необходимо, корректирует длину
получателя
Записывает словное значение по указанному
байтовому смещению. Этот метод
предполагает, что память получателя в
глобальной куче предварительно была
заблокирована. Если это не так, резулыат
будет непредсказуемым, возможно
катастрофическим
Возвращает словное значение из указанного
индекса. Этот метод основывается на таккх
же предположениях, что и предыдущий
Возвращает новый закрепленный дескриптор
Освобождает дескриптор, возвращает true,
если освобождение выполнено
Возвращает дескриптор глобальной кучи
Возвращает реальную длину выделенной
памяти. Если память не выделена,
возвращает nil. Заметьте, что реальная длина
может отличаться от той, которая
запрашивалась (но никогда не меньше). Это
зависит от того, как Windows выделяет
память в глобальной куче
Блокирует дескриптор, возвращает длинный
адрес
Выделяет перемещаемую память из
неразмещенной части глобальной кучи
Windows
Возвращает новый перемещаемый дескриптор
Запоминает байтовое значение по указанному
байтовому смещению
Запоминает словное значение по указанному
байтовому смещению
Повторно выделяет блок глобальной памяти,
данного типа и длины и размещает в нем
его информацию
Устанавливает значение дескриптора блока
памяти
Возвращает новый дескриптор разделяемой
памяти
330^
Вопросы проектирования в объектно-ориентированной среде Windows
size
sysPrintOn
unlock
MemoryBlock
Исходный файл:
Происходит из:
Переменные класса:
$LiveBlocks
Переменные экземп-
handle
address
length
Методы класса:
destroyAll
fixed
init
liveBlocks
lower
Возвращает реальную длину выделенной
памяти. То же, что для сообщения length
Печатает объект Handle в указанный поток
Выполняет разблокировку дескриптора,
возвращает true, если счетчик ссылок
достиг 0
MEMORYBL.CLS
Object
Дескрипторы всех выделенных на данный
момент блоков
шров:
movable
shared
Методы объектов:
alloc
asStructAt
byteAt
checkHandle
сору
destroy
fastPutWord
Уничтожает все ”живые” блоки MemoryBlock
Возвращает новый закрепленный дескриптор
Инициализирующий метод
Возвращает набор ”живых” блоков памяти
Выделяет перемещаемую память из еще не
распределенной части глобальной кучи
Windows
Возвращает новый перемещаемый дескриптор
Возвращает новый дескриптор из разделяемой
памяти
Выделяет в глобальной памяти блок данного
типа и длины и запоминает его информацию
Возвращает Srtuct размера sz, содержащий
данные, начинающиеся с указанного
смещения
Возвращает значение байта по указанному
смещению. Временно, если блокировки еще
не было, блокирует дескриптор
Проверяет, что память выделена. Если нет,
то возбуждается сообщение об ошибке
Переписывает блок с переносом его хвоста
в голову сегмента, если в этом имеется
необходимость
Разблокирует и уничтожает self и исключает
дескриптор из $LiveBlock
Записывает словное значение по указанному
байтовому смещению. Этот метод предполагает,
Описания классов для работы с памятью
331
fastWordAt
free
handle
length
lockLock
longAt
putByte
putLong
putStructAt
putWord
removeRef
setHandle
setLength
sysPrintOn
unlock
workAt
что память получателя в глобальной куче
предварительно была заблокирована. Если
это не так, результат будет непредсказуемым,
возможно катастрофическим
Возвращает словное значение из указанного
индекса. Этот метод основывается на таких
же предположениях, что и предыдущий
Освобождает дескриптор, возвращает true,
если освобождение выполнено
Возвращает дескриптор ivar
Возвращает длину блока
Блокирует дескриптор, возвращает длинный
адрес
Возвращает число типа Long для данного
смещения
Запоминает байтовое значение по указанному
байтовому смещению
Запоминает значение типа Long по
указанному смещению
Переписывает nBytes байтов из структуры
ByteCollection по данному смещению
Запоминает словное значение по указанному
байтовому смещению
Исключает MemoryBlock из набора LiveBlocks
без освобождения памяти. Этот метод
используется для блоков памяти, которые
будут подаваться в буфер или из буфера
clipboard
Устанавливает значение собственного
дескриптора. Используется только системой
Устанавливает собственную длину.
Используется только системой
Печатает объект Handle в указанный поток
Выполняет разблокировку дескриптора,
возвращает true, если счетчик ссылок
достиг 0
Возвращает словное значение из указанного
смещения. Заметьте, что индекс является
байтовым смещением, т.е. в случае (handle, 2)
возвращается слово, расположенное со
смещением в 2 байта. Блокирует временно
дескриптор, если он еще не был
заблокирован
332
Вопросы проектирования в объектно-ориентированной среде Windows
Я ПРОЕКТИРОВАНИЕ
ИНДЕКСНО-ПОСЛЕДОВАТЕЛЬНОЙ
БАЗЫ ДАННЫХ
Этот раздел посвящен другой важной сфере возможностей компь¬
ютеров — массовому хранению данных. Какие преимущества дает
объектно-ориентированный подход в повышении эффективности
имеющихся сред хранения данных на дисках? В действительности
существует ряд преимуществ объектно-ориентированного подхода
при реализации баз данных с традиционными файлами прямого
доступа, близкими к индексно-последовательному типу. Для того
чтобы проиллюстрировать, каковы эти преимущества и как можно
проектировать перспективные системы хранения данных с их
помощью, я сначала опишу расширение Актора для построения
объектно-ориентированных баз данных, называемое Wintrieve.
Wintrieve — это пакет для программирования баз данных под
управлением MS-Windows, который поддерживает стандарт
X/Open ISAM (индексно-последовательного метода доступа) для
баз данных. Одним из важных достоинств Wintrieve, которое
оценят многие программисты, состоит в том, что он не ограничи¬
вает количество индексов. Двумя другими привлекательными
особенностями пакета Wintrieve являются поддержка транзакций
и журнализация. В сочетании друг с другом эти средства помогают
поддерживать целостность больших и сложных баз данных. Пакет
Wintrieve включает средства для работы с языками Актор и Си.
То, что из объектно-ориентированного языка типа Актор в системе
Windows имеется доступ к файловой системе ISAM, является
несомненным достоинством. Данные из файлов ISAM можно счи¬
тывать в предопределенные объекты Актора и писать их обратно.
Теперь для разработки баз данных стали доступными основные
достоинства хорошо оснащенных объектно-ориентированных сис¬
тем. Таким образом, в определенном смысле вы получаете самое
лучшее из того, на что можно было бы рассчитывать. Перед
описанием того, как спроектированы эти системы, было бы полез¬
но дать краткий обзор архитектуры Wintrieve.
■ WINTRIEVE - КРАТКИЙ ОБЗОР
Наиболее важными классами в Wintrieve, с которыми надо позна¬
комиться в первую очередь, являются IsamFile и IsamManager.
IsamFile предназначен для создания объектов, которые могут
управлять отдельными файлами данных. У него имеются шесть
экземплярных переменных, которые приводятся в табл. 10.1.
Файл ISAM можно организовать при помощи метода create класса
IsamFile, после чего дескриптор записи запоминается как строка
в информационной секции файла.
Wintrieve — краткий обзор
333
Таблица 10.1. Экземплярные переменные в IsamFile
filename
record
keys
manager
currentIndex
block
Имя файла
Экземпляр IsamRecord
Словарь для пар ключей keyName/key
Объект IsamManager
Значение keyName в текущем индексе
Блок в глобальной куче, используемый в com
для ISAM Server
Класс RelFile предоставляет средства для связывания множества
файлов данных в реляционную базу данных. По используемой
схеме один из объектов RelFile выступает как владелец для других
IsamFile- и RelFile-объектов, которые составляют реляционную
базу данных. Метод relate класса RelFile можно использовать для
построения связей между полями объекта-владельца типа RelFile
и полями других файлов. Эти связи имеют смысл только для
операций чтения.
BF := new(BookFile); setFilename(BF, "c:\cbook\book.dat");
MasterFile := initFile(new(RelFile), BF, "book", #book);
RelFile"book")
DependentFile1 := initFile(new(IsamFile), DB, "aut",
#author);
IsamFile("author")
DependentFile2 := initFile(new(IsamFile), DB, "publ",
#publisher);
IsamFile("author")
relate(MasterFile, "publD, DependentFile2, #primary);
Подчиненный (dependent) файл в объекте-владельце RelFile сам
может быть RelFile-объектом, который является владельцем своих
подчиненных, и т.д. Таким образом строится иерархия подчинен¬
ных файлов.
Техника, основанная на чтении ISAM-данных в объекты, не безуп¬
речна. Есть ряд обстоятельств, в которых проявляются и недостат¬
ки данного подхода. В реляционной базе данных, если для каждого
файла из базы данных у вас имеется отдельный класс, появляются
намерения уклониться от инкапсуляции, когда она начинает
работать против вас, поскольку объекты одного класса, возможно,
не имеют непосредственного доступа к данным объектов других
классов. Конечно, имеется ряд способов обойти подобные трудно¬
сти. При использовании интерфейса Си++ для Wintrieve классы
могут определяться как классы-друзья. В Акторе классы для
файлов данных могли бы быть потомками общего суперкласса,
который давал бы полный дескриптор данных для всех полей
записей различных файлов. Как обычные потомки этого общего
для них класса классы файлов данных имели бы доступ к любым
334
Вопросы проектирования в объектно-ориентированной среде Windows
данным. Другой вариант состоит в объединении объектов различ¬
ных файлов данных в один объект-коллекцию.
■ ОБЪЕКТНО-ОРИЕНТИРОВАННЫЕ СИСТЕМЫ
РЕЛЯЦИОННЫХ БАЗ ДАННЫХ
Объектно-ориентированные системы реляционных баз данных —
это специально разработанные приложения, обладающие всеми
достоинствами традиционных реляционных баз данных и в то же
время предоставляющие привлекательные возможности, которые
может дать последовательно вы держанное модульное проектиро¬
вание как в сфере обеспечения доступа, так и в сфере представле¬
ния данных.
Одной из наиболее привлекательных особенностей таких систем
является то, что объектно-ориентированная система может подпи¬
тываться реляционной базой данных так, что конечный пользова¬
тель даже не будет замечать этого. Объектно-ориентированный
подход весьма удобен для создания внешних оболочек (front end)
реляционных баз данных, которые скрывают внутренние механиз¬
мы файловой системы и обладают чрезвычайной гибкостью в
средствах представления данных для пользователя. В последую¬
щих разделах я опишу архитектуру Wintrieve — объектно-ориен¬
тированной системы для построения реляционных баз данных,
построенной как расширение языка Актор.
■ РАЗРАБОТКА ОБЪЕКТНО-ОРИЕНТИРОВАННЫХ БАЗ
ДАННЫХ ПРИ ПОМОЩИ WINTRIEVE
Как вы уже видели, в Wintrieve при помощи класса RelFile и
метода relate для создания связей можно построить полную реля¬
ционную базу данных. В традиционных базах данных наборы
файлов данных используются для представления больших связан¬
ных массивов информации, например о различных видах деятель¬
ности в бизнесе. Всегда имеется некоторое поле данных, общее для
двух файлов. В идеальном случае каждый файл должен иметь
хотя бы один другой файл с полем из общей области.
Хотя система файлов данных Wintrieve сама по себе не является
иерархической базой данных, но настоящая объектно-ориентиро¬
ванная система всегда может стать таковой, поэтому Wintrieve
может быть использована для построения иерархической базы
данных. Иерархическая база данных — это такая база данных,
которая поддерживает понятийные взаимосвязи между файлами
данных, а также перекрытия полей, реализующих связи-отноше¬
ния. Например, все файлы данных в иерархической базе данных,
которые, скажем, относятся к средствам передвижения некоторого
вида, могли бы иметь некоторую понятийную взаимосвязь неза¬
Классы Wintrieve
335
висимо от того, связаны или не связаны они связью-отношением.
Экземплярные переменные объектов подобны полям в иерархиче
ской базе данных. Однако в системах типа Wintrieve обычно не
обеспечивается произвольный доступ к объектам.
Несмотря на то, что реляционные и иерархические базы данных
определенно различаются, есть возможность имитировать иерар¬
хические базы данных при помощи реляционных. Очевидный
метод здесь состоит в том, чтобы включить в каждый файл данных
такие поля, как: InheritsFrom, InheritedBy и InstanceOf (наследу-
ет_из, имеет_наследников и является_экземпляром). Это были бы
’’мета-поля” файлов данных, в которых хранилась бы скорее
информация о понятийной структуре самой файловой системы, а
не содержащаяся в ней информация из реального мира.
По аналогии с имитацией иерархической базы данных реальную
иерархию объектов можно было бы построить из классов системы
объектов. В конце концов для каждой записи базы данных может
быть создан объект, но для большинства приложений это могло
бы показаться расточительной тратой ресурсов — хранить объект,
пока в нем не отпадет необходимость. Поскольку объекты созда¬
ются, они могли бы организовать чтение соответствующих записей
в наборы своих переменных. В идеале подобная система могла бы
функционировать так, как будто для иерархической базы данных
обеспечивается произвольный доступ. В действительности, здесь
потребовались бы некоторые компромиссы, обусловленные соотно¬
шением между существующими требованиями приложений и тем,
что можно реализовать на практике.
■ КЛАССЫ WINTRIEVE
Object
Collection
IndexedCollection
ByteCollection
Struct
MemoryObject
Handle
KeyedCollection
Dictionary
OrderedDictionary
CStruct
IsamCStruct
SubStruct
CType
IsamRecord
UserType
FieldInfo
336
Вопросы проектирования в объектно-ориентированной среде Windows
IsamDesc
IsamDict
IsamKey
IsamKeyParser
IsamFile
RelFile
IsamManager
WindowsObject
Window
IsamLink
lsamCStruct
IsamCStruct подобен объектам CStruct, но его данными служит
объект Handle. Память последнего выделяется из неразмещенной
(нижней) части глобальной кучи.
lsamRecord
Объект lsamRecord также является специальным видом CStruct,
который определяет внутреннее строение записей для доступа к
файлам Isam Актора.
IsamDict
Класс IsamDict предоставляет протокол для доступа и работы со
структурами словаря.
IsamKey
Класс IsamKey обеспечивает протокол для создания и поиска
ключей индекса Isam.
lsamLink
Класс lsamLink предоставляет протокол для управления подклю¬
чением сервера ISAM Server. Использование объекта lsamLink
обычно требуется в начале сеанса, при посылке требований и
завершении сеанса с использованием ISAM Server. lsamLink про¬
исходит из класса Window, поскольку связь между приложениями
в MS-Windows в действительности выполняется через окна. Де¬
скрипторы окон используются для того, чтобы уникально иденти¬
фицировать сообщающиеся между собой абоненты. Если вам
потребуется для обращения к ISAM Server узнать, как должно
заполняться то или иное поле в queryBlock, обращайтесь к руко¬
водству ”ISAM Server Protocol Specification”.
IsamFile
Класс IsamFile предоставляет протокол для управление отдельны¬
ми файлами ISAM.
Базисные классы, используемые в Wintrieve
337
RelFile
Класс RelFile предоставляет протокол для связывания полей за¬
писи родительского RelFile-объекта с ключевыми полями подчи¬
ненного IsamFile-класса или его подчиненными объектами.
lsamManager
Класс lsamManager предоставляет базисный интерфейс сервера
Isam Server. Все обращения к ISAM Server осуществляются через
объект lsamManager. lsamManager предоставляет протокол для
управления сеансом, транзакциями, журнализацией и доступом к
файлам.
lsamDesc
Класс lsamDesc — класс поддержки, который предоставляет про¬
токол для управления дескрипторами записей ISAM.
lsamKeyParser
IsamKeyParser — это класс поддержки для класса IsamKey. Он
предоставляет протокол для разбора ключевых атрибутов и клю¬
чевых разделов дескрипторов.
■ БАЗИСНЫЕ КЛАССЫ, ИСПОЛЬЗУЕМЫЕ В WINTRIEVE
CStruct — это класс для поддержки обращений к структурам
языка Си. CStruct содержит две экземплярные переменные: сло¬
варь характеристик полей и двоичные данные. CStruct полезен
везде, где требуется переход от объектов Актора к двоичным
данным и обратно, как, например, при взаимодействии с ОС,
файлами на дисках или средствами управления окнами. Для
реализации своих объектов CStruct использует еще четыре класса:
FieldInfo, CType, UserType и SubStruct.
Класс FieldInfo хранит информацию о полях в объектах со Си-
структурой. Объекты CType описывают типы языка Си для Си-
структур и организуют трансляцию из формата Актора в двоичный
формат Struct. Экземпляр класса CType необходим для каждого
типа данных в языке Си, который будет использоваться как поле
а CStruct-объектах. Файл CTYPES.ACT содержит некоторые про¬
стые определения CType. Класс UserType поддерживает типы,
определяемые пользователем для структур Актора CStruct. Класс
Substruct предоставляет объекты CStruct, которые являются вло¬
женными полями других структур.
Объекты Struct имеют фиксированный размер, они являются
индексированными коллекциями слов (два байта) или длинных
слов (четыре байта). Эти объекты полезны для обмена информа¬
цией с MS-Windows и другими языками программирования, Все
классы геометрических объектов в Акторе, за исключением Point,
являются потомками Struct. К данным, помещающимся внутри
338
Вопросы проектирования в объектно-ориентированной среде Windows
объектов Struct, доступ осуществляется посредством указания
смещения в байтах, например, ”слово, расположенное в байте со
смещением 3”. Класс MemoryObject является абстрактным роди¬
тельским классом для описания блоков ”неакторовской” памяти.
Класс Handle описывает объекты памяти, выделенные системой
управления глобальной памятью.
■ ОПИСАНИЯ КЛАССА WINTRIEVE
OrderedDictionary
/* Упорядоченный словарь */
Исходный файл: ORDEREDD.CLS
Наследует из: Dictionary
Переменные экземпляров:
orderKeys Коллекция для хранения ключей в
упорядоченном виде
Методы объектов:
addAssoc
assocDo
do
grow
init
keys
keysDo
Добавляет связь (Association) в словарь
Находит блок для каждой полученной связи
Перенумеровывает все элементы
в OrderedCollection
Копирует элементы в коллекцию большего
размера и помещает ее на место старой
коллекции
Инициализирует KeyedCollection, устанавливая
в экземплярную переменную tally 0
Возвращает ключи в правильном порядке
Находит один аргументный блок через
ключи в словаре
Заменяет текущий элемент или создает новый.
Для этого класса метод put во всем, кроме
порядка аргументов, идентичен методу add
Удаляет из словаря элемент с указанным
ключом. Если для указанного ключа aKey
соответствующего элемента нет, то
выполняется О-аргументный блок. Данный
метод возвращает удаленный ключ или
значение блока
setCompareBlock Выполняет пересортировку в соответствии с
newCompareBlock и возвращает self
setOrderClass Задает класс для упорядочивания ключей
put
removeUsing
■ ОПИСАНИЯ КЛАССА SAMPLE
BookWindow
Исходный файл: BOOKWIND.CLS
Описания класса Sample
339
Наследует из: TextWindow
Переменные экземпляров:
keyDict
bookDB
bookTable
Методы объектов:
changeIndex
closeDB
ommand
createMenu
deleteRec
getISBN
getTitle
init
insertRec
nextRee
openDB
prevRec
printRecord
recDlg
searchRec
shouldClose
updateRec
validateInput
Словарь ключей
Система управления ISAM (ISAM manager)
book file для ISAM
В выбранном индексе выполняет сортировку
в соответствии с новым порядком и выдает
первую запись
Закрывает базу данных
Обрабатывает события меню. Аргумент wp
задает выбранное ID для меню. Берет символ
сообщения из объекта-меню
Задает полоску меню
Уничтожает текущую запись
Организует обработку ввода в диалоге для
получения номера ISBN. Возвращает номер
ISBN или nil, если пользователь ответил
отказом
Организует обработку ввода в диалоге для
получения заголовка. Возвращает заголовок
или nil, если пользователь ответил отказом
Инициализирует Book Window. Создает меню
и ”About” для управления меню
Вставляет запись
Читает следующую запись и выдает ее
Начинает сеанс с ISAM manager
Читает предыдущую запись и выдает ее
Выдает текущую запись в окно
Строит диалог для записей
Выполняет поиск записи, используя
имеющийся порядок в индексе
Закрывая окно закрывает и базу данных
Модифицирует запись
Проверяет ввод от диалога записи. Если все
в порядке, то возвращает словарь значений
полей ввода. Если в одном из полей имеется
ошибка, то выдает окно ошибок и затем
возвращает nil
BookDlg
Исходный файл:
Наследует из:
BOOKDLG.CLS
Window
Переменные экземпляров:
isbn Элемент управления для поля редактирования
title Элемент управления для поля редактирования
340
Вопросы проектирования в объектно-ориентированной среде Windows
pubID
quantity
price
isbnVal
titleVal
pubIDVal
quantityVal
priceVal
ok
bOK
bCancel
bookFile
op
Методы объектов:
command
initButtons
initEdits
isbn
price
pubID
quantity
setIsbn
setPrice
setPubID
setQuantity
setTitle
shouldClose
title
#
Элемент управления для поля редактирования
Элемент управления для поля редактирования
Элемент управления для поля редактирования
Значение поля ввода
Значение поля ввода
Значение поля ввода
Значение поля ввода
Значение поля ввода
Флажок для информирования о согласии
пользователя
Элемент управления — кнопка OK (о’кеу)
Элемент управления — кнопка Cancel (отказ)
Управляет командными событиями.
Проверяет, какая кнопка — theOK или
CANCEL — нажата, и предпринимает
соответствующие действия. Если это OK, то
читает значения из полей ввода
Создает кнопки OK и CANCEL
Создает поля ввода
Возвращает значение поля isbn
Возвращает значение поля price
Возвращает значение поля pubID
Возвращает значение поля quantity
Задает выдаваемое значение поля ISBN
Задает выдаваемое значение поля price
Задает выдаваемое значение поля pubID
Задает выдаваемое значение поля quantity
Задает выдаваемое значение поля title
Посылает обратно к родителю сообщение о
том, что получатель собирается закрываться
Возвращает значение поля title
■ УКАЗАТЕЛЬ
Accessories, группа 10
ActorApp, класс 180, 196
Always Warn, опция 19
AppView, класс 85, 86
Arrange All, опция 22
Array, класс 66, 67, 147, 148
AsciiFiler, класс 66, 67
AVL, класс 67
BalNode, класс 66, 67, 69
Buffer, класс 79
С, язык 44, 52, 55, 75—77, 117,
141, 156, 275, 324, 332
С++, язык 52—64, 314, 333
CaJculator, утилита 13—15
Caiendar, утилита 12, 14
Cardfile, утилита 14
Codeview, отладчик 114
Collapse branch, опция 11
Collection, класс 68, 79, 146, 147
Container, класс 79, 146
Control Panel, утилитаЮ, 12, 18
Ctalk, язык 6, 75—84, 89, 91
Detect Idle Time, опция 20, 21
EMM386.SYS, драйвер памяти 15
Exeel, электронная таблица 5, 8, 15,
16, 24, 27, 34
Exclusive in Foreground, опция 19
ExecWindow, функция 193—196
Expand All, опция 11
Expand Branches, опция 11
Expand One Level, опция 11
File Manager, утилита 9, 10—12
Games, группа 10
Heapwalker, отладочное средство
112, 117, 325
HIMEM.SYS, драйвер памяти 15
Hyperscript, язык 29, 30
Idle, опция 19
Inspector, отладочное средство 133
Macro Recorder, утилита 12, 17
Main, группа 10
Minimum Time-Slice, опция 19
Newer Warn, опция 19
NewWave, среда 22—24, 312, 313
Non-Windows Applications 10
Notifier, класс 85, 86
Objective-C, язык 6, 64—75
Paintbrush, графическая утилита
12, 15, 16
Print Manager, утилита 9, 10, 16,
17, 18
PrintScreen, ’’горячая” клавиша 35
Program Manager, утилита 9, 10, 35
Run, команда 10
SDK Paint, утилита 109
Setup, программа задания
конфигурации 16
Shaker, отладочное средство
113—114
Software Development Kit (SDK),
пакет разработчика 107—118, 323
Spy, отладочное средство 111, 117
Stream, класс 79
Swap, отладочное средство 113
Switch to, опция 10
Symdeb, отладчик 114
Toolbook, среда 24—28
VGA, видеоадаптер 13
Video New Wave, программа 24
VIEWS, среда программирования 6,
314—315
Wingz, электронная таблица 28—35
WIN.INI, инициализационный файл
7, U
WINMEM32.DLL, библиотека
работы с памятью 323—324
Word for Windows, текстовый
процессор 8, 15, 21, 22
342
Указатель
Zoomin, утилита 110, 111
Zortech С++, компилятор 60—62
Zortech Tools, набор инструментов 63
Активные данные (Active Data) 41
Актор (Actor) 6, 71, 74, 130—171
Анимация 25, 26, 231—232
Битовая карта 226, 250
Броузер 46, 51
Виртуальные классы 57—58
Виртуальные функции 57—58
Дата 159—164
Деструктор 58—59
Диалоговое окно 101—102
Динамическийдиалог 138, 187—189
Динамическое меню 138, 186—187
Динамический обмен данными
(Dynamic Data Exchange DDE) 16,
26, 106
Динамически подключаемая
библиотека (Dynamic Link Library
DLL) 27, 35, 106, 323—324
Иерархия классов 43, 66—68
Инициализационный файл 11, 19—20
Инкапсуляция 44, 45, 54
Интерфейс с графическим
устройством (Graphics Device
Interface GDI) 175
Интерфейс с многими документами
(Multiple Document Interface MDI)
274—279
Информационный контекст
(Information context) 104
Карман (Clipboard) 10, 12—13, 16,
35—36
Класс 42—49, 52—53
Коллекция 69—71
Командная строка DOS (DOS
Prompt) 10
Комбинированное окошко (Combo
box) 102—103, 190—191
Компилятор ресурсов (Resource
compiler) 107—108
Конструктор 58—59
Контейнер 23
Контекст устройства (device context)
104
Линия прокрутки (scrollbar) 9, 101
Меню 88—89, 100, 138—139,
186—187, 280—282
Метакласс 44
Метафайл 104—105
Метод 43
Многозадачность 9—10
Наследование (Inheritance) 42
Объектно-ориентированная графика
217—272
Окошко списков (list box) 102,
189—190
Отладка 61—63, 108, 111
Палитры 260
Перегрузка (Overloading) 43, 55—57
Переключение задач (Task switch) 35
Переменные классов 45, 151
Перехват экрана (Screen capture) 13, 16
Пиктограммы 253
Подменю (Popup menus) 274
Порт ввода-вывода 19
Принтер 12, 24
Процессор заголовков (Outline
processor) 22
Профайлер 110, 111
Реальный режим (Real mode) 9
Редактор диалогов (Dialog Editor)
93, 109
пиктограмм (Icon Editor) 15
шрифтов (Font editor) 18, 93, 110
Смолток (SmallTalk) 44, 46, 51, 55,
65, 66, 68, 71, 73, 74, 75, 76, 140,
141, 143, 275
Создание экземпляров 42—44
Сообщение 96—97
Список задач (Task List) 9
Стандартный режим (Standard mode) 9
Стиль интерфейса пользователя 116
Суперкласс 42, 43, 48
Фон экрана 36
Функции Windows 94—98, 118—125
Часы (Clock) 12, 164—166
Усовершенствованный 386 режим
(386 Enchanced mode) 9, 13, 323
Управление памятью (Memory
managment) 7, 21, 305, 320—331
Управляющие элементы (Controls)
99, 100, 137—138
Электронная кнопка (Button) 101,
182—184, 315
Электронная таблица (Spreadsheet)
310—311
■ ОГЛАВЛЕНИЕ
Предисловие. Зачем нужно объектно-ориентированное программирование
для Windows? 5
Глава 1. Введение в Windows 3.0 8
Что нового в версии 3.0 9
Многозадачность —
Менеджер программ (Program Manager) 10
Основная группа (Main Group) —
Менеджер файлов CFtie Manager) —
Карман (Clipboard) 12
Перехват экрана 13
Калькулятор —
Работа с числовыми данными 15
Запись макро СМясго Recorder) 17
Управление печатью (Printer Manager) —
Панель управления (Control Panel) 18
Управление усовершенствованным 386 режимом —
Управление многозадачностью 19
Инициализационные файлы —
Редактор .РН*1 файлов СРП? Editor) 20
Клавиши активизации, определяемые прикладной программой 21
Управление памятью —
Microsoft Word for Windows —
Работа с заголовками 22
NewWave * —
Office Tools 23
Toolbook 24
Wingz: электронная таблица нового поколения 28
Сеанс работы с Windows 3.0 35
Глава 2. Что такое объектноориентированное программирование? 37
Введение —
Парадигмы программирования —
Объектноориентированный подход 38
Метафоры объектно-ориентированного программирования 40
Активные данные 41
Передача сообщений —
Классы: порождение экземпляров и наследование 42
Типы объектноориентированных систем 44
Как работают объектноориентированные системы? 45
Использование объектноориентированных систем 46
Почему применяют объектноориентированное программирование? . . . —
Некоторые выводы 47
Дополнительные заботы программиста 49
Глава 3. Объектноориентированные расширения Снс языки и
инструментальные средства 52
Обзор средств Си++ 2.0 —
Структуры и классы 53
Множественное наследование * 54
Функции-члены —
Регулируемая степень инкапсуляции —
Функциц-друзья —
Перегрузка операторов и функций 55
Прототипы 56
Виртуальные функции и классы 57
Конструкторы и деструкторы 58
344
Оглавление
Компилятор фирмы Zortech 60
Модели памяти —
Карта памяти программ 61
Отладчик для Си++ —
Классы библиотеки Zortech Tools 63
Заключительные замечания о Си++ —
Язык Objective-C 64
Иерархия классов 66
Коллекции 69
Графика 71
Символьная отладка 73
Обсуждение —
CtaUc 75
Синтаксис CtaUc 77
Базовые классы Ctalk 78
Создание исполняемых программ 81
Заключительные замечания о CtaDc —
VIEWS: объектно-ориентированный инструмент разработки
Си-программ для Windows 82
Броузер Си++ системы VD3WS 83
Модель MVC пользовательского интерфейса 84
Класс Appview 85
Назначение окон в системе VLEWS 86
Класс View 87
Создание диалогов с помощью генераторов интерфейсов 88
Создание меню —
Классы редакторов текстов 89
Таймер 90
Коммуникационный класс —
Графика —
Ускорители приложений . . . . 91
Заключительные замечания о VffiWS . . . . —
Глава 4. Разработка программ для Windows 3.0 92
Что нового появилось в SDK Windows 3.0? : —
Как работает система Windows 93
Функции, которые создают объекты 94
Классы окон . . —
Сообщения 96
Функции окон 97
Контекст дисплея 98
Элементы оконных пользовательских интерфейсов —
Управляющие элементы, перерисовываемые владельцем 99
Пиктограммы —
Меню . . . 100
Управляющие элементы —
Кнопки 101
ScroU Bars (линии прокрутки) —
Диалоги —
Окошки списков 102
Комбинированные окошки —
Всплывающие меню 103
Интерфейс с графическим устройством —
Интерфейс со многими документами C^DI — Multi-Document
biterface) 105
Динамически подключаемые библиотеки (DLL — Dynamic Link
Libraries) 106
Динамический обмен данными (DDE — Dynamic Data Exchange) . . . . —
Машинно-независимая цветная графика 107
Система построения справочной информации (Help) —
Компилятор ресурсов 107
Оглавление
345
Редакторы ресурсов и инструменты 108
Редактор диалогов 109
Утилита SDKPaint —
Редактор шрифтов FontEdit 110
Лупа —
Профайлер —
Spy (мониторинг сообщений) 111
HeapwaUcer, (мониторинг кучи) 112
Swap (обмен) 113
Shaker (миксер для кучи) —
CodeView for Windows 114
Отладчик Symdeb для реального режима —
Обеспечение совместимости между версиями 2.0 и 3.0 115
Структура прикладных программ для Windows —
Стиль пользовательского интерфейса Windows 116
Описания ресурсов —
Заключение —
Управляющие сообщения редактирования 118
Новые функции версии 3.0 —
Новые сообщения Windows 3.0 125
Новые структуры данных Windows 3.0 129
Глава 5. Введение в систему программирования Актор 130
Язык Актор, версия 3.0 . —
Среда программирования Актор —
Броузер Актора 131
Инспекторы 133
Классы Актора 134
Классы MS-Windows —
Элементы управления 137
Динамические меню и диалоги 138
Callbacks . . . . .^ 139
Списковые структуры —
Программирование на Акторе 140
Основы синтаксиса Актора —
Немного музыки? 143
Программные блоки —
Форматы файлов —
Тренировки в языке Актор —
Сеанс с классом Number . . . 144
Строки . . . . 145
Структуры управления —
Циклы —
Коллекции 146
Массивы 147
Другие коллекции 148
Упорядоченные коллекции 149
Словари —
Текстовые коллекции 150
Очереди —
Переменные класса . 151
Вызов библиотек с динамическим связыванием 152
Представление знаний —
Отладка 154
Профайлер —
Выполнение внешних программ —
’’Распечатывание” приложений 155
Класс Application —
Добавление примитивов в Актор 156
Измерение времени 158
Работа с датами и временем 159
346
Оглавление
Что такое время? 164
Цифровые часы —
Описания классов для работы со временем 166
Дополнительный пример программ 169
Глава 6. Объектноориентированный пользовательский интерфейс для
Windows 172
Создание окон —
Текстовые окна 175
EditWindows 177
FileEditor 178
Описания класса —
Класс Actorapp 180
Клавиши, диалоги и другие элементы управления 182
Создание кнопок —
Создание файловых диалогов 184
Рабочие области 185
Класс TextfiIe —
Создание динамических меню 186
Создание динамических диалогов 187
Внережимные диалоги 188
Обычный динамический диалог 189
Создание списковых окон —
Создание комбинированных окошек 190
Создание полосок меню главного окна 191
Простой контроллер исполнения 193
Некоторые извлечения из примера ресурсного файла 196
Директории Dos 200
Глава 7. Объектноориентированное графическое программирование для
Windows 217
Встроенные графические классы —
Класс Point 218
Scribble (Каракули) —
Описание класса Scribble 220
Прямоугольники —
Прямоугольники с закругленными краями 222
Эллипсы —
Многоугольники —
Цветные битовые карты 226
Трехмерная точка 227
Диаграммы 228
Анимация 231
Некоторые итоги 232
Описания графических классов 233
Глава 8. Программирование при помощи ObjectGraphics 241
Обзор ObjectGraphics 242
Демонстрационный пример SampleDraw —
Добавление меню и новых форм в SampleDraw 243
ObjectDraw 245
Расширенный Актор 248
Фильтры платформ 249
Класс Color 250
Битовые карты —
Прямоугольники —
Формы 251
Графические пространства 252
Пиктограммы ^ 253
Полиформы и ломаные линии 254
Кривые —
Рисование треугольников 255
Рисунки (Pictures) 258
Оглавление
347
Области (Region) 259
Палитры 260
Группировки —
Лрименение инструментов формирования изображения 262
Полимарки 266
Иерархия класса ObjectGraphics 268
Расширения класса Window в ObjectGraphics 270
Глава 9. Пример приложения в Windows 3.0 273
Краткий обзор Executive Control —
Мультидокументный интерфейс (MDI) 275
MDI-поддержка в Акторе —
MDIFrameWindow 276
MDIFileWindow 277
Иерархия классов MDI 278
Управление большой и сложной системой меню 280
Иерархические меню —
Комбинирование статических и динамических меню 281
Динамические файловые диалоги 282
Факторизация командных методов —
Глава 10. Вопросы проектирования в объектно-ориентированной среде
Windows 305
Дизайн для пользователей —
Что же реально делает компьютер? 307
Разделение процедур и протоколов —
Проектирование классов 308
Экземпляры дублирующихся приложений 309
Объектно-ориентированное проектирование графического
пользовательского интерфейса —
Пример проектирования объектно-ориентированной электронной
таблицы 310
Открытые цепочки действий 311
Факторизация командных методов 313
Объектно-ориентированное проектирование для MS-Windows —
Иерархические меню 315
Управление большой и сложной системой меню 316
CommonView —
Классы для изображений в C+4- 317
Классы CommonView 318
Проектирование управления памятью в Windows 320
Типы хранения данных 321
Сбрасываемая память 322
Управление памятью повышенной эффективности —
Стандартный режим —
Усовершенствованный режим 386-го процессора 323
Несколько правил ’’хорошего тона” —
оиблиотека WINMEM32.DLL —
Управление памятью в Акторе 324
Своппинг статической памяти 326
Классы памяти Актора 327
Описания классов для работы с памятью —
Проектирование индексно-последовательной базы данных 332
Wintrieve — краткий обзор —
Объектно-ориентированные системы реляционных баз данных . . . . 334
Разработка объектно-ориентированных баз данных при помощи
Wintrieve —
Классы Wintrieve 335
Базисные классы, используемые в Wintrieve 337
Описания класса Wintrieve 338
Описания класса Sample —
Указатель 341
Научное издание
Эрнест Р. Телло
Объектно-ориентированное программирование в среде Windows
Рецензенты: И. С. Кондратьев, В. А. Семичев
Ответственный за выпуск: К. В. Коробов
Редактор: JI. В. Речицкая
Технический редактор: А. А. Кондратьева
Дизайн переплета, рисунки: И. С. Кондратьев
Оригинал-макет книги подготовлен
в системе Ventura Publisher 2.0 А. А Кондратьевой
Подписано в печать 09.03.93 г. Формат 60x88/16.
Бумага офс. № 2. Гарнитура школьная.
Печать офсетная. Усл. печ. л. 21,56. Усл. кр.-отт. 21,56.
Уч.-изд. л. 19,26. Тираж 20 000 экз. Заказ № 857.
Издательство «Высшая школа» 101430, Москва, Неглинная, 29/14
Издательство Наука-Уайли 117864, Москва, ул. Профсоюзная, 90
Акционерное общество AKME 101000, Москва, ул. Чернышевского, 7
Набрано на персональных компьютерах фирмы AKME
Отпечатано Московской типографией № 8 Министерства печати и информации РФ
101898, Москва, Центр, Хохловский пер., 7
Акционерное общество AKME
предлагает полный спектр издательских услуг:
- разработку фирменного стиля, логотипов эмблем, товарных зна¬
ков, макетов рекламы, буклетов, бланочной продукции;
- компьютерный набор текстов, верстку и изготовление оригинал-
макетов изданий любой сложности, в том числе c иллюстрациями и
компьютерной графикой;
- изготовление макетов акций, сертификатов акций, депозитных и
сберегательных сертификатов, облигаций, векселей, других ценных
бумаг c использованием графики высокой сложности;
- полиграфические услуги;
- изготовление рекламной видеопродукции средствами компьютер¬
ной графики;
- консультации, подготовку договоров и ведение переговоров о при-
обретении авторских прав на зарубежные издания,
а также представительство ваших интересов при уступке авторских
прав на издания и художественное оформление к ним;
- содействие в организации компьютерных издательских систем,
широкий выбор профессиональных шрифтов и шрифтовых спецэф¬
фектов.
101000 Москва, ул. Чернышевского, 7
Телефоны: (095)162-9751
(095)287-2685
Тел./факс: (095)286-3463
E-maii (RELCOM/INTERNET): koko@akme msk.su
demos
demos + Aps сом - дилер нр
Официальный дилер Hewlett Packard предлагает компьютеры,
лазерные принтеры, сканеры, плоттеры и другое оборудование
фирмы со скидкой!
0 486DX-33/4-32Mb RAM/100-660Mb HDD/SVGA...
0 NoteBook-386SX-25/2-5Mb/40-80Mb/VGA/2,3 кг.
0 Портативный струйный принтер BJ-10 EX, вес - 1,8 кг,
лазерное качество, русские шрифты, аккумулятор.
0 Телефонная станция для офиса 6/16, подключение телефон¬
ных аппаратов любых типов.
0 Модем Discovery 2400CM/D MNP-5, 2400 bps, аттестат Мин¬
связи РФ. Гарантия - 2 года.
0 Модемы и факс-модемы фирмы Datatronics.
0 Высокоскоростные модемы: Telebit Т-3500 70000 bps/V32b/
V42b/Turbo PEP; ZyXel U1496S/E до 57600 bps/V23/V33/
V32b/V42b/V27ter/V29/Vl7/G3 Fax...
demos
113035 Москва, Овчинниковская наб., 6
Телефоны: (095) 231-6395, 233-0670
Fax:
E-mail:
(095) 233-5016
info@hq.demos.su
119136
г.Москва, а/я 20
тел.: 142-77-12
Акционерное общество АЙНА предлагает:
- руководителям большого и малого бизнеса.желающим всегда иметь под рукой оперативную аналитичес-
куюинформациюофинансахпредприятияигибкуюсистемусоставлениябухгалтерскойотчетности; пред-
приятиямвсехформсобственности.задачиучетанакоторыхдостаточнотрудоемки; главнымбухгалтерам,
желающим иметь простую в эксплуатации, не требующую специальных знаний в области компьютеров,
надежную в отношении защиты данных и “прозрачную” при аналитической работе Информационно-анали-
тическую систему (ИАС) CORPAC - Corporate Accounting.
Специалисты А/0 “Айна" имеют большой практический опыт в области автоматизации бухгалтерских
задач, прекрасно владеют не только языками программирования и базами данных, но и технологией обра¬
ботки бухгалтерских документов.
В состав системы CORPAC входятследующие модули:
- SALARY*-pac4eT заработной платы;
- ACCOUNTS - бухгалтерский учет:
- учет кассовых и банковских операций;
- учет движения по счетам (оперативная информация о состоянии составления счетов бухгалтерского
баланса);
- учет валютных поступлений (с оформлением необходимых документов);
- учет основных средств;
- учет малоценных и быстроизнашивающихся материалов;
- учет материальных ценностей на складе;
- автоматизированная выписка и контроль исполнения платежных поручений;
- составление сводного баланса.
Модули системы CORPAC поставляются в любой необходимой комплектации с гибкой настройкой на специ¬
фику решаемых задач по ценам, дифференцированным в зависимости от комплектации.
Стоимость системы CORPAC в полтора-два раза ниже аналогичных продуктов других фирм и приемлема
даже для предприятий малого бизнеса.
Специалисты А/О “Айна” готовы не только поставить указанные комплексы программ, но и квалифициро¬
ванно обучить Ваш персонал работе с персональным компьютером и прикладными программами.
АЮ “Айна” имеет значительный практический опытв деле внедрения автоматизацииучета на предпри¬
ятиях сферы услуг, в частности - в задачах автоматизации учета разветвленных гостиничных комплексов.
В случае отсутствия у Вас вычислительной техники, мы готовы поставить Вам необходимый комплекс
технических средств и выполнить работы по созданиюлокальной сети “под ключ”. А/О “Айна” готово также
предложить сервисное и техническое обслуживание всего комплекса технических средств.
Если Вас заинтересовали наши предложения, будем рады сотрудничать с Вами.
Контактные телефоны: (095)427-3911 с 9-00 до 18-00
(095)213-8588 после19-00
Факс: (095)339-5911 (для А/0 “Айна”)
Ранняяверсия системы SALARYтестировалась в софтлабораториижурнала *КОМПЬЮТЕР”и описача
sN2(5) за 1991 г. на с. 47-48.