- •Инструментальные средства разработки по
- •Лекция 1
- •Краткий исторический обзор развития инструментальные средства разработки по
- •Лекция 2
- •2. Гецци к., Джазайери м., Мандриоли д. Основы инженерии программного обеспечения.
- •1. Начало (Inception)
- •2. Уточнение (Elaboration)
- •3. Конструирование(Construction)
- •4. Внедрение (Transition)
- •Подход rad
- •Лекция 3 тема: Этап логического проектирования ис. Основные подходы при создании концептуальной модели.
- •2. Гецци к., Джазайери м., Мандриоли д. Основы инженерии программного обеспечения.
- •Методы моделирования бизнес процессов
- •Структурный подход
- •Объектно-ориентированный подход
- •Лекция 4 тема: Описание функциональности разработки. Методы и инструменты.
- •2. Гецци к., Джазайери м., Мандриоли д. Основы инженерии программного обеспечения.
- •Функциональная методика idef0
- •1. Внешние сущности
- •2. Системы и подсистемы
- •3. Процессы
- •4. Накопители данных
- •5. Потоки данных
- •6. Построение иерархии диаграмм потоков данных
- •Лекция 5
- •Основные понятия er-диаграмм
- •Более сложные элементы er-модели
- •Разработка er - моделей.
- •Концептуальные и физические er-диаграммы.
- •Лекция 9.
- •Программная среда разработки пользовательской программы
- •Системы визуальной разработки приложений
- •Выбор среды разработки
- •Обзор сред разработки
- •Операционная среда Eclipse
- •Виртуальные машины
- •Процессные и системные виртуальные машины
- •Инструменты для создания и работы с виртуальными машинами
- •Лекция 10
- •Интерфейс программирования приложений (api)
- •Уровни абстракции
- •Лекция 11
- •Основные этапы физического проектирования программного продукта
- •Библиотеки
- •Тип связывания или тип компоновки
- •Загрузка
- •Трансляция, компиляция и интерпретация
- •Макроязыки и макрогенерация
- •Препроцессор
- •Функции основных инструментов интегрированной среды разработки
- •Компиляция и выполнение проекта Delphi
- •Лекция 12 тема: Управление версиями пп.
- •Работа с Subversion Возможности
- •Модель работы
- •Типы репозиториев
- •Файловая система
- •Номера ревизий
- •Операции над файловой системой
- •Фиксация изменений Рабочая копия
- •Структура хранилища Создание хранилища
- •Структура проекта в хранилище
- •Стволовая ветвь
- •Релизная ветвь
- •Функциональная ветвь
- •Ветвление
- •Простейший рабочий цикл
- •Обновление рабочей копии
- •Внесение изменений в рабочую копию
- •Анализ изменений
- •Разрешение конфликтов (при слиянии с чужими изменениями)
- •Слияние конфликтов вручную
- •Копирование файла поверх вашего рабочего файла
- •Использование svn revert
- •Фиксация изменений
- •Лекция 13 тема: Open тooLs api.
- •Понятие эксперта
- •Стили экспертов Delphi
- •"Старый стиль" и "новый стиль" ToolsApi
- •Модули интерфейса api Open Tools
- •Типы экспертов
- •Пример создания эксперта
- •Некоторые полезные эксперты
- •Лекция 14 тема: Использование и создание dll.
- •Разработка библиотеки Структура библиотеки
- •Экспорт подпрограмм
- •Соглашения о вызове подпрограмм
- •Использование библиотеки в программе
- •Статический импорт
- •Динамический импорт
- •Глобальные переменные и константы
- •Инициализация и завершение работы библиотеки
- •Исключительные ситуации и ошибки выполнения подпрограмм
- •Использование стандартных системных переменных IsLibrary и CmdLine
- •Лекция 15 тема: Разработка собственных компонентов.
- •Основные шаги при создании нового компонента:
- •Выбор предка компонента
- •Создание заготовки для нового компонента
- •Создание свойств, событий и методов компонента Соглашения по наименованиям
- •Создание свойств компонента
- •Создание перечисляемых свойств компонента
- •Создание свойств-множеств в компоненте
- •Создание свойства-объекта в компоненте
- •Создание свойства-массива в компоненте
- •Команды Default и NoDefault
- •Переопределение значений свойств компонента-предка
- •Создание событий компонента
- •Пример создания нового события компонента
- •Создание методов компонента
- •Регистрация компонента в среде Delphi
- •Создание пиктограммы для компонента
- •Переустановка компонента
- •Использование созданного компонента
- •Лекция 16 тема: Инструментальные средства и методы расширения функциональности среды разработки.
- •Редакторы свойств
- •Стандартные Редакторы Свойств
- •Создание Редактора Свойств
- •Регистрация Редактора Свойств
- •Установка Редактора свойств
- •Редактор Компонент
- •Пример Редактора Компонент
- •Лекция 17
- •Работа с отладчиком в Delphi
- •Настройка ide для отладки
- •Включение в код отладочной информации
- •Трассировка программы
- •Точки останова программы
- •Наблюдение значений переменных
- •Отладка dll
- •Окно cpu (дизассемблер)
- •Окно состояния подзадач.
- •Окно Modules
- •Окно Call Stack
- •Трассировка исходного кода vcl
интегрированные среды – включают в себя большую часть выше перечисленных средств и обеспечивают их взаимосвязь.
Представители: Borland Delphi, Borland C++ Builder, Kylix (Borland Software Corporation), Power Builder(SY Base), Designer, Developer(Oracle), Visual Bаsic, Visual C++, Microsoft Visual Studio (.Net) (Microsoft Corp.), NuMega Driver Studio (NuMega), Eclipse (IBM).
Отдельно стоит отметить такие инструменты как методологии разработки ПО (например, RUP) и языки моделирования (например, ER-диаграммы, IDEF, DFD, UML), которые применяются в различных CASE-средствах, но могут использоваться и без них.
В каждом классе существуют огромное число продуктов, каждый со своими особенностями, достоинствами и недостатками.
Дадим краткую характеристику названным классам программ и приведем некоторые критерии оценки, по которым можно сравнивать программы из одного класса.
Но сначала укажем на характеристики, универсальные для всех программ:
фирма-производитель, автор (зачастую имя производителя значит больше, чем все остальное).
название продукта;
номер последней версии;
класс продукта, который установил для него производитель (например, HackersViewer, который включает в себя неплохой дизассемблер и редактор PE-файлов, поставляется просто как hex-редактор);
тип дистрибьюции программы (с открытыми кодами/бесплатная (freeware)/условно-бесплатная (shareware)/платная) и стоимость;
наличие и тип поддержки, ее стоимость;
доступность и качество документации;
простота и понятность интерфейса;
наличие пробных версий (для платных программ);
сайт программы и возможность ее скачки;
размер дистрибутива и его состав;
дополнительные (не основные) возможности, предоставляемые программой;
Теперь рассмотрим отдельно основные классы инструментов.
Компиляторы (ассемблеры) и редакторы связей.
Эти два класса программ следует объединить, т.к. в поставку любого современного компилятора входит и редактор связей.
Компилятор (ассемблер) формирует объектный код, переводя программу с языка программирования (языка ассемблера), а редактор формирует исполнимый файл, собирая объектные и библиотечные файлы и редактируя перекрестные ссылки.
Для компиляторов можно указать следующие характеристики:
язык, с которого производится компиляция;
диалект/стандарт языка;
аппаратные платформы и ОС, для которых может формироваться объектный и исполнимый файл;
наличие возможности и качество оптимизации кода;
форматы поддерживаемых объектных, библиотечных и исполнимых файлов;
Представители: C/C++: Intel C++ Compiler, Borland C++ Compiler, Watcom C++, GNU C.
Pascal: Free Pascal, GNU Pascal.
Редакторы текстов.
Предназначены для ввода и корректировки текстов программ. Могут быть как общими, так и предназначенными для поддержки конкретных языков или среды.
Характеристики:
формат и кодировка обрабатываемых файлов;
возможность выделения лексем в тексте;
возможность поддержки оформления текста в соответствии с парадигмами языка;
возможность вызывать процесс компиляции прямо из редактора;
возможность генерации части текста программы (чаще бывает не у редакторов, а у сред).
Отладчики.
Предназначены для поиска ошибок в других программах, ядрах операционных систем, SQL-запросах и других видах кода. Отладчик позволяет выполнять пошаговую трассировку, отслеживать, устанавливать или изменять значения переменных в процессе выполнения кода, устанавливать и удалять контрольные точки или условия остановки, отслеживать изменение состояния процессора во время работы программы и т.д.
Различают два основных типа отладчиков:
отладчики пользовательского режима;
отладчики режима ядра.
Первые могут лишь следить за работой программ пользовательского режима и не способны ни отслеживать системные вызовы, ни следить за работой ядра. Кроме того, для использования таких отладчиков программа должна быть соответствующим образом подготовлена (скомпилирована).
Отладчики же режима ядра, напротив, позволяют полностью контролировать работу системы, а, следовательно, и всех программ.
Характеристики:
тип (режима ядра/пользовательский);
поддержка символьной отладки (способность читать исходные коды программы и работать с ними). Набор поддерживаемых языков (сред/диалектов);
набор отображаемой информации: регистры процессора, стек, память (режимы отображения содержимого памяти);
поддерживаемые режимы отладки: пошаговый, с точками останова, с реакцией на события в системе;
состав отслеживаемых событий в системе: аппаратные прерывания, обращения к драйверу (другому модулю ядра), вызов функции и т.д.
(обычно для отладчиков режима ядра) требования к аппаратной поддержке, возможность работы на «живой» системе;
возможность анализа файлов дампа.
Представители:
Отладчики пользовательского режима: Turbo Debugger (Borland Software Corporation), Cool Debugger (Wei Bao), W32Dasm, AQtime, FlexTracer, GNU Debugger.
Отладчики режима ядра: i386kd/alphakd/ia64kd и WinDbg (Microsoft Corporation) (для работы в “живую” требуют 2 машины. Для обхода этого ограничения существует надстройка LiveKd (Mark E. Russinovich)), SoftIce (NuMega).
Программы создания инсталляторов
Предназначены для создания дистрибутивов программ и пакетов программ.
Задачи, выполняемые подобными программами для различных платформ, могут сильно различаться. Мало того, с выходом Windows Installer и опубликования его API для платформы Win32 началось разделение программ на поддерживающие Windows Installer и использующие свои средств.
Как правило, все дистрибутивы имеют интерфейс программ-мастеров (т.е. пошаговое уточнение настроек). Кроме того, почти всегда имеется возможность удаления установленной программы.
Характеристики:
ориентированны на использование Windows Installer или используют свои средства;
возможность автоматического отслеживания зависимостей исполнимых файлов и разделяемых библиотек;
наличие встроенного языка сценариев;
возможность и пределы, в которых можно изменять поведения мастера инсталляции;
возможность использования и поддержка национальных языков;
функции, поддерживаемые в процессе установки (кроме копирования файлов):
создание ключей реестра;
регистрация COM-объектов;
перезагрузка системы после или в процессе установки;
возможность удаления установленной программы;
возможность контроля версий устанавливаемой программы (перезапись, если необходимо) и разделяемых библиотек;
возможность и степень сжатия дистрибутива;
возможность создания дистрибутива, состоящего из одного, или заданного количества файлов;
Представители:
InstallShield (Install Shield Corp.), Wise InstallMaster Setup (Wise Solutions), Factory (Indigo Rose Corp.), Ghost Installer Studio; GkSetup (Gero Kuehn), Nullsoft Install System (Nullsoft), GP-Install (Quality Software Components), Little Setup Builder (http://www.ammasw.eboard.com), Inno Setup (http://www.gentee.com), Setup Generator (http://www.jrsoftware.org), Ghost Installer (http://www.ginstall.com).
Редакторы ресурсов
Создают и обрабатывают файлы ресурсов, которые после обработки могут быть скомпилированы и включены в исполнимый модуль. Эти программы специфичны для платформы Win.
Характеристики:
состав поддерживаемых ресурсов;
возможность работы с нестандартными ресурсами;
возможности импорта и экспорта ресурсов.
Представители:
Borland Resource Workshop, eXe Scope, Resource Builder (SiComponents), Resource Explorer (http://batry.hypermart.net).
Профилировщики
Применяются для множества различных задач по оптимизации производительности приложения и потребления им памяти. Производят многосторонний анализ производительности приложения с точки зрения затраченного времени на строчку кода при выполнении функции. Выявляют проблемы производительности и утечки памяти, анализирует уровень потребления ресурсов, вызовы функций и их порядок. Также осуществляют мониторинг покрытия кода и т.д.
Характеристики:
поддерживаемые платформы;
возможности кросс-профилировки (эмуляции системы);
вид выдаваемых данных (графики, гистограммы, таблицы).
Представители: Intel VTune (Intel), AMD Code Analyst (AMD), AQtime.
Программы поддержки версий
Дают возможность централизованно отслеживать изменения в проекте, создавать независимые новые версии программных модулей, автоматически разносить изменения по копиям проекта программной системы и т.д.
Представители: Visual Source Safe (Microsoft Cor.), Nexus Safe Source; Subversion.
Программы создания файлов помощи (документации).
Позволяют создавать файлы помощи, автоматизировать документирование.
Характеристики:
форматы поддерживаемых выходных файлов (hlp, chm, html, pdf, …);
средства, необходимые для работы с файлами документации;
возможность конвертирования из других распространенных форматов;
возможность структурирования информации в файле помощи;
возможность организации поиска по документации;
возможность интеграции в существующие среды (например, для библиотеки COM – объектов возможность вызывать справку по ней при нажатии F1, если курсор стоит на объекте из этой библиотеки);
возможность автоматической генерации помощи (или заготовки) по описанию библиотеки, COM-объекта.
Представители: RoboHelp (), Anet Help Tool, Help and Manual, Visual CHM, Adobe Acrobat (Adobe); HTML HelpWorkshop.
Генератор документации.
Программа или пакет программ, позволяющая получать документацию, предназначенную для программистов (документация на API) или для конечных пользователей системы, по особым образом комментированному исходному коду и, в некоторых случаях, по исполняемым модулям (полученным на выходе компилятора).
Обычно генератор анализирует исходный код программы, выделяя синтаксические конструкции, соответствующие значимым объектам программы (типам, классам и их членам/свойствам/методам, процедурам/функциям и т. п.). В ходе анализа также используется мета-информация об объектах программы, представленная в виде документирующих комментариев. На основе всей собранной информации формируется готовая документация, как правило, в одном из общепринятых форматов - HTML, HTMLHelp, PDF, RTF и других.
Представители: Document! X, Doxygen, PasDoc, ROBODoc, XHelpGen.
Дизассемблеры и декомпиляторы.
Предназначены для получения исходного кода на языке программирования из исполняемого модуля.
Характеристики:
поддерживаемые языки (компиляторы);
возможность использования символьной информации о файле (отладочной и др.);
возможность интерактивной работы с листингом (замены имен переменных и функций, отслеживания вызовов, модификация кода).
Представители: Interactive DisAssembler (Data Resource), Sourcer, Decafe Pro, DeDe.
Программы отслеживания активности системы и изменений, происходящих в системе.
Позволяют отслеживать действия программ по изменению реестра, файловой системы, вызовов системных сервисов и т.д. Следят за загруженностью системы в целом.
Характеристики:
тип отслеживаемых изменений/активности;
возможность протоколирования (логирования);
возможность фильтрации получаемой информации;
возможность уведомления.
Представители:
Microsoft: Spy++, Process Viewer, ApiMon, SysMon (для Win2000/XP – ActiveX компонент для mmc).
Winternals Systems (Mark E. Russinovich): RegMon, FileMon, HandleEx.
Программы-вериферы и контейнеры.
Создают виртуальную среду для отдельных классов программ, в которой можно исследовать поведение программы).
Представители: Driver Verifier, ActiveX Control Test Container (Microsoft Corp.)
Программы для защиты разрабатываемого программного обеспечения (протекторы).
Позволяют создавать систему защиты ПО от несанкционированного копирования исполняемых файлов, непрофессионального реверс-инжиниринга, а также создавать регистрационные ключи, оценочные или демо-версии приложений (к примеру, программу, работающую 30 дней), шифровать и сжимать данные и т.п.
Представители: ASProtect, Obsidium, Armadillo, VMProtect, ORiEN
SDK.
SDK (от англ. software development kit) - комплект средств разработки, который позволяет специалистам по программному обеспечению создавать приложения для определённого пакета программ, программного обеспечения базовых средств разработки, аппаратной платформы, компьютерной системы, игровых консолей, операционных систем и прочих платформ.
Программист, как правило, получает SDK непосредственно от разработчика целевой технологии или системы. Часто SDK распространяется через Интернет. Многие SDK распространяются бесплатно для того, чтобы побудить разработчиков использовать данную технологию или платформу.
Поставщики SDK иногда подменяют слово «software» в словосочетании «software development kit» на более точное слово. Например, Microsoft и Apple предоставляют Driver Development Kit (DDK) для разработки драйверов устройств, PalmSource называет свой инструментарий для разработки PalmOS Development Kit (PDK), а Oracle - Java Development Kit (JDK).
Примеры SDK: Windows Phone SDK, Adobe Flex, DirectX, Eclipse, iPhone SDK, Java Development Kit, Opera Devices SDK, Source SDK.
Парсеры.
В информатике, синтакси́ческий ана́лиз (па́рсинг) - это процесс сопоставления линейной последовательности лексем (слов, токенов) языка с его формальной грамматикой. Результатом обычно является дерево разбора (синтаксическое дерево). Обычно применяется совместно с лексическим анализом. Синтаксический анализатор (парсер) — это программа или часть программы, выполняющая синтаксический анализ.
Технологические стандарты.
OLE (Object Linking and Embedding) - позволяет создавать приложения, включающие в свой состав объекты, полученные из других приложений.
ODBC (Open DataBase Connectivity) - обеспечивает единый интерфейс доступа к различным базам данных на различных платформах.
MAPI (Messaging Application Program Interface) - обеспечивает независимость приложений от систем связи в режиме телекоммуникаций, работает по принципу драйвера.
Краткий исторический обзор развития инструментальные средства разработки по
Этап 1: до середины 50-х.
Основные затраты связаны с кодированием (в машинных кодах). Появляются автокоды (языки с использованием мнемонических обозначений команд) и трансляторы с них (ассемблеры).
Реализуются возможности раздельной компиляции и перемещаемости программ. Появляются загрузчики и компоновщики программ.
Этап 2: середина 50-х – середина 60-х гг.
Увеличиваются размеры программ, выявляется разрыв между понятиями проблемных областей и машинно-ориентированных языков. Появляются различные языки высокого уровня (алгоритмические, универсальные):
Fortran (1954-1957);
Algol-60 (1958-1960);
Cobol (1959-1961);
Lisp (1959);
Basic (1964);
PL/1 (1964).
и трансляторы с них (компиляторы). Изобретаются и опробуются почти все основные типы данных, операции над ними, управляющие структуры и способы изображения их в программах, различные варианты параметризации подпрограмм.
Этап 3: середина 60-х – начало 70-х гг.
Резко увеличиваются размеры ПО, происходит переход к коллективному характеру работ. Повышаются требования к ПО вследствие перехода к товарному производству.
Изменяется соотношение затрат на разработку ПО (40% и более тратится на отладку, проектирование и документирование), кодирование – один из самых простых видов работ. Используются и создаются "большие" языки программирования – ПЛ/1, АЛГОЛ-68, СИМУЛА-67, обобщающие и интегрирующие ранее найденные решения.
Появляются развитые системы программирования с оптимизирующими и отладочными трансляторами, макробиблиотеками, библиотеками стандартных программ, специализированных текстовыми редакторами, средствами анализа и диалоговой отладки в терминах входного языка. Разрабатываются развитые операционные системы, первые СУБД, многочисленные системы автоматизации документирования, системы управления программной конфигурацией (отслеживания модификаций и сборки версий ПО).
Этап 4 (“этап кризиса в развитии ПО”): начало 70-х–середина 70-х гг.
Несмотря на развитие инструментальных средств, производительность труда программистов не растёт. Более того, вследствие повышения требований к ПО и нелинейного роста его сложности, производительность труда падает. Срываются сроки разработки ПО, растёт его стоимость, непредсказуемо его качество, не срабатывают традиционные методы (предоставление дополнительных человеческих и материальных ресурсов), что характеризуется как "кризис ПО".
Получают признание методологии структурного программирования (Дейкстра, 1968г.), формируются основы технологии программирования (язык Паскаль (Н.Вирт), 1971г.).
Этап 5:1976г.– наше время. Этап посткризисного развития инструментальных средств.
1976г. – публикация работы Боэма, где вводится понятие жизненного цикла ПО и указывается, что основные затраты приходятся не на разработку, а на сопровождение программ.
Языки программирования:
C (начало 1970-х, впервые достаточно полно описан в 1978 г.);
Modula-2 (1978 г., развитие – язык Oberon (1988));
Ada (1980);
Prolog (1972 г., распространение получил с 1980 г.);
Smalltalk (1970-е годы, в 1980 был представлен как Smalltalk-80);
C++ (начало 1980-х гг., название – 1983, в привычном сегодня виде существует с 1990 г.);
Java (версия Java 1.0 – 1996 г., Java 2.0 – 1998, Java 5 – 2004...);
C# (1998–2001, версия 1.0 – 2000–2002, версия 2.0 – 2003-2005, версия 3.0 – 2004–2008, версия 4.0 – 2008–2010).
Развиваются интегрированные инструментальные среды разработки программ. Получает признание объектно-ориентированный подход к проектированию и программированию. Разрабатываются программы, поддерживающие создание ПО на каждом этапе.
Лекция 2
ТЕМА: Методологии разработки ПО. RUP.
Литература: 1. Зелковиц М., Шоу А., Гэннон Дж. Принципы разработки программного обеспечения.
2. Гецци к., Джазайери м., Мандриоли д. Основы инженерии программного обеспечения.
3. Камаев В. А., Костерин В. В. Технологии программирования.
Рассмотрим понятия методологии, метода и средства.
Определение 1: Метод (от греч. methodos - способ исследования или познания, теория или учение) - прием или система приемов практического осуществления чего-нибудь в какой-либо предметной области, совокупность приемов или операций практического или теоретического освоения действительности, подчиненных решению конкретных задач.
Метод включает средства - с помощью чего осуществляется действие и способы - каким образом осуществляется действие.
Определение 2: Методология - это система принципов, а также совокупность идей, понятий, методов, способов и средств, определяющих стиль разработки программного обеспечения.
Методология - это реализация стандарта. Сами стандарты лишь говорят о том, что должно быть, оставляя свободу выбора и адаптации.
Конкретные вещи реализуются через выбранную методологию. Именно она определяет, как будет выполняться разработка. Существует много успешных методологий создания программного обеспечения. Выбор конкретной методологии зависит от размера команды, от специфики и сложности проекта, от стабильности и зрелости процессов в компании и от личных качеств сотрудников.
Методологии представляют собой ядро теории управления разработкой программного обеспечения.
В зависимости от используемой модели жизненного цикла методологии делятся на:
водопадные (каскадные);
итерационные (спиральные).
Также существует и более общая классификация на:
прогнозируемые;
адаптивные.
Прогнозируемые методологии фокусируются на детальном планировании будущего. Известны запланированные задачи и ресурсы на весь срок проекта. Команда с трудом реагирует на возможные изменения. План оптимизирован исходя из состава работ и существующих требований. Изменение требований может привести к существенному изменению плана, а также дизайна проекта. Часто создается специальный комитет по «управлению изменениями», чтобы в проекте учитывались только самые важные требования.
Адаптивные методологии нацелены на преодоление ожидаемой неполноты требований и их постоянного изменения. Когда меняются требования, команда разработчиков тоже меняется. Команда, участвующая в адаптивной разработке, с трудом может предсказать будущее проекта. Существует точный план лишь на ближайшее время. Более удаленные во времени планы существуют лишь как декларации о целях проекта, ожидаемых затратах и результатах.
Каскадная разработка или модель водопада (англ. waterfall model) - модель процесса разработки программного обеспечения, в которой процесс разработки выглядит как поток, последовательно проходящий фазы анализа требований, проектирования, реализации, тестирования, интеграции и поддержки.
Принципиальной особенностью каскадного подхода является: переход на следующую стадию осуществляется только после того, как будет полностью завершена работа на текущей стадии, и возвратов на пройденные стадии не предусматривается. Каждая стадия заканчивается получением некоторых результатов, которые служат в качестве исходных данных для следующей стадии (рис. 1).
Рис. 1. Каскадная модель жизненного цикла.
Каждая стадия завершается выпуском комплекта документации, достаточной для того, чтобы разработка могла быть продолжена другой командой разработчиков. Критерием качества разработки при таком подходе является точность выполнения спецификаций технического задания.
Преимущества применения каскадного способа:
на каждой стадии формируется законченный набор проектной документации, отвечающий требованиям полноты и согласованности;
выполняемые в логической последовательности стадии работ позволяют планировать сроки завершения всех работ и соответствующие затраты.
Каскадный подход хорошо зарекомендовал себя при построении электронных информационных систем, для которых в самом начале разработки можно достаточно точно и полно сформулировать все требования, с тем, чтобы предоставить разработчикам свободу реализовать их технически как можно лучше.
В то же время этот подход обладает рядом недостатков, вызванных, прежде всего тем, что реальный процесс создания программного обеспечения никогда полностью не укладывается в такую жесткую схему. Процесс создания ПО носит, как правило, итерационный характер: результаты очередной стадии часто вызывают изменения в проектных решениях, выработанных на предыдущих стадиях. Таким образом, постоянно возникает потребность в возврате к предыдущим стадиям и уточнении или пересмотре ранее принятых решений (рис. 2). Изображенную схему можно отнести к отдельной модели - модели с промежуточным контролем, в которой межстадийные корректировки обеспечивают большую надежность по сравнению с каскадной моделью, хотя увеличивают весь период разработки.
Основным недостатком каскадной модели является существенное запаздывание с получением результатов и, как следствие, высокий риск создания системы, не удовлетворяющей изменившимся потребностям пользователей. Это объяснятся двумя причинами:
пользователи не в состоянии сразу изложить все свои требования и не могут предвидеть, как они изменятся в ходе разработки;
за время разработки могут произойти изменения во внешней среде, которые повлияют на требования к системе.
Рис. 2. Каскадная модель ЖЦ на практике.
В рамках каскадного подхода требования к разрабатываемому продукту фиксируются в виде технического задания на все время его создания, а согласование получаемых результатов с пользователями производится только в точках, планируемых после завершения каждой стадии (при этом возможна корректировка результатов по замечаниям пользователей, если они не затрагивают требования, изложенные в техническом задании). Таким образом, пользователи могут внести существенные замечания только после того, как работа над системой будет полностью завершена. Пользователи могут получить систему, не удовлетворяющую их потребностям. В результате приходится начинать новый проект, который может постигнуть та же участь.
Для преодоления перечисленных проблем в середине 80-х годов была предложена спиральная модель жизненного цикла (рис.3).
Рис. 3. Спиральная (итерационная) модель ЖЦ.
Ее принципиальной особенностью является следующее: прикладное ПО создается не сразу, как в случае каскадного подхода, а по частям с использованием метода прототипирования.
Под прототипом понимается действующий программный компонент, реализующий отдельные функции и внешние интерфейсы разрабатываемого ПО. Создание прототипов осуществляется в несколько итераций, или витков спирали. Каждая итерация соответствует созданию фрагмента или версии ПО, на ней уточняются цели и характеристики проекта, оценивается качество полученных результатов и планируются работы следующей итерации. На каждой итерации производится тщательная оценка риска превышения сроков и стоимости проекта, чтобы определить необходимость выполнения еще одной итерации, степень полноты и точности понимания требований к системе, а также целесообразность прекращения проекта.
Спиральная модель избавляет пользователей и разработчиков от необходимости точного и полного формулирования требований к системе на начальной стадии, поскольку они уточняются на каждой итерации. Таким образом, углубляются и последовательно конкретизируются детали проекта, и в результате выбирается обоснованный вариант, который доводится до реализации.
Спиральная модель - классический пример применения эволюционной стратегии конструирования. Спиральная модель (автор Барри Боэм, 1988) базируется на лучших свойствах классического жизненного цикла и макетирования, к которым добавляется новый элемент анализ риска, отсутствующий ранее.
Спиральная модель определяет четыре действия, представляемые отдельными секторами спирали:
1. Планирование - определение целей, вариантов и ограничений.
2. Анализ риска - анализ вариантов и распознавание/выбор риска.
3. Конструирование - разработка продукта следующего уровня.
4. Оценивание - оценка заказчиком текущих результатов конструирования.
Интегрирующий аспект спиральной модели очевиден при учете радиального измерения спирали. С каждой итерацией по спирали (продвижением от центра к периферии) строятся все более полные версии ПО.
В первом витке спирали определяются начальные цели, варианты и ограничения, распознается и анализируется риск. Если анализ риска показывает неопределенность требований, на помощь разработчику и заказчику приходит макетирование (используемое в квадранте проектирования). Для дальнейшего определения проблемных и уточненных требований может быть использовано моделирование. Заказчик оценивает инженерную (конструкторскую) работу и вносит предложения по модификации. Следующая фаза планирования и анализа риска базируется на предложениях заказчика. В каждом цикле по спирали результаты анализа риска формируются виде «продолжать, не продолжать». Если риск слишком велик, проект может быть остановлен.
В большинстве случаев движение по спирали продолжается, с каждым шагом продвигая разработчиков к более общей модели системы.
При итеративном способе недостающую часть работы можно выполнять на следующей итерации. Главная же задача - как можно быстрее показать пользователям системы работоспособный продукт, тем самым активизируя процесс уточнения и дополнения требований.
Спиральная модель не исключает каскадного подхода на завершающих стадиях проекта в тех случаях, когда требования к системе оказываются полностью определенными.
Основная проблема спирального цикла - определение момента перехода на следующую стадию. Для ее решения необходимо ввести временные ограничения на каждую из стадий ЖЦ. Переход осуществляется в соответствии с планом, даже если не вся запланированная работа закончена. План составляется на основе статистических данных, полученных на предыдущих проектах, и личного опыта разработчиков.
Достоинства спиральной модели:
наиболее реально (в виде эволюции) отображает разработку программного обеспечения;
позволяет явно учитывать риск на каждом витке эволюции разработки;
включает шаг системного подхода в итерационную структуру разработки;
использует моделирование для уменьшения риска и совершенствования программного изделия.
Недостатки спиральной модели:
новизна (отсутствует достаточная статистика эффективности модели);
повышенные требования к заказчику;
трудности контроля и управления временем разработки.
На сегодняшний день можно выделить следующие итеративные методологии разработки программного обеспечения:
Rational Unified Process (RUP)
Гибкие методологии разработки (SCRUM, KANBAN, DSDM, MSF, ALM, XP)
Гибкая методология разработки (англ. Agile software development).
Большинство гибких методологий нацелены на минимизацию рисков, путём сведения разработки к серии коротких циклов, называемых итерациями, которые обычно длятся одну-две недели. Каждая итерация сама по себе выглядит как программный проект в миниатюре, и включает все задачи, необходимые для выдачи мини-прироста по функциональности: планирование, анализ требований, проектирование, кодирование, тестирование и документирование. Хотя отдельная итерация, как правило, недостаточна для выпуска новой версии продукта, подразумевается, что гибкий программный проект готов к выпуску в конце каждой итерации. По окончании каждой итерации, команда выполняет переоценку приоритетов разработки.
Agile-методы делают упор на непосредственное общение лицом к лицу. Большинство agile-команд расположены в одном офисе. Как минимум она включает и «заказчиков» (заказчики которые определяют продукт, также это могут быть менеджеры продукта, бизнес-аналитики или клиенты). Офис может также включать тестировщиков, дизайнеров интерфейса, технических писателей и менеджеров.
Одной из наиболее известных и передовых гибких методик является методология SCRUM.
SCRUM - методология, предназначенная для небольших команд (до 10 человек). Весь проект делится на итерации (спринты) продолжительностью 30 дней каждый. Выбирается список функций системы, которые планируется реализовать в течение следующего спринта. Самые важные условия - неизменность выбранных функций во время выполнения одной итерации и строгое соблюдение сроков выпуска очередного релиза, даже если к его выпуску не удастся реализовать весь запланированный функционал. Руководитель разработки проводит ежедневные 20 минутные совещания, которые так и называют — scrum, результатом которых является определение функции системы, реализованных за предыдущий день, возникшие сложности и план на следующий день. Такие совещания позволяют постоянно отслеживать ход проекта, быстро выявлять возникшие проблемы и оперативно на них реагировать.
KANBAN – гибкая методология разработки программного обеспечения, ориентированная на задачи.
Основные правила:
визуализация разработки:
разделение работы на задачи;
использование отметок о положение задачи в разработке;
ограничение работ, выполняющихся одновременно, на каждом этапе разработки;
измерение времени цикла (среднее время на выполнение одной задачи) и оптимизация процесса.
Преимущества KANBAN:
уменьшение числа параллельно выполняемых задач значительно уменьшает время выполнения каждой отдельной задачи;
быстрое выявление проблемных задач;
вычисление времени на выполнение усредненной задачи.
DYNAMIC SYSTEM DEVELOPMENT METHOD (DSDM) появился в результате работы консорциума из 17 английских компаний. Целая организация занимается разработкой пособий по этой методологии, организацией учебных курсов, программ аккредитации и т.п. Кроме того, ценность DSDM имеет денежный эквивалент.
Все начинается с изучения осуществимости программы и области ее применения. В первом случае, вы пытаетесь понять, подходит ли DSDM для данного проекта. Изучать область применения программы предполагается на короткой серии семинаров, где программисты узнают о той сфере бизнеса, для которой им предстоит работать. Здесь же обсуждаются основные положения, касающиеся архитектуры будущей системы и план проекта.
Далее процесс делится на три взаимосвязанных цикла: цикл функциональной модели отвечает за создание аналитической документации и прототипов, цикл проектирования и конструирования — за приведение системы в рабочее состояние, и наконец, последний цикл — цикл реализации — обеспечивает развертывание программной системы.
Базовые принципы, на которых строится DSDM:
активное взаимодействие с пользователями;
частые выпуски версий;
самостоятельность разработчиков в принятии решений;
тестирование в течение всего цикла работ.
Как и большинство других гибких методологий, DSDM использует короткие итерации, продолжительностью от двух до шести недель каждая. Особый упор делается на высоком качестве работы и адаптируемости к изменениям в требованиях.
MICROSOFT SOLUTIONS FRAMEWORK (MSF) - методология разработки программного обеспечения, предложенная корпорацией Microsoft. MSF опирается на практический опыт Microsoft и описывает управление людьми и рабочими процессами в процессе разработки решения.
Базовые концепции и принципы модели процессов MSF:
единое видение проекта - все заинтересованные лица и просто участники проекта должны чётко представлять конечный результат, всем должна быть понятна цель проекта;
управление компромиссами - поиск компромиссов между ресурсами проекта, календарным графиком и реализуемыми возможностями;
гибкость – готовность к изменяющимся проектным условиям;
концентрация на бизнес-приоритетах - сосредоточенность на той отдаче и выгоде, которую ожидает получить потребитель решения;
поощрение свободного общения внутри проекта;
создание базовых версии — фиксация состояния любого проектного артефакта, в том числе программного кода, плана проекта, руководства пользователя, настройки серверов и последующее эффективное управление изменениями, аналитика проекта.
MSF предлагает проверенные методики для планирования, проектирования, разработки и внедрения успешных IT-решений. Благодаря своей гибкости, масштабируемости и отсутствию жестких инструкций MSF способен удовлетворить нужды организации или проектной группы любого размера. Методология MSF состоит из принципов, моделей и дисциплин по управлению персоналом, процессами, технологическими элементами и связанными со всеми этими факторами вопросами, характерными для большинства проектов.
Application Lifecycle Management (ALM) - разработанная и поддерживаемая компанией Borland.
Extreme Programming (XP) - экстремальное программирование, поддерживаемое открытым сообществом независимых разработчиков.
Rational Unified Process
Rational Unified Process (RUP) - методология разработки программного обеспечения, созданная и поддерживаемая компанией Rational Software. Это:
новый подход к разработке ПС, основанный на использовании лучших практических методов, успешно зарекомендовавших себя во многих проектах разработки ПС по всему миру;
четко определенный процесс (технологическая процедура), описывающий структуру жизненного цикла проекта, роли и ответственности отдельных исполнителей, выполняемые ими задачи и используемые в процессе разработки модели, отчеты и т.д.;
готовый продукт, предоставляемый в виде веб-сайта, содержащего все необходимые модели и документы с описанием процесса.
В основе методологии лежат 6 основных принципов:
Ранняя идентификация и непрерывное (до окончания проекта) устранение основных рисков.
Концентрация на выполнении требований заказчиков к исполняемой программе (анализ и построение модели прецедентов).
Ожидание изменений в требованиях, проектных решениях и реализации в процессе разработки.
Компонентная архитектура, реализуемая и тестируемая на ранних стадиях проекта.
Постоянное обеспечение качества на всех этапах разработки проекта (продукта).
Работа над проектом в сплочённой команде, ключевая роль в которой принадлежит архитекторам.
Использование методологии RUP направлено на итеративную модель разработки. Особенность методологии состоит в том, что степень формализации может меняться в зависимости от потребностей проекта. Можно по окончании каждого этапа и каждой итерации создавать все требуемые документы и достигнуть максимального уровня формализации, а можно создавать только необходимые для работы документы, вплоть до полного их отсутствия. За счет такого подхода к формализации процессов методология является достаточно гибкой и широко популярной. Данная методология применима как в небольших и быстрых проектах, где за счет отсутствия формализации требуется сократить время выполнения проекта и расходы, так и в больших и сложных проектах, где требуется высокий уровень формализма, например, с целью дальнейшей сертификации продукта. Это преимущество дает возможность использовать одну и ту же команду разработчиков для реализации различных по объему и требованиям проектов.
В конце каждой итерации (в идеале продолжающейся от 2 до 6 недель) проектная команда должна достичь запланированных на данную итерацию целей, создать или доработать проектные артефакты и получить промежуточную, но функциональную версию конечного продукта. Итеративная разработка позволяет быстро реагировать на меняющиеся требования, обнаруживать и устранять риски на ранних стадиях проекта, а также эффективно контролировать качество создаваемого продукта.
IBM Rational Unified Process — процесс, управляемый на основе прецедентов. Это означает, что в качестве метода описания функциональных требований к системе, а также в качестве естественной единицы для дальнейшего планирования и оценки выполнения работ используются сценарии использования. Сценарии использования позволяют легко выявлять реальные потребности будущих пользователей системы и отслеживать полноту описания этих требований. Они гарантируют выполнения требований заказчика к ПС. Кроме того, использование завершенных сценариев в качестве единицы измерения прогресса помогает избежать неадекватной оценки степени выполнения проекта исполнителем.
IBM Rational Unified Process предполагает разработку, реализацию и тестирование архитектуры на самых ранних стадиях выполнения проекта. Такой подход позволяет устранять самые опасные риски, связанные с архитектурой, на ранних стадиях разработки. Благодаря ему удается избежать существенных переработок в последний момент, если вдруг выяснится, что выбранное решение не обеспечивает, к примеру, выполнение требований к производительности системы.
RUP - Четко определенный процесс
RUP создавался по методике, используемой при проектировании ПС. В частности, моделирование производилось с помощью Software Process Engineering Metamodel (SPEM) - стандарта моделирования процессов, основанного на Unified Modeling Language (UML).
Особенностью RUP является то, что в результате работы над проектом создаются и совершенствуются модели. Вместо создания громадного количества бумажных документов, RUP опирается на разработку и развитие семантически обогащенных моделей, всесторонне представляющих разрабатываемую систему. RUP – это руководство по тому, как эффективно использовать UML. Стандартный язык моделирования, используемый всеми членами группы, делает понятными для всех описания требований, проектирование и архитектуру системы.
У процесса есть два измерения:
Динамическая структура. Горизонтальное измерение представляет собой динамическую структуру или временное измерение процесса. Оно показывает, как процесс, выраженный в форме циклов, фаз, итераций и вех, развертывается в ходе жизненного цикла проекта.
Статическая структура. Вертикальное измерение представляет собой статическую структуру процесса. Оно описывает, как элементы процесса - задачи, дисциплины, артефакты и роли - логически группируются в дисциплины или рабочие процессы.
Моделирование бизнес-процессов применяется с тем, чтобы разобраться в структуре исследуемой предметной области, обеспечить единство понимания основных автоматизируемых процессов среди всех участников проекта и определить высокоуровневые требования, которые должны быть реализованы в ходе проекта.
Управление требованиями позволяет прийти к соглашению с заказчиками и конечными пользователями, определить, что должна уметь делать создаваемая система, предоставить более четкие инструкции участникам проекта о возможностях системы, создать базу для успешного планирования работ в проекте и оценки его статуса в любой момент жизненного цикла.
Анализ и проектирование служат для последовательного преобразования выявленных требований к системе в спецификации особого вида, которые описывают, как следует конкретно реализовать конечный продукт. Следует при этом делать различия между анализом и проектированием. Основное из них состоит в том, что спецификации анализа не зависят от конкретной платформы и технологии, для которой осуществляется создание ИС. А спецификации проектирования являются точным представлением проектируемой системы, часто позволяя автоматизировать процесс генерации на их основе программного кода.
Реализация необходима для выявления порядка организации программного кода в терминах отдельных подсистем, преобразования исходного кода в выполняемые компоненты, тестирования созданных компонентов и интеграции отдельных компонентов в подсистемы и системы.
Тестирование позволяет определять и контролировать качество создаваемых продуктов, следить за тем, насколько качественно осуществлена интеграция компонентов и подсистем, все ли требования к системе реализованы и все ли выявленные ошибки устранены до того, как система будет развернута на оборудовании конечного пользователя.
Развертывание является процессом, в ходе которого осуществляется доставка разрабатываемого продукта к конечному пользователю. В ходе данного процесса производится новый выпуск системы, распространение ПО, его установка на стороне конечного пользователя, обучение последнего навыкам эффективной работы с поставленным ПО, предоставление услуг по технической поддержке, бета-тестирование и т. п.
Конфигурационное управление и управление изменениями позволяет организовать эффективную работу с артефактами проекта, контролировать и управлять доступом к ним, вести историю изменений, обеспечить эффективное взаимодействие участников проекта, как в простых командах, так и в распределенных, находящихся на большом удалении друг от друга.
Управление проектом включает в себя непосредственное формирование условий для эффективного хода всего проекта, определение руководств и руководящих принципов для планирования, формирования команды и мониторинга проекта, выявление и управление рисками, организацию работы участников проекта, формирование бюджета, планирование фаз и итераций.
Управление средой позволяет осуществить поддержку всех участников проекта. В эту поддержку входят выбор инструментария и его приобретение, настройка и установка, конфигурирование процесса, доработка и адаптация методологии, используемой для ведения проекта, обучение
Полный жизненный цикл разработки продукта состоит из четырех фаз, каждая из которых включает в себя одну или несколько итераций:
Рис. 4. Графическое представление процесса разработки по RUP
Переход с фазы на фазу возможен только после выполнения задач фазы и представляет собой контрольную точку процесса.
1. Начало (Inception)
В фазе «Начало»:
Формируются видение и границы проекта.
Создается экономическое обоснование (business case).
Определяются основные требования, ограничения и ключевая функциональность продукта.
Создается базовая версия модели прецедентов.
Оцениваются риски.
При завершении начальной фазы оценивается достижение вехи целей жизненного цикла, которое предполагает соглашение заинтересованных сторон о продолжении проекта. В этой фазе, как правило, осуществляется проектирование и реализация первого прототипа системы.
2. Уточнение (Elaboration)
В фазе «Уточнение» производится анализ предметной области и построение исполняемой архитектуры. Это включает в себя:
Документирование требований (включая детальное описание для большинства прецедентов).
Спроектированную, реализованную и оттестированную исполняемую архитектуру.
Обновленное экономическое обоснование и более точные оценки сроков и стоимости.
Сниженные основные риски.
Успешное выполнение фазы разработки означает достижение вехи архитектуры жизненного цикла. В этой фазе концептуальный прототип превращается в реальную систему, позволяющую протестировать и оценить выбранные архитектурные решения. В результате, к концу фазы команда готова к быстрой и качественной разработке основного объема кода системы. Наличие тщательно проработанной устойчивой архитектуры гарантирует, что в дальнейшем не придется перерабатывать большие фрагменты системы.
3. Конструирование(Construction)
В фазе «Конструирование» происходит реализация большей части функциональности продукта. Основной задачей становится быстрая и экономичная разработка кода системы. Она завершается первым внешним релизом системы и вехой начальной функциональной готовности. К концу фазы система должна быть готова к передаче заказчику для бета-тестирования и/или приемо-сдаточных испытаний.
4. Внедрение (Transition)
В фазе «Внедрение» создается финальная версия продукта и передается от разработчика к заказчику. Это включает в себя программу бета-тестирования, обучение пользователей, а также определение качества продукта. В случае, если качество не соответствует ожиданиям пользователей или критериям, установленным в фазе Начало, фаза Внедрение повторяется снова. Выполнение всех целей означает достижение вехи готового продукта и завершение полного цикла разработки.
IBM Rational Unified Process представляет собой готовый продукт. Он состоит из нескольких частей. Это:
размещаемая на Web база знаний, которая состоит из руководств, шаблонов, наставлений по использованию инструментальных средств, и которая может быть разбита на:
обширные руководства для всех членов коллектива разработчиков, для каждого временного интервала жизненного цикла ПО. Руководства представлены в двух видах: для осмысления процесса на верхнем уровне, и в виде подробных наставлений по повседневной деятельности. Руководства опубликованы в HTML формате;
наставления по пользованию инструментальными средствами, которые автоматизируют большие разделы процесса создания ПО. Наставления опубликованы в HTML формате.
примеры и шаблоны для Rational Rose, которые служат руководствами по тому, как структурировать информацию в Rational Rose при следовании указаниям RUP;
шаблоны для SoDa – более десятка шаблонов для SoDa, которые помогают автоматизировать документирование ПО;
Microsoft Word шаблоны – более 30 шаблонов, которые предназначены для поддержки документации по всем последовательностям действий и интервалам жизненного цикла ПО;
планы в формате Microsoft Project – для тех, кому трудно сразу перейти к созданию планов - отражают итерационную разработку. Данные документы помогают произвести такой переход;
Книга Ph. Kruchten - Rational Unified Process-An Introduction. Книга содержит 277 страниц и является хорошим вступлением и обзором к процессу и базе знаний.
веб сайт, содержащий описание процесса и интегрированный со многими инструментальными средствами;
Development Kit – описывает то, каким образом можно конфигурировать и расширить RUP для специфических нужд проекта, и обеспечивает инструменты и шаблоны, помогающие это выполнить;
средства кастомизации, позволяющие создавать собственные процессы (IBM Rational Workbench);
доступ к Resource Center, который содержит последние публикации, обновления, подсказки, методики, а также ссылки на add-on и сервисы.
сообщество пользователей RUP, участие в котором поможет вам обмениваться опытом (в том числе и готовыми описаниями процессов) с другими разработчиками. Пользователи RUP могут либо выбрать одно из типовых представлений процесса, либо создать свое собственное.
Продукт RUP позволяет настраивать процесс под нужды конкретной организации-разработчика и конкретного проекта, включая в него различные готовые компоненты (plug-in), а также разрабатывать и включать в состав процесса собственные компоненты. Продукт содержит также представления (view), которые позволяют участникам разработки получать доступ к необходимой им информации в зависимости от ролевых или персональных настроек.
Использование инструментальных средств
Для обеспечения инструментальной поддержки всех процессов жизненного цикла разработки и сопровождения ПС RUP рекомендует использование специализированных инструментальных средств IBM Rational: — управление требованиями – IBM Rational RequisitePro; — визуальное моделирование и генерация объектного кода – IBM Rational Rose, IBM Rational XDE; — разработка — IBM Rational RapidDeveloper — конфигурационное управление – IBM Rational ClearCase; — управление изменениями – IBM Rational ClearQuest; — автоматизированное документирование – IBM Rational SoDA; — автоматизированное тестирование – IBM Rational TeamTest, IBM Rational TestFactory, IBM Rational Robot, IBM Rational PurifyPlus, Rational Visual Quantify, Rational Visual PureCoverage, Rational PerformanceStudio, IBM Rational SiteCheck и IBM Rational SiteLoad.
Rational Requisite Pro - поддерживает обновления и отслеживает изменения в требованиях для всего коллектива разработчиков, представляя их в удобном виде для чтения, обсуждения и изменений.
Rational ClearQuest - Windows и Web-размещаемый продукт, который помогает коллективу разработчиков отслеживать и управлять всеми действиями по изменению ПО в течение его жизненного цикла.
Rational Rose - мировой лидер среди средств визуального моделирования для бизнес процессов, анализа требований, и проектирования на основе архитектуры компонентов.
Rational SoDA - автоматизирует создание документации для всего процесса разработки ПО, значительно сокращая стоимость документации и время на ее создание.
Rational Purify - средство поиска ошибок на run-time для разработчиков приложений и компонентов, программирующих на C/C++; помогает находить ошибки утечки памяти.
Rational Visual Quantify - средство измерения характеристик для разработчиков приложений и компонентов, программирующих на C/C++, Visual Basic и Java; помогает определять и устранять узкие места в производительности ПО.
Rational Visual PureCoverage - автоматически определяет области кода, которые не подвергаются тестированию; разработчики могут учесть это и более тщательно выполнять проверку.
SQA TeamTest - создает, обслуживает и выполняет автоматизированные функциональные тесты, позволяя тщательно протестировать код и проверить, соответствует ли ПО предъявляемым к нему требованиям.
Rational PerformanceStudio - простое в использовании, точное и масштабируемое средство, которое измеряет и предсказывает характеристики клиент/серверных и Web систем.
Rational ClearCase - лидирующее на рынке средство конфигурационного управления, позволяющее менеджерам проекта отслеживать эволюцию каждого разрабатываемого проекта.
Подход rad
Одним из возможных подходов к разработке прикладного ПО в рамках спиральной модели ЖЦ является получивший широкое распространение способ так называемой быстрой разработки приложений, или RAD (Rapid Application Development). RAD предусматривает наличие трех составляющих:
небольших групп разработчиков (3-7 чел.), выполняющих работы по проектированию отдельных подсистем ПО. Это обусловлено требованием максимальной управляемости коллектива;
короткого, но тщательного проработанного производственного графика (до 3 месяцев);
повторяющегося цикла, при котором разработчики по мере того, как приложение начинает приобретать форму, запрашивают и реализуют в продукте требования, полученные в результате взаимодействия с заказчиком.
Команда разработчиков должна представлять собой группу профессионалов, имеющих опыт в проектировании, программировании и тестировании ПО, способных хорошо взаимодействовать с конечным пользователем и трансформировать их предложения в рабочие прототипы.
ЖЦ ПО в соответствии с подходом RAD включает 4 стадии:
1. Анализ и планирование требований предусматривает действия:
определение функций, которые должна выполнять система;
выделение наиболее приоритетных функций, требующих проработки в первую очередь;
описание информационных потребностей. Формулирование требований к системе осуществляется в основном силами пользователей под руководством специалистов-разработчиков.
Кроме того, на данной стадии реализуются следующие задачи:
ограничивается масштаб времени;
устанавливаются временные рамки для каждой из последующих стадий. Определяется сама возможность реализации проекта в заданных рамках финансирования, на имеющихся аппаратных средствах.
Результатом стадии должны быть список расставленных по приоритету функций будущего ПО ЭИС и предварительные модели ПО.
2. Проектирование. На этой стадии часть пользователей принимает участие в техническом проектировании системы под руководством специалистов-разработчиков. Для быстрого получения работающих прототипов приложений используются соответствующие инструментальные средства (CASE - средства). Пользователи, непосредственно взаимодействуя с разработчиками, уточняют и дополняют требования к системе, которые не были выявлены на предыдущей стадии:
более детально рассматриваются процессы системы;
при необходимости для каждого элементарного процесса создается частичный прототип: экранная форма, диалог, отчет, устраняющий неясности и неоднозначности;
устанавливаются требования разграничения доступа к данным;
определяется состав необходимой документации.
После детального определения состава процессов оценивается количество так называемых функциональных точек разрабатываемой системы и принимается решение о разделении ЭИС на подсистемы, поддающиеся реализации одной командой разработчиков за приемлемое время (до 3 месяцев). Под функциональной точкой понимается любой из следующих элементов разрабатываемой системы:
входной элемент приложения (входной документ или экранная форма);
выходной элемент приложения (отчет, документ, экранная форма)
запрос (пара «вопрос/ответ»);
логический файл (совокупность записей данных, используемых внутри приложения);
интерфейс приложения (совокупность записей данных, передаваемых другому приложению или получаемых от него).
Далее проект распределяется между различными командами разработчиков. Опыт показывает, что для повышения эффективности работ необходимо разбить проект на отдельные слабо связанные по данным и функциям подсистемы. Реализация подсистем должна выполняться отдельными группами специалистов. При этом необходимо обеспечить координацию ведения общего проекта и исключить дублирование результатов работ каждой проектной группы, которое может возникнуть в силу наличия общих данных и функций.
Результатом данной стадии должно быть:
общая информационная модель системы;
функциональные модели системы в целом и подсистем, реализуемых отдельными командами разработчиков;
точно определенные интерфейсы между автономно разрабатываемыми подсистемами;
построенные прототипы экранных форм, отчетов, диалогов.
3. Реализация. На этой стадии выполняется непосредственно сама быстрая разработка приложения.
Разработчики производят итеративное построение реальной системы на основе полученных на предыдущей стадии моделей, а также требований нефункционального характера (требования к надежности, производительности и т.д.).
Пользователи оценивают получаемые результаты и вносят коррективы, если в процессе разработки система перестает удовлетворять определенным ранее требованиям. Тестирование системы осуществляется в процессе разработки.
После окончания работ каждой отдельной команды разработчиков производится постепенная интеграция данной части системы с остальными, формируется полный программный код, выполняется тестирование совместной работы данной части приложения, а затем тестирование системы в целом. Реализация системы завершается выполнением следующих работ:
осуществляется анализ использования данных и определяется необходимость их распределения;
Производится физическое проектирование базы данных;
формируются требования к аппаратным ресурсам;
устанавливаются способы увеличения производительности;
завершается разработка документации проекта.
Результатом стадии является готовая система, удовлетворяющая всем согласованным требованиям.
4. Внедрение. На этой стадии производится обучение пользователей, организационные изменения и параллельно с внедрением новой системы продолжается эксплуатация существующей системы (до полного внедрения новой). Так как стадия реализации достаточно продолжительна, планирование и подготовка к внедрению должны начинаться заранее, как правило, на стадии проектирования системы.
Приведенная схема разработки ЭИС не является абсолютной. Возможны различные варианты, зависящие, например, от начальных условий, в которых ведется разработка:
разрабатывается совершенно новая система;
уже было проведено обследование организации и существует модель ее деятельности;
в организации уже существует некоторая ЭИС, которая может быть использована в качестве начального прототипа или должна быть интегрирована с разрабатываемой системой.
Следует отметить, что подход RAD, как и любой другой подход, не может претендовать на универсальность. Он хорош в первую очередь для относительно небольших проектов, разрабатываемых для конкретного заказчика. Если же разрабатывается крупномасштабная система (например, масштаба отрасли), которая не является законченным продуктом, а представляет собой комплекс программных компонентов, адаптируемых к программно-аппаратным платформам, системам управления базами данных (СУБД), средствам телекоммуникаций, то на первый план выступают такие показатели проекта как управляемость и качество, которые могут войти в противоречие с простотой и скоростью разработки. Для таких проектов необходимы высокий уровень планирования и жесткая дисциплина проектирования, строгое следование заранее разработанным протоколам и интерфейсам, что снижает скорость разработки.
Подход RAD не применим для построения сложных расчетных программ, операционных систем.
Не годится этот подход и для приложений, в которых отсутствует ярко выраженная интерфейсная часть, наглядно определяющая логику работы системы и приложений, от которых зависит безопасность людей (например программа управления самолетом или атомной станцией), так как итеративный подход предполагает, что первые несколько версий наверняка не будут полностью работоспособны, что в данном случае исключается.
Итак, перечислим основные принципы подхода RAD.
Разработка приложений итерациями.
Необязательность полного завершения работ на каждой стадии ЖЦ ПО.
Обязательность вовлечения пользователей в процесс разработки ЭИС.
Целесообразность применения CASE - средств, обеспечивающих целостность проекта и генерацию кода приложений.
Целесообразность применения средств управления конфигурацией, облегчающих внесение изменений в проект и сопровождение готовой системы.
Использование прототипирования, позволяющее полнее выяснить и удовлетворить потребности пользователей.
Тестирование и развитие проекта, осуществляемые одновременно с разработкой.
Ведение разработки немногочисленной хорошо управляемой командой профессионалов.
Грамотное руководство разработкой системы, четкое планирование и контроль выполнения работ.
Таким образом, существует множество различных методологий и подходов при разработке программного обеспечения, они не универсальны и описываются различными принципами. Выбор методологии и подхода при разработке конкретного проекта зависит от предъявляемых требований.
Лекция 3 тема: Этап логического проектирования ис. Основные подходы при создании концептуальной модели.
Литература: 1. Зелковиц М., Шоу А., Гэннон Дж. Принципы разработки программного обеспечения.
2. Гецци к., Джазайери м., Мандриоли д. Основы инженерии программного обеспечения.
3. Камаев В. А., Костерин В. В. Технологии программирования.
4. Грекул В.И. Проектирование информационных систем.
Рассмотрим такие понятии, как «Предметная область» и «Бизнес-моделирование».
Предметная область - часть реального мира, подлежащая изучению с целью организации управления и, в конечном счете, автоматизации. Предметная область представляется множеством фрагментов, например, предприятие - цехами, дирекцией, бухгалтерией и т.д. Каждый фрагмент предметной области характеризуется множеством объектов и процессов, использующих объекты, а также множеством пользователей, характеризуемых различными взглядами на предметную область.
Бизнес-моделирование (деловое моделирование) - деятельность по формированию моделей организаций, включающая описание деловых объектов (подразделений, должностей, ресурсов, ролей, процессов, операций, информационных систем, носителей информации и т. д.) и указание связей между ними. Требования к формируемым моделям и их соответствующее содержание определяются целями моделирования.
Бизнес-моделирование является отдельным подпроцессом в процессе разработки программного обеспечения, в котором описывается деятельность компании и определяются требования к системе, т.е. те подпроцессы и операции, которые подлежат автоматизации в разрабатываемой информационной системе.
Моделью бизнес-процесса называется его формализованное (графическое, табличное, текстовое, символьное) описание, отражающее реально существующую или предполагаемую деятельность предприятия.
Модель, как правило, содержит следующие сведения о бизнес-процессе:
набор составляющих процесс шагов - бизнес-функций;
порядок выполнения бизнес-функций;
механизмы контроля и управления в рамках бизнес-процесса;
исполнителей каждой бизнес-функции;
входящие документы/информацию, исходящие документы/информацию;
ресурсы, необходимые для выполнения каждой бизнес-функции;
документацию/условия, регламентирующие выполнение каждой бизнес-функции;
параметры, характеризующие выполнение бизнес-функций и процесса в целом.
Модели бизнес-процессов применяются предприятиями для различных целей, что определяет тип разрабатываемой модели.
Графическая модель бизнес-процесса в виде наглядной, общепонятной диаграммы может служить для обучения новых сотрудников их должностным обязанностям, согласования действий между структурными единицами компании, подбора или разработки компонентов информационной системы и т. д. Описание с помощью моделей такого типа существующих и целевых бизнес-процессов используется для оптимизации и совершенствования деятельности компании путем устранения узких мест, дублирования функций и прочего.
Имитационные модели бизнес-процессов позволяют оценить их эффективность и посмотреть, как будет выполняться процесс с входными данными, не встречавшимися до сих пор в реальной работе предприятия.
Исполняемые модели бизнес-процессов могут быть запущены на специальном программном обеспечении для автоматизации процесса непосредственно по модели.
Поскольку модели бизнес-процессов предназначены для широкого круга пользователей (бизнес-аналитиков, рядовых сотрудников и руководства компании), а их построением часто занимаются неспециалисты в области информационных технологий, наиболее широко используются модели графического типа, в которых в соответствии с определенной методологией бизнес-процесс представляется в виде наглядного графического изображения - диаграммы, состоящей в основном из прямоугольников и стрелок. Такое представление обладает высокой, многомерной информативностью, которая выражается в различных свойствах (цвет, фон, начертание и т.д.) и атрибутах (вес, размер, стоимость, время и т.д.) каждого объекта и связи.
В последние годы разработчики программных средств моделирования бизнес-процессов уделяют большое внимание преобразованию графических моделей в модели других видов, в частности в исполняемые, назначением которых является обеспечение автоматизации бизнес-процесса и интеграция работы задействованных в его исполнении информационных систем.
Согласно еще одной классификации, пришедшей из моделирования сложных систем, выделяют следующие виды моделей бизнес-процессов:
функциональные, описывающие совокупность выполняемых системой функций и их входы и выходы;
поведенческие, показывающие, когда и/или при каких условиях выполняются бизнес- функции, с помощью таких категорий, как состояние системы, событие, переход из одного состояния в другое, условия перехода, последовательность событий;
структурные, характеризующие морфологию системы - состав подсистем, их взаимосвязи;
информационные, отражающие структуры данных - их состав и взаимосвязи.
Проблема сложности является главной проблемой, которую приходится решать при создании больших систем любой природы, в том числе и ЭИС. Ни один разработчик не в состоянии выйти за пределы человеческих возможностей и понять все систему в целом. Единственно эффективный подход к решению этой проблемы заключается в построении сложной системы из небольшого количества крупных частей, каждая из которых, в свою очередь, строится из частей меньшего размера и т.д., до тех пор, пока самые небольшие части можно будет строить из имеющегося материала. Этот подход известен под самыми разными названиями, среди них такие, как «разделяй и властвуй», иерархическая декомпозиция и др. По отношению к проектированию сложной программной системы это означает, что ее необходимо разделять (декомпозировать) на небольшие подсистемы, каждую из которых можно разрабатывать независимо от других. Это позволяет при разработке подсистемы любого уровня держать в уме информацию только о ней, а не обо всех остальных частях системы. Правильная декомпозиция является главным способом преодоления сложности разработки больших систем. Понятие «правильная» по отношению к декомпозиции означает следующее:
1. Количество связей между отдельными подсистемами должно быть минимальным.
2. Связность отдельных частей внутри каждой подсистемы должна быть максимальной.
Структура системы должна быть таковой, чтобы все взаимодействия между ее подсистемами укладывались в ограниченные, стандартные рамки:
1. Каждая подсистема должна инкапсулировать свою содержимое (скрывать его от других подсистем).
2. Каждая подсистема должна иметь четко определенный интерфейс с другими подсистемами.
На сегодняшний день в программной инженерии существуют два основных подхода к разработке ПО ЭИС, принципиальное различие которых обусловлено разными способами декомпозиции систем:
Функционально-модульный или структурный
Объектно-ориентированный.
Объектные методики рассматривают моделируемую организацию как набор взаимодействующих объектов – производственных единиц. Объект определяется как осязаемая реальность – предмет или явление, имеющие четко определяемое поведение. Целью применения данной методики является выделение объектов, составляющих организацию, и распределение между ними ответственностей за выполняемые действия. При этом структура системы описывается в терминах объектов и связей между ними, а поведение системы описывается в терминах обмена сообщениями между объектами.
Функциональные методики, наиболее известной из которых является методика IDEF, рассматривают организацию как набор функций, преобразующий поступающий поток информации в выходной поток. Процесс преобразования информации потребляет определенные ресурсы. Основное отличие от объектной методики заключается в четком отделении функций (методов обработки данных) от самих данных.
С точки зрения бизнес-моделирования каждый из представленных подходов обладает своими преимуществами. Объектный подход позволяет построить более устойчивую к изменениям систему, лучше соответствует существующим структурам организации. Функциональное моделирование хорошо показывает себя в тех случаях, когда организационная структура находится в процессе изменения или вообще слабо оформлена. Подход от выполняемых функций интуитивно лучше понимается исполнителями при получении от них информации об их текущей работе.
Методы моделирования бизнес процессов
На сегодняшний день существует достаточно большое количество методов моделирования бизнес процессов. Эти методы относятся к разным видам моделирования и позволяют сфокусировать внимание на различных аспектах. Они содержат как графические, так и текстовые средства, за счет которых можно наглядно представить основные компоненты процесса и дать точные определения параметров и связей элементов.
Наиболее часто моделирование бизнес-процессов выполняют с помощью следующих методов:
Flow Chart Diagram (диаграмма потока работ) – это графический метод представления процесса в котором операции, данные, оборудование процесса и пр. изображаются специальными символами. Метод применяется для отображения логической последовательности действий процесса. Главным достоинством метода является его гибкость. Процесс может быть представлен множеством способов.
Data Flow Diagram (диаграмма потока данных). Диаграмма потока данных или DFD применяется для отображения передачи информации (данных) от одной операции процесса к другой. DFD описывает взаимосвязь операций за счет информации и данных. Этот метод является основой структурного анализа процессов, т.к. позволяет разложить процесс на логические уровни. Каждый процесс может быть разбит на подпроцессы с более высоким уровнем детализации. Применение DFD позволяет отразить только поток информации, но не поток материалов. Диаграмма потока данных показывает, как информация входит и выходит из процесса, какие действия изменяют информацию, где информация хранится в процессе и прочее.
Role Activity Diagram (диаграмма ролей). Она применяется для моделирования процесса с точки зрения отдельных ролей, групп ролей и взаимодействия ролей в процессе. Роль представляет собой абстрактный элемент процесса, выполняющий какую-либо организационную функцию. Диаграмма ролей показывает степень «ответственности» за процесс и его операции, а также взаимодействие ролей.
IDEF (Integrated Definition for Function Modeling) – представляет собой целый набор методов для описания различных аспектов бизнес-процессов (IDEF0, IDEF1, IDEF1X , IDEF2, IDEF3, IDEF4, IDEF5). Эти методы строятся на базе методологии SADT (Structured Analysis and Design Technique). Для моделирования бизнес процессов наиболее часто применяют методы IDEF0 и IDEF3.
IDEF0 – позволяет создать модель функций процесса. На диаграмме IDEF0 отображаются основные функции процесса, входы, выходы, управляющие воздействия и устройства, взаимосвязанные с основными функциями. Процесс может быть декомпозирован на более низкий уровень.
IDEF3 – этот метод позволяет создать «поведенческую» модель процесса. IDEF3 состоит из двух видов моделей. Первый вид представляет описание потока работ. Второй – описание состояний перехода объектов.
ERD (Entity - Relationship Diagrams) - диаграммы «сущность-связь». С помощью ERD выполняется описание используемых в организации данных на концептуальном уровне, не зависимо от средств реализации базы данных (СУБД). ER-диаграммы моделируют данные и их отношения. Ключевыми понятиями в них являются сущность, атрибуты сущности и связи между сущностями.
Цветные сети Петри – этот метод представляет модель процесса в виде графа, где вершинами являются действия процесса, а дугами события, за счет которых осуществляется переход процесса из одного состояния в другое. Сети Петри применяют для динамического моделирования поведения процесса.
Unified Modeling Language (UML) - представляет собой объектно-ориентированный метод моделирования процессов. Он состоит из 8-ти различных диаграмм, каждая из которых позволяет моделировать отдельные статические или динамические аспекты процесса.
Большинство из указанных методов реализованы в виде программного обеспечения. Оно позволяет осуществлять поддержку бизнес-процессов или проводить их анализ. Примерами такого ПО являются различные CASE средства моделирования процессов.
Структурный подход
Итак, сущность структурного подхода к разработке ПО ЭИС заключается в ее декомпозиции (разбиении) на автоматизируемые функции: система разбивается на функциональные подсистемы, которые, в свою очередь, делятся на подфункции, те - на задачи и так далее до конкретных процедур. При этом система сохраняет целостное представление, в котором все составляющие компоненты взаимоувязаны. При разработке системы «снизу-вверх», от отдельных задач ко всей системе, целостность теряется, возникают проблемы при описании информационного взаимодействия отдельных компонентов.
Все наиболее распространенные методы структурного подхода базируются на ряде общих принципов:
1. Принцип «разделяй и властвуй»;
2. Принцип иерархического упорядочения - принцип организации составных частей системы в иерархические древовидные структуры с добавлением новых деталей на каждом уровне.
Выделение двух базовых принципов не означает, что остальные принципы являются второстепенными, т.к. игнорирование любого из них может привести к непредсказуемым последствиям (в том числе и к провалу всего проекта»). Основными из этих принципов являются:
1. Принцип абстрагирования - выделение существенных аспектов системы и отвлечение от несущественных.
2. Принцип непротиворечивости, обоснованность и согласованность элементов системы.
3. Принцип структурирования данных - данные должны быть структурированы и иерархически организованы.
В структурном подходе в основном две группы средств, описывающих функциональную структуру системы и отношения между данными. Каждой группе средств соответствуют определенные виды моделей (диаграмм), наиболее распространенными среди них являются:
DFD (Data Flow Diagrams) - диаграммы потоков данных;
SADT (Structured Analysis and Design Technique - методология структурного анализа и проектирования) - модели и соответствующие функциональные диаграммы: нотации IDEF0 (функциональное моделирование систем), IDEF1х (концептуальное моделирование баз данных), IDEF3х (построение систем оценки качества работы объекта; графическое описание потока процессов, взаимодействия процессов и объектов, которые изменяются этими процессами);
ERD (Entity - Relationship Diagrams) - диаграммы «сущность-связь».
Практически во всех методах структурного подхода (структурного анализа) на стадии формирования требований к ПО используются две группы средств моделирования:
1. Диаграммы, иллюстрирующие функции, которые система должна выполнять, и связи между этими функциями - DFD или SADT (IDEF0).
2. Диаграммы, моделирующие данные и их отношения (ERD).
Конкретный вид перечисленных диаграмм и интерпретация их конструкций зависят от стадии ЖЦ ПО.
На стадии формирования требований к ПО SADT-модели и DFD используются для построения модели “AS-IS” и модели “TO-BE”, отражая таким образом существующую и предлагаемую структуру бизнес-процессов организации и взаимодействие между ними (использование SADT-моделей , как правило, ограничивается только данной стадией, поскольку они изначально не предназначались для проектирования ПО). С помощью ERD выполняется описание используемых в организации данных на концептуальном уровне, не зависимо от средств реализации базы данных (СУБД).
На стадии проектирования DFD используются для описания структуры проектируемой системы.
Перечисленные модели в совокупности дают полное описание ПО ЭИС независимо от того, является ли система существующей или вновь разрабатываемой.
Объектно-ориентированный подход
Принципиальное отличие между функциональным и объектным подходом заключается в способе декомпозиции системы. Объектно-ориентированный подход использует объектную декомпозицию, при этом статическая структура описывается в терминах объектов и связей между ними, а поведение системы описывается в терминах обмена сообщениями между объектами. Целью методики является построение бизнес-модели организации, позволяющей перейти от модели сценариев использования к модели, определяющей отдельные объекты, участвующие в реализации бизнес-функций.
Концептуальной основой объектно-ориентированного подхода является объектная модель, которая строится с учетом следующих принципов:
абстрагирование;
инкапсуляция;
модульность;
иерархия;
типизация;
параллелизм;
устойчивость.
Основными понятиями объектно-ориентированного подхода являются объект и класс.
Определение: Объект - предмет или явление, имеющее четко определенное поведение и обладающие состоянием, поведением и индивидуальностью.
Структура и поведение схожих объектов определяют общий для них класс.
Определение: Класс – это множество объектов, связанных общностью структуры и поведения.
Следующую группу важных понятий объектного подхода составляют наследование и полиморфизм.
Понятие полиморфизм может быть интерпретировано как способность класса принадлежать более чем одному типу.
Наследование означает построение новых классов на основе существующих с возможностью добавления или переопределения данных и методов.
Важным качеством объектного подхода является согласованность моделей деятельности организации и моделей проектируемой информационной системы от стадии формирования требований до стадии реализации. По объектным моделям может быть прослежено отображение реальных сущностей моделируемой предметной области (организации) в объекты и классы информационной системы.
Большинство существующих методов объектно-ориентированного подхода включают язык моделирования и описание процесса моделирования. Процесс – это описание шагов, которые необходимо выполнить при разработке проекта. В качестве языка моделирования объектного подхода используется унифицированный язык моделирования UML, который содержит стандартный набор диаграмм для моделирования.
В нотации языка UML определены следующие виды канонических диаграмм:
вариантов использования (use case diagram)
классов (class diagram)
кооперации (collaboration diagram)
последовательности (sequence diagram)
состояний (statechart diagram)
деятельности (activity diagram)
компонентов (component diagram)
развертывания (deployment diagram)
Перечень этих диаграмм и их названия являются каноническими в том смысле, что представляют собой неотъемлемую часть графической нотации языка UML. Более того, процесс ООАП неразрывно связан с процессом построения этих диаграмм. При этом совокупность построенных таким образом диаграмм является самодостаточной в том смысле, что в них содержится вся информация, которая необходима для реализации проекта сложной системы.
Каждая из этих диаграмм детализирует и конкретизирует различные представления о модели сложной системы в терминах языка UML. При этом диаграмма вариантов использования представляет собой наиболее общую концептуальную модель сложной системы, которая является исходной для построения всех остальных диаграмм. Диаграмма классов, по своей сути, логическая модель, отражающая статические аспекты структурного построения сложной системы.
Диаграммы кооперации и последовательностей представляют собой разновидности логической модели, которые отражают динамические аспекты функционирования сложной системы. Диаграммы состояний и деятельности предназначены для моделирования поведения системы. И, наконец, диаграммы компонентов и развертывания служат для представления физических компонентов сложной системы и поэтому относятся к ее физической модели.
В целом интегрированная модель сложной системы в нотации UML может быть представлена в виде совокупности указанных выше диаграмм (рис. 2).
Рис. 2. Интегрированная модель сложной системы в нотации UML
На этапе постановки задачи и анализа требований к системе используют диаграммы прецедентов, диаграммы деятельностей для расшифровки содержания прецедентов, диаграммы состояний для моделирования поведения объектов со сложным состоянием, диаграммы классов для выделения концептуальных сущностей предметной области задачи и диаграммы последовательностей действий.
Спецификация разрабатываемого программного обеспечения при использовании UML объединяет несколько моделей: логическую, использования, реализации, процессов, развертывания.
Модель использования содержит описание функций программного обеспечения с точки зрения пользователя.
Логическая модель описывает ключевые понятия моделируемого программного обеспечения (классы, интерфейсы и т. п.), т. е. средства, обеспечивающие его функциональность.
Модель реализации определяет реальную организацию программных модулей в среде разработки.
Модель процессов отображает организацию вычислений и позволяет оценить производительность, масштабируемость и надежность программного обеспечения.
Модель развертывания показывает, каким образом программные компоненты размещаются на конкретном оборудовании.
Все вместе указанные модели, каждая из которых характеризует определенную сторону проектируемого продукта, составляют относительно полную модель разрабатываемого программного обеспечения.
Диаграмма (Diagram) — это графическое представление множества элементов. Чаще всего она изображается в виде связного графа с вершинами (сущностями) и ребрами (отношениями) и представляет собой некоторую проекцию системы.
Объектно-ориентированный подход обладает следующими преимуществами:
Объектная декомпозиция дает возможность создавать модели меньшего размера путем использования общих механизмов, обеспечивающих необходимую экономию выразительных средств. Использование объектного подхода существенно повышает уровень унификации разработки и пригодность для повторного использования, что ведет к созданию среды разработки и переходу к сборочному созданию моделей.
Объектная декомпозиция позволяет избежать создания сложных моделей, так как она предполагает эволюционный путь развития модели на базе относительно небольших подсистем.
Объектная модель естественна, поскольку ориентирована на человеческое восприятие мира.
К недостаткам объектно-ориентированного подхода относятся высокие начальные затраты. Этот подход не дает немедленной отдачи. Эффект от его применения сказывается после разработки двух–трех проектов и накопления повторно используемых компонентов. Диаграммы, отражающие специфику объектного подхода, менее наглядны.
Лекция 4 тема: Описание функциональности разработки. Методы и инструменты.
Литература: 1. Зелковиц М., Шоу А., Гэннон Дж. Принципы разработки программного обеспечения.
2. Гецци к., Джазайери м., Мандриоли д. Основы инженерии программного обеспечения.
3. Камаев В. А., Костерин В. В. Технологии программирования.
4. Грекул В.И. Проектирование информационных систем.
Функциональная методика idef0
Методологию IDEF0 можно считать следующим этапом развития хорошо известного графического языка описания функциональных систем SADT (Structured Analysis and Design Technique). Исторически IDEF0 как стандарт был разработан в 1981 году в рамках обширной программы автоматизации промышленных предприятий, которая носила обозначение ICAM (Integrated Computer Aided Manufacturing). Семейство стандартов IDEF унаследовало свое обозначение от названия этой программы (IDEF=Icam DEFinition), и последняя его редакция была выпущена в декабре 1993 года Национальным Институтом по Стандартам и Технологиям США (NIST).
Целью методики является построение функциональной схемы исследуемой системы, описывающей все необходимые процессы с точностью, достаточной для однозначного моделирования деятельности системы.
В основе методологии лежат четыре основных понятия: функциональный блок, интерфейсная дуга, декомпозиция, глоссарий.
Функциональный блок (Activity Box) представляет собой некоторую конкретную функцию в рамках рассматриваемой системы. По требованиям стандарта название каждого функционального блока должно быть сформулировано в глагольном наклонении (например, "производить услуги"). На диаграмме функциональный блок изображается прямоугольником (рис. 1). Каждая из четырех сторон функционального блока имеет свое определенное значение (роль), при этом:
верхняя сторона имеет значение "Управление" (Control);
левая сторона имеет значение "Вход" (Input);
правая сторона имеет значение "Выход" (Output);
нижняя сторона имеет значение "Механизм" (Mechanism).
Рис. 1. Функциональный блок
Интерфейсная дуга (Arrow) отображает элемент системы, который обрабатывается функциональным блоком или оказывает иное влияние на функцию, представленную данным функциональным блоком. Интерфейсные дуги часто называют потоками или стрелками.
С помощью интерфейсных дуг отображают различные объекты, в той или иной степени определяющие процессы, происходящие в системе. Такими объектами могут быть элементы реального мира (детали, вагоны, сотрудники и т.д.) или потоки данных и информации (документы, данные, инструкции и т.д.).
В зависимости от того, к какой из сторон функционального блока подходит данная интерфейсная дуга, она носит название "входящей", "исходящей" или "управляющей". Функциональный блок (или Функция) преобразует Входы в Выходы (т.е. входную информацию в выходную), Управление определяет, когда и как это преобразование может или должно произойти, Механизмы непосредственно осуществляют это преобразование. Механизмы (дуги снизу) показывают средства, с помощью которых осуществляется выполнение функций. Механизм может быть человеком, компьютером или любым другим устройством, которое помогает выполнять данную функцию.
Дуги показывают, как функции между собой взаимосвязаны, как они обмениваются данными и осуществляют управление друг другом. Дуги могут разветвляться и соединяться. Выходы одной функции могут быть Входами, Управлением или Механизмами для другой.
Необходимо отметить, что любой функциональный блок по требованиям стандарта должен иметь, по крайней мере, одну управляющую интерфейсную дугу и одну исходящую. Это и понятно – каждый процесс должен происходить по каким-то правилам (отображаемым управляющей дугой) и должен выдавать некоторый результат (выходящая дуга), иначе его рассмотрение не имеет никакого смысла.
Обязательное наличие управляющих интерфейсных дуг является одним из главных отличий стандарта IDEF0 от других методологий классов DFD (Data Flow Diagram) и WFD (Work Flow Diagram).
Декомпозиция (Decomposition) является основным понятием стандарта IDEF0. Принцип декомпозиции применяется при разбиении сложного процесса на составляющие его функции. При этом уровень детализации процесса определяется непосредственно разработчиком модели.
Декомпозиция позволяет постепенно и структурировано представлять модель системы в виде иерархической структуры отдельных диаграмм, что делает ее менее перегруженной и легко усваиваемой.
Последним из понятий IDEF0 является глоссарий (Glossary). Для каждого из элементов IDEF0-диаграмм, функциональных блоков, интерфейсных дуг - существующий стандарт подразумевает создание и поддержание набора соответствующих определений, ключевых слов, повествовательных изложений и т.д., которые характеризуют объект, отображенный данным элементом. Этот набор называется глоссарием и является описанием сущности данного элемента. Глоссарий гармонично дополняет наглядный графический язык, снабжая диаграммы необходимой дополнительной информацией.
Прежде чем приступать к описанию модели, необходимо определить субъект модели, цель и точку зрения модели. В качестве субъекта модели выступает описываемая система. Субъект определяет, что включить в модель, а что исключить из нее.
Модель IDEF0 всегда начинается с представления системы как единого целого – одного функционального блока с интерфейсными дугами, простирающимися за пределы рассматриваемой области. Такая диаграмма с одним функциональным блоком называется контекстной диаграммой.
В пояснительном тексте к контекстной диаграмме должна быть указана цель (Purpose) построения диаграммы в виде краткого описания и зафиксирована точка зрения (Viewpoint).
Определение и формализация цели разработки IDEF0-модели является крайне важным моментом. Фактически цель определяет соответствующие области в исследуемой системе, на которых необходимо фокусироваться в первую очередь. Цель становится критерием окончания моделирования.
Точка зрения определяет основное направление развития модели и уровень необходимой детализации. Четкое фиксирование точки зрения позволяет разгрузить модель, отказавшись от детализации и исследования отдельных элементов, не являющихся необходимыми, исходя из выбранной точки зрения на систему. Правильный выбор точки зрения существенно сокращает временные затраты на построение конечной модели.
Выделение подпроцессов. В процессе декомпозиции функциональный блок, который в контекстной диаграмме отображает систему как единое целое, подвергается детализации на другой диаграмме. Получившаяся диаграмма второго уровня содержит функциональные блоки, отображающие главные подфункции функционального блока контекстной диаграммы, и называется дочерней (Child Diagram) по отношению к нему (каждый из функциональных блоков, принадлежащих дочерней диаграмме, соответственно называется дочерним блоком – Child Box). В свою очередь, функциональный блок-предок называется родительским блоком по отношению к дочерней диаграмме (Parent Box), а диаграмма, к которой он принадлежит – родительской диаграммой (Parent Diagram). В правом верхнем углу диаграммы располагается номер блока, чью декомпозицию представляет диаграмма, он же рассматривается как номер диаграммы. Внизу блоков располагается номер, который будет присваиваться диаграмме, на которой будет представлена декомпозиция данного блока.
Каждая из подфункций дочерней диаграммы может быть далее детализирована путем аналогичной декомпозиции соответствующего ей функционального блока. В каждом случае декомпозиции функционального блока все интерфейсные дуги, входящие в данный блок или исходящие из него, фиксируются на дочерней диаграмме. Этим достигается структурная целостность IDEF0–модели.
Блоки SADT никогда не размещаются на диаграмме случайным образом. Они размещаются по степени важности, как ее понимает автор диаграммы. Этот относительный порядок называется доминированием. Доминирование понимается как влияние, которое один блок оказывает на другие блоки диаграммы. Например, самым доминирующим блоком диаграммы может быть либо первый из требуемой последовательности функций, либо планирующая или контролирующая функция, влияющая на все остальные функции. Наиболее доминирующий блок обычно располагается в верхнем левом углу диаграммы, а наименее доминирующий – в правом нижнем углу. В результате получается «ступенчатая» схема.
После завершения диаграммы ее внешние дуги стыкуются с родительской диаграммой для обеспечения согласованности. Одним из способов такой стыковки может служить присваивание кодов ICOM внешним дугам новой диаграммы согласно следующим правилам:
присваивается код каждой зрительной связи. Используется I для входных дуг, С - для связей между дугами управления, О - для связей между выходными дугами, М - для связей между дугами механизма.
после каждой буквы добавляется цифра, соответствующая положению данной дуги среди других дуг того же типа, касающихся родительского блока. Причем входные и выходные дуги пересчитываются сверху вниз, а дуги управлений и механизмов пересчитываются слева направо.
Данный принцип используется на контекстной диаграмме. Далее полученные там ICOM-коды используются вместо имен данных для сокращения записи.
Дуги, входящие в блок и выходящие из него на диаграмме верхнего уровня, являются точно теми же самыми, что и дуги, входящие в диаграмму нижнего уровня и выходящие из нее, потому что блок и диаграмма изображают одну и ту же часть системы.
Иногда отдельные интерфейсные дуги высшего уровня не имеет смысла продолжать рассматривать на диаграммах нижнего уровня, или наоборот — отдельные дуги нижнего отражать на диаграммах более высоких уровней – это будет только перегружать диаграммы и делать их сложными для восприятия. Для решения подобных задач в стандарте IDEF0 предусмотрено понятие туннелирования. Обозначение "туннеля" (Arrow Tunnel) в виде двух круглых скобок вокруг начала интерфейсной дуги обозначает, что эта дуга не была унаследована от функционального родительского блока и появилась (из "туннеля") только на этой диаграмме. В свою очередь, такое же обозначение вокруг конца (стрелки) интерфейсной дуги в непосредственной близи от блока–приемника означает тот факт, что в дочерней по отношению к этому блоку диаграмме эта дуга отображаться и рассматриваться не будет. Чаще всего бывает, что отдельные объекты и соответствующие им интерфейсные дуги не рассматриваются на некоторых промежуточных уровнях иерархии, – в таком случае они сначала "погружаются в туннель", а затем при необходимости "возвращаются из туннеля".
Обычно IDEF0-модели несут в себе сложную и концентрированную информацию, и для того, чтобы ограничить их перегруженность и сделать удобочитаемыми, в стандарте приняты соответствующие ограничения сложности.
Рекомендуется представлять на диаграмме от трех до шести функциональных блоков, при этом количество подходящих к одному функциональному блоку (выходящих из одного функционального блока) интерфейсных дуг предполагается не более четырех.
Стандарт IDEF0 содержит набор процедур, позволяющих разрабатывать и согласовывать модель большой группой людей, принадлежащих к разным областям деятельности моделируемой системы. Обычно процесс разработки является итеративным и состоит из следующих условных этапов:
Создание модели группой специалистов, относящихся к различным сферам деятельности предприятия. Эта группа в терминах IDEF0 называется авторами (Authors). Построение первоначальной модели является динамическим процессом, в течение которого авторы опрашивают компетентных лиц о структуре различных процессов, создавая модели деятельности подразделений. При этом их интересуют ответы на следующие вопросы:
Что поступает в подразделение "на входе"?
Какие функции и в какой последовательности выполняются в рамках подразделения?
Кто является ответственным за выполнение каждой из функций?
Чем руководствуется исполнитель при выполнении каждой из функций?
Что является результатом работы подразделения (на выходе)?
На основе имеющихся положений, документов и результатов опросов создается черновик (Model Draft) модели.
Распространение черновика для рассмотрения, согласований и комментариев. На этой стадии происходит обсуждение черновика модели с широким кругом компетентных лиц (в терминах IDEF0 — читателей) на предприятии. При этом каждая из диаграмм черновой модели письменно критикуется и комментируется, а затем передается автору. Автор, в свою очередь, также письменно соглашается с критикой или отвергает ее с изложением логики принятия решения и вновь возвращает откорректированный черновик для дальнейшего рассмотрения. Этот цикл продолжается до тех пор, пока авторы и читатели не придут к единому мнению.
Официальное утверждение модели. Утверждение согласованной модели происходит руководителем рабочей группы в том случае, если у авторов модели и читателей отсутствуют разногласия по поводу ее адекватности. Окончательная модель представляет собой согласованное представление о предприятии (системе) с заданной точки зрения и для заданной цели.
Наглядность графического языка IDEF0 делает модель вполне читаемой и для лиц, которые не принимали участия в проекте ее создания, а также эффективной для проведения показов и презентаций. В дальнейшем на базе построенной модели могут быть организованы новые проекты, нацеленные на производство изменений в модели.
Недостатки IDEF0-диаграмм:
явно не указаны ни последовательность, ни время;
большое количество дуг на диаграммах и большое количество уровней декомпозиции увеличивают сложность восприятия;
трудно увязать нескольких процессов.
На рис. 2 приведена диаграмма IDEF0 верхнего уровня бизнес-процесса "Увольнение сотрудника".
Рис. 2. Диаграмма IDEF0 верхнего уровня бизнес-процесса "Увольнение сотрудника".
DFD
Нотация DFD – моделирование потоков данных (процессов) – основа методологии Gane/Sarson, в соответствии с которой модель системы определяется как иерархия диаграмм потоков данных, описывающих асинхронный процесс преобразования информации от ее ввода в систему до выдачи объекту или субъекту. Контекстные диаграммы иерархии определяют основные процессы или подсистемы системы с внешними входами и выходами. Они детализируются при помощи диаграмм-потомков. Декомпозиция ведется до тех пор, пока не будет достигнут такой уровень декомпозиции, на котором процессы становятся элементарными и детализировать их далее невозможно. Главная цель такого представления - продемонстрировать, как каждый процесс преобразует свои входные данные в выходные, а также выявить отношения между этими процессами.
Диаграммы DFD обычно строятся для наглядного изображения текущей работы системы документооборота организации. Как правило, диаграммы DFD используют в качестве дополнения модели бизнес-процессов, выполненной в IDEF0.
Источники информации (внешние сущности) порождают информационные потоки (потоки данных), переносящие информацию к подсистемам или процессам. Те в свою очередь преобразуют информацию и порождают новые потоки, которые переносят информацию к другим процессам или подсистемам, накопителям данных или внешним сущностям - потребителям информации. Таким образом, основными компонентами диаграмм потоков данных являются:
внешние сущности;
системы/подсистемы;
процессы;
накопители данных;
потоки данных.
Данный стандарт представлен двумя немного различающихся вариантами, которые называют нотациями. Первая из них называется нотацией Гейна Сарсана, вторая нотацией Йордона-Де Марко.
Таблица 1. Элементы методологии DFD в нотациях Гейна-Сарсана и Йордона-Де Марко.
1. Внешние сущности
Внешняя сущность представляет собой материальный предмет или физическое лицо, представляющее собой источник или приемник информации, например, заказчики, персонал, поставщики, клиенты, склад. Определение некоторого объекта или системы в качестве внешней сущности указывает на то, что она находится за пределами границ анализируемой ИС. В процессе анализа некоторые внешние сущности могут быть перенесены внутрь диаграммы анализируемой ИС, если это необходимо, или, наоборот, часть процессов ИС может быть вынесена за пределы диаграммы и представлена как внешняя сущность.
Внешняя сущность обозначается квадратом (рисунок 3), расположенным как бы "над" диаграммой и бросающим на нее тень, для того, чтобы можно было выделить этот символ среди других обозначений:
Рис. 3. Внешняя сущность.
2. Системы и подсистемы
При построении модели сложной ИС она может быть представлена в самом общем виде на так называемой контекстной диаграмме в виде одной системы как единого целого, либо может быть декомпозирована на ряд подсистем.
Подсистема (или система) на контекстной диаграмме изображается следующим образом (рисунок 4).
Рис. 4. Подсистема.
Номер подсистемы служит для ее идентификации. В поле имени вводится наименование подсистемы в виде предложения с подлежащим и соответствующими определениями и дополнениями.
3. Процессы
Процесс представляет собой преобразование входных потоков данных в выходные в соответствии с определенным алгоритмом. Физически процесс может быть реализован различными способами: это может быть подразделение организации (отдел), выполняющее обработку входных документов и выпуск отчетов, программа, аппаратно реализованное логическое устройство и т.д.
Процесс на диаграмме потоков данных изображается, как показано на рисунке 5.
Рис. 5. Процесс.
Номер процесса служит для его идентификации. В поле имени вводится наименование процесса в виде предложения с активным недвусмысленным глаголом в неопределенной форме (вычислить, рассчитать, проверить, определить, создать, получить), за которым следуют существительные в винительном падеже, например: "Ввести сведения о клиентах"; "Выдать информацию о текущих расходах"; "Проверить кредитоспособность клиента".
Использование таких глаголов, как "обработать", "модернизировать" или "отредактировать" означает, как правило, недостаточно глубокое понимание данного процесса и требует дальнейшего анализа.
Информация в поле физической реализации показывает, какое подразделение организации, программа или аппаратное устройство выполняет данный процесс.
4. Накопители данных
Накопитель данных представляет собой абстрактное устройство для хранения информации, которую можно в любой момент поместить в накопитель и через некоторое время извлечь, причем способы помещения и извлечения могут быть любыми.
Накопитель данных может быть реализован физически в виде ящика в картотеке, таблицы в оперативной памяти, файла на магнитном носителе и т.д. Накопитель данных на диаграмме потоков данных изображается, как показано на рисунке 6.
Рис. 6. Накопитель данных.
Накопитель данных идентифицируется буквой "D" и произвольным числом. Имя накопителя выбирается из соображения наибольшей информативности для проектировщика.
Накопитель данных в общем случае является прообразом будущей базы данных и описание хранящихся в нем данных должно быть увязано с информационной моделью (ERD).
5. Потоки данных
Поток данных определяет информацию, передаваемую через некоторое соединение от источника к приемнику. Реальный поток данных может быть информацией, передаваемой по кабелю между двумя устройствами, пересылаемыми по почте письмами, магнитными лентами или дискетами, переносимыми с одного компьютера на другой и т.д.
Поток данных на диаграмме изображается линией, оканчивающейся стрелкой, которая показывает направление потока (рисунок 7). Каждый поток данных имеет имя, отражающее его содержание.
Рис. 7. Поток данных.
6. Построение иерархии диаграмм потоков данных
При построении иерархии потоков данных целесообразно пользоваться следующими рекомендациями:
Размещать на каждой диаграмме от 3 до 6-7 процессов. Верхняя граница соответствует человеческим возможностям одновременного восприятия и понимания структуры сложной системы с множеством внутренних связей, нижняя граница выбрана по соображениям здравого смысла: нет необходимости детализировать процесс диаграммой, содержащей всего один или два процесса.
Не загромождать диаграммы не существенными на данном уровне деталями.
Декомпозицию потоков данных осуществлять параллельно с декомпозицией процессов. Эти две работы должны выполняться одновременно, а не после завершения другой.
Выбирать ясные, отражающие суть дела имена процессов и потоков, при этом стараться не использовать аббревиатуры.
Первым шагом при построении иерархии DFD является построение контекстных диаграмм. Обычно при проектировании относительно простых ИС строится единственная контекстная диаграмма со звездообразной топологией, в центре которой находится так называемый главный процесс, соединенный с приемниками и источниками информации, посредством которых с системой взаимодействуют пользователи и другие внешние системы. Количество потоков на контекстной диаграмме должно быть по возможности небольшим, поскольку каждый из них может быть в дальнейшем разбит на несколько потоков на следующих уровнях диаграммы.
Внешние сущности выделяются по отношению к основному процессу. Для их определения необходимо выделить поставщиков и потребителей основного процесса, т.е. все объекты, которые взаимодействуют с основным процессом. На этом этапе описание взаимодействия заключается в выборе глагола, дающего представление о том, как внешняя сущность использует основной процесс или используется им. Например, основной процесс – "учет обращений граждан", внешняя сущность – "граждане", описание взаимодействия – "подает заявления и получает ответы". Этот этап является принципиально важным, поскольку именно он определяет границы моделируемой системы.
Для всех внешних сущностей строится таблица событий, описывающая их взаимодействие с основным потоком. Таблица событий включает в себя наименование внешней сущности, событие, его тип (типичный для системы или исключительный, реализующийся при определенных условиях) и реакцию системы.
Если же для сложной системы ограничиться единственной контекстной диаграммой, то она будет содержать слишком большое количество источников и приемников информации, которые трудно расположить на листе бумаги нормального формата, и кроме того, единственный главный процесс не раскрывает структуры распределенной системы. Признаками сложности (в смысле контекста) могут быть:
наличие большого количества внешних сущностей (десять и более);
распределенная природа системы;
многофункциональность системы с уже сложившейся или выявленной группировкой функций в отдельные подсистемы.
Для сложных ИС строится иерархия контекстных диаграмм. При этом контекстная диаграмма верхнего уровня содержит не единственный главный процесс, а набор подсистем, соединенных потоками данных. Контекстные диаграммы следующего уровня детализируют контекст и структуру подсистем.
Иерархия контекстных диаграмм определяет взаимодействие основных функциональных подсистем проектируемой ИС как между собой, так и с внешними входными и выходными потоками данных и внешними объектами (источниками и приемниками информации), с которыми взаимодействует ИС.
Разработка контекстных диаграмм решает проблему строгого определения функциональной структуры ИС на самой ранней стадии ее проектирования, что особенно важно для сложных многофункциональных систем, в разработке которых участвуют разные организации и коллективы разработчиков.
После построения контекстных диаграмм полученную модель следует проверить на полноту исходных данных об объектах системы и изолированность объектов (отсутствие информационных связей с другими объектами).
Для каждой подсистемы, присутствующей на контекстных диаграммах, выполняется ее детализация при помощи DFD. Каждый процесс DFD, в свою очередь, может быть детализирован при помощи DFD или миниспецификации. При детализации должны выполняться следующие правила:
правило балансировки - означает, что при детализации подсистемы или процесса детализирующая диаграмма в качестве внешних источников/приемников данных может иметь только те компоненты (подсистемы, процессы, внешние сущности, накопители данных), с которыми имеет информационную связь детализируемая подсистема или процесс на родительской диаграмме;
правило нумерации - означает, что при детализации процессов должна поддерживаться их иерархическая нумерация. Например, процессы, детализирующие процесс с номером 12, получают номера 12.1, 12.2, 12.3 и т.д.
Миниспецификация (описание логики процесса) должна формулировать его основные функции таким образом, чтобы в дальнейшем специалист, выполняющий реализацию проекта, смог выполнить их или разработать соответствующую программу.
Миниспецификация является конечной вершиной иерархии DFD. Решение о завершении детализации процесса и использовании миниспецификации принимается аналитиком исходя из следующих критериев:
наличия у процесса относительно небольшого количества входных и выходных потоков данных (2-3 потока);
возможности описания преобразования данных процессом в виде последовательного алгоритма;
выполнения процессом единственной логической функции преобразования входной информации в выходную;
возможности описания логики процесса при помощи миниспецификации небольшого объема (не более 20-30 строк).
Спецификации должны удовлетворять следующим требованиям:
Для каждого процесса нижнего уровня должна существовать одна и только одна спецификация.
Спецификация должна определять способ преобразования входных потоков в выходные.
Нет необходимости (по крайней мере на стадии формирования требований) определять метод реализации этого преобразования.
Спецификация должна стремиться к ограничению избыточности - не следует переопределять то, что уже было определено на диаграмме.
Набор конструкций для построения спецификаций должен быть простым и понятным.
Фактически спецификации представляют собой описания алгоритмов задач, выполняемых процессами. Спецификации содержат:
Номер и/или имя процесса.
Списки входных и выходных данных.
Тело (описание процесса), являющееся спецификацией алгоритма или операции, трансформирующей входные потоки данных в выходные.
При построении иерархии диаграмм потоков данных переходить к детализации процессов следует только после определения содержания всех потоков и накопителей данных, которое описывается при помощи структур данных. Для каждого потока данных формируется список всех его элементов данных, затем элементы данных объединяются в структуры данных, соответствующие более крупным объектам данных (например, строкам документов или объектам предметной области). Каждый объект должен состоять из элементов, являющихся его атрибутами. Структуры данных могут содержать альтернативы, условные вхождения и итерации.
Условное вхождение означает, что данный компонент может отсутствовать в структуре (например, структура «данные о страховании» для объекта «служащий»).
Альтернатива означает, что в структуру может входить один из перечисленных элементов.
Итерация означает вхождение любого числа элементов в указанном диапазоне (например, элемент «имя ребенка» для объекта «служащий»).
Для каждого элемента данных может указываться его тип (непрерывные или дискретные данные). Для непрерывных данных может указываться единица измерения (кг, см и т.п.), диапазон значений, точность представления и форма физического кодирования. Для дискретных данных может указываться таблица допустимых значений.
После декомпозиции основного процесса для каждого подпроцесса строится аналогичная таблица внутренних событий.
Следующим шагом после определения полной таблицы событий выделяются потоки данных, которыми обмениваются процессы и внешние сущности. Простейший способ их выделения заключается в анализе таблиц событий. События преобразуются в потоки данных от инициатора события к запрашиваемому процессу, а реакции – в обратный поток событий. После построения входных и выходных потоков аналогичным образом строятся внутренние потоки. Для их выделения для каждого из внутренних процессов выделяются поставщики и потребители информации. Если поставщик или потребитель информации представляет процесс сохранения или запроса информации, то вводится хранилище данных, для которого данный процесс является интерфейсом.
После построения законченной модели системы ее необходимо верифицировать (проверить на полноту и согласованность). В полной модели все ее объекты (подсистемы, процессы, потоки данных) должны быть подробно описаны и детализированы. Выявленные недетализированные объекты следует детализировать, вернувшись на предыдущие шаги разработки. В согласованной модели для всех потоков данных и накопителей данных должно выполняться правило сохранения информации: все поступающие куда-либо данные должны быть считаны, а все считываемые данные должны быть записаны. Непротиворечивость системы обеспечивается выполнением наборов формальных правил о возможных типах процессов: на диаграмме не может быть потока, связывающего две внешние сущности – это взаимодействие удаляется из рассмотрения; ни одна сущность не может непосредственно получать или отдавать информацию в хранилище данных – хранилище данных является пассивным элементом, управляемым с помощью интерфейсного процесса; два хранилища данных не могут непосредственно обмениваться информацией – эти хранилища должны быть объединены.
Кроме основных элементов, в состав DFD входят словари данных, которые являются каталогами всех элементов данных, присутствующих в DFD, включая групповые и индивидуальные потоки данных, хранилища и процессы, а также все их атрибуты.
К преимуществам методики DFD относятся:
возможность однозначно определить внешние сущности, анализируя потоки информации внутри и вне системы;
возможность проектирования сверху вниз, что облегчает построение модели "как должно быть";
наличие спецификаций процессов нижнего уровня, что позволяет преодолеть логическую незавершенность функциональной модели и построить полную функциональную спецификацию разрабатываемой системы.
К недостаткам модели отнесем: необходимость искусственного ввода управляющих процессов, поскольку управляющие воздействия (потоки) и управляющие процессы с точки зрения DFD ничем не отличаются от обычных; отсутствие понятия времени, т.е. отсутствие анализа временных промежутков при преобразовании данных (все ограничения по времени должны быть введены в спецификациях процессов).
На рис. 8 приведен пример DFD-схемы бизнес-процесса "Оформлении и выдача трудовой книжки сотруднику при увольнении", разработанной в нотации Гейна-Сарсона, а на рис. 9 - в нотации Йордона-Де Марко.
Рис. 8. DFD-схема бизнес-процесса "Оформлении и выдача трудовой книжки сотруднику при увольнении" в нотации Гейна-Сарсона.
Рис. 9. DFD-схема бизнес-процесса "Оформлении и выдача трудовой книжки сотруднику при увольнении" в нотации Йордона-Де Марко.
IDEF3
Этот метод предназначен для моделирования последовательности выполнения действий и взаимозависимости между ними в рамках процессов. Модели IDEF3 могут использоваться для детализации функциональных блоков IDEF0, не имеющих диаграмм декомпозиции.
Нотация IDEF3 использует категорию Сценариев (Scenario) для упрощения структуры описаний сложного многоэтапного процесса. IDEF3 осуществляет реализацию следующей информации о процессе:
объекты, участвующие в описании операции;
функции, которые выполняют эти объекты;
взаимосвязь между процессами;
состояния и изменения, которым подвергаются объекты;
время выполнения и контрольные точки синхронизации работ;
ресурсы, необходимые для выполнения работ.
Существует два типа диаграмм в стандарте IDEF3:
PFDD - диаграммы описания последовательности этапов процесса.
OSTN - диаграммы состояния объекта и его изменений в процессе.
На рис. 10 изображена диаграмма PFDD, показывающая процессы создания программного обеспечения. Прямоугольники на диаграмме PFDD называются функциональными элементами или элементами поведения (UOB) и обозначают событие, стадию процесса или принятие решения (рис. 11). Каждый UOB имеет конкретное имя (функция, процесс, действие, акт, событие, сценарий, процедура, операция, решение), отображаемое в глагольном наклонении и уникальный номер (номер действия обычно предваряется номером его родителя, например, 1.1.). В правом нижнем углу UOB элемента располагается ссылка на какие-либо элементы функциональной модели IDEF0 или на отделы, конкретных исполнителей, выполняющие конкретный процесс.
Рис. 10. PFDD-диаграмма создания электронной программы.
Рис. 11. Функциональный элемент (UOB).
Стрелки или линии являются отображением хода выполнения операций между UOB-блоками в ходе процесса (рис. 12).
а) б) в)
Рис. 12. Стрелки для отображения хода выполнения операции
Линии в нотации IDEF3 бывают следующих видов:
Временное предшествование или старшая (Temporal precedence, рис. 12, а) - сплошная линия, связывающая UOB. Рисуется слева направо или сверху вниз. Исходное действие должно завершиться, прежде чем конечное действие сможет начаться.
Нечеткое отношение (Relationship link, рис. 12, б) - пунктирная линия, использующаяся для изображения связей между UOB в том случае, если конечное действие сможет начаться и даже завершиться до того момента, когда завершится исходное действие.
Объектный поток (Object flow, рис. 12, в) - стрелка с двумя наконечниками используется для описания того факта, что объект (деталь) используется в двух или более единицах работы, например, когда объект порождается в одной работе и используется в другой (т.е. выход исходного действия является входом конечного действия). Исходное действие должно завершиться, прежде чем конечное действие сможет начаться. Наименования потоковых связей должны чётко идентифицировать объект, который передается с их помощью.
Все связи в IDEF3 являются однонаправленными.
Завершение одного действия может инициировать начало выполнения сразу нескольких других действий, или наоборот, определенное действие может требовать завершения нескольких других действий до начала своего выполнения (ветвление процесса). Ветвление процесса отражается с помощью специальных блоков, называемых перекрестками. Каждый перекресток (Junction) имеет свой определенный идентификационный номер (на рис. 10 J1 - перекресток). Перекресток не может использоваться одновременно для слияния и для разветвления. При вводе перекрестка в диаграмму необходимо указать тип перекрестка. Типы перекрестков представлены в таблице 2.
Таблица 2. Описание типов перекрестков.
Обозначение |
Наименование |
Смысл для стрелок слияния |
Смыл для стрелок разветвления |
|
Асинхронное И (asynchronous AND) |
Все предшествующие процессы должны быть завершены |
Все следующие процессы должны быть запущены |
|
Cинхронное И (synchronous AND) |
Все предшествующие процессы завершены одновременно |
Все следующие процессы запускаются одновременно |
|
Асинхронное ИЛИ (asynchronous OR) |
Один или несколько предшествующих процессов должны быть завершены |
Один или несколько следующих процессов должны быть запущены |
|
Синхронное ИЛИ (synchronous OR) |
Один или несколько предшествующих процессов завершаются одновременно |
Один или несколько следующих процессов запускаются одновременно |
|
Исключающее ИЛИ (XOR, exclusive OR) |
Только один предшествующий процесс завершен |
Только один следующий процесс запускается |
Пояснение: Перекресток "Исключающий ИЛИ" обозначает, что после завершения работы "A" (рис. 13), начинает выполняться только одна из трех расположенных параллельно работ B, С или D в зависимости от условий 1, 2 и 3. Перекресток "И" обозначает, что после завершения работы "A", начинают выполняться одновременно три параллельно расположенные работы B, С и D. Перекресток "ИЛИ" обозначает, что после завершения работы "A", может запуститься любая комбинация трех параллельно расположенных работ B, С и D. Например может запуститься только одна из них, могут запуститься три работы, а также могут запуститься двойные комбинации В и С, либо C и D, либо B и D. Перекресток "Исключающий ИЛИ" является самым неопределенным, так как предполагает несколько возможных сценариев реализации бизнес-процесса и применяется для описания слабо формализованных ситуаций.
Рис. 13. Применение перекрестков "Исключающий ИЛИ", "И" и "ИЛИ" - схемы расхождения.
4
Рис. 14. Применение перекрестков "Исключающий ИЛИ", "И" и "ИЛИ" - схемы схождения.
Сценарий, отображаемый на диаграмме (рис. 10), можно описать в следующем виде. Программный код, подготовленный к компиляции, компилируется в компиляторе программ. В процессе компиляции создается исполнительный файл программы. После этого, производится тестирование программы, после которой начинается этап проверки программного продукта. Если тест подтверждает недостаточное качество программы, то она заново пропускается через этап создания программного кода. Если программа успешно проходит контроль качества, то она отправляется пользователю.
Метод IDEF3 позволяет декомпозировать действие несколько раз, что обеспечивает документирование альтернативных потоков процесса в одной модели.
Каждый функциональный блок UOB может иметь последовательность декомпозиций. Номера UOB дочерних диаграмм имеют сквозную нумерацию, т.е., если родительский UOB имеет номер "1", то блоки UOB на его декомпозиции будут соответственно иметь номера "1.1", "1.2" и т.д.
Если диаграммы PFDD представляют технологический процесс "С точки зрения наблюдателя", то другой класс диаграмм IDEF3 - OSTN позволяет рассматривать тот же самый процесс "С точки зрения объекта". На рис. 15 представлено отображение процесса создания электронной программы с точки зрения OSTN диаграммы. Состояния объекта (в нашем случае электронной программы) и изменение состояния являются ключевыми понятиями OSTN диаграммы. Состояния объекта отображаются окружностями, а их изменения направленными линиями. Каждая линия имеет ссылку на соответствующий функциональный блок UOB, в результате которого произошло отображаемое ей изменение состояния объекта.
Рис. 15. Пример OSTN-диаграммы создания электронной программы.
На схеме бизнес-процесса также можно использовать такой элемент как "объект- ссылка", который связывается с работами и перекрестками. Ссылки обеспечивают более полное понимание, дополнительный смысл и упрощение описания процесса. Ссылки позволяют:
обращаться к ранее определенному действию;
организовывать циклы;
уточнять работу перекрестков;
связывать элементы диаграммы с каким-либо внешним объектом;
комментировать различные элементы диаграммы.
Ссылка изображается в виде прямоугольника. В верхней его части указывается тип ссылки и ее имя.
Рис. 16. Ссылки
Ссылки могут быть различного типа. Список типов ссылок приведен в таблице 3.
Таблица 3. Типы ссылок.
Тип ссылки |
Назначение |
OBJECT |
Описывает участие важного объекта в действии. |
GOTO |
Позволяет применять на диаграмме циклический переход. В том случае, когда все действия цикла находятся в рамках одной диаграммы, цикл можно изобразить стрелкой, которая будет указывать на начало цикла. Тогда ссылка будет связана с перекрестком, управляющим циклом. |
UOB |
Предназначена для многократного вызова какого-либо действия в рамках одной модели |
NOTE |
Позволяет прокомментировать присутствие какого-либо элемента на диаграмме. |
ELAB (elaboration) |
Применяется для уточнения использования ветвления стрелок на перекрестках. |
Примеры использования ссылок различного типа приведены на рис. 17.
Рис. 17. Примеры использования ссылок различного типа
Таким образом, можно сделать следующие выводы по практическому использованию: применение универсальных графических языков моделирования IDEF0, IDEF3 и DFD обеспечивает логическую целостность и полноту описания, необходимую для достижения точных и непротиворечивых результатов на этапе анализа.
По диаграммам делаем следующий вывод: наиболее существенное различие между разновидностями структурного анализа заключается в их функциональности.
Модели SADT (IDEF0) наиболее удобны при построении функциональных моделей. Они наглядно отражают функциональную структуру объекта: производимые действия, связи между этими действиями. Таким образом, четко прослеживается логика и взаимодействие процессов организации. Главным достоинством нотации является возможность получить полную информацию о каждой работе, благодаря ее жестко регламентированной структуре. С ее помощью можно выявить все недостатки, касающиеся как самого процесса, так и то, с помощью чего он реализуется: дублирование функций, отсутствие механизмов, регламентирующих данный процесс, отсутствие контрольных переходов и т.д.
DFD позволяет проанализировать информационное пространство системы и используется для описания документооборота и обработки информации. Поэтому, диаграммы DFD применяют в качестве дополнения модели бизнес-процессов, выполненной в IDEF0.
IDEF3 хорошо приспособлен для сбора данных, требующихся для проведения анализа системы с точки зрения рассогласования/согласования процессов во времени.
Нельзя говорить о достоинствах и недостатках отдельных нотаций. Возможны ситуации, при которых анализ IDEF0 не обнаружил недостатков в деятельности организации с точки зрения технологического или производственного процесса, однако это не является гарантией отсутствия ошибок. Поэтому в следующем этапе анализа необходимо перейти к исследованию информационных потоков с помощью DFD и затем объединить эти пространства с помощью последней нотации - IDEF3.
Сегодня для описания функциональности разработки предлагаются различные инструменты. Например:
CASE-средство AllFusion Process Modeler (BPwin) компании Computer Associates. AllFusion Process Modeler наряду с ERwin Data Modeler (ERwin), входит в состав пакета программных средств AllFusion Modeling Suite. Основным преимуществом данного инструмента является присутствие нотаций IDEF и DFD, которые распространены в российских компаниях и принята в России на уровне стандарта, а также связь с продуктом Erwin, используемым для проектирования структур данных.
CASE-средство Silverrun американской фирмы Сomputer Systems Advisers, Inc. (CSA) используется для анализа и проектирования ИС бизнес-класса [22] и ориентировано в большей степени на спиральную модель ЖЦ. Оно применимо для поддержки любой методологии, основанной на раздельном построении функциональной и информационной моделей (диаграмм потоков данных и диаграмм "сущность-связь").
CASE.Аналитик 1.1 является практически единственным в настоящее время конкурентоспособным отечественным CASE-средством функционального моделирования. Его основные функции:
построение и редактирование DFD;
анализ диаграмм и проектных спецификаций на полноту и непротиворечивость;
получение разнообразных отчетов по проекту;
генерация макетов документов в соответствии с требованиями ГОСТ 19.ХХХ и 34.ХХХ.
Также существует множество программ для рисования различных диаграмм, например, MS Visio или Dia.
Лекция 5
ТЕМА: Проектирование с использованием метода «сущность-связь».
Литература: 1. Карпова И.П. Проектирование реляционных баз данных //методические указания к курсовому проектированию по курсу "Базы данных"
2. http://citforum.ru/database/dblearn/dblearn08.shtml - Пушников А.Ю. Введение в системы управления базами данных// Учебное пособие.
В реальном проектировании структуры базы данных применяется так называемое, семантическое моделирование. Семантическое моделирование представляет собой моделирование структуры данных, опираясь на смысл этих данных. В качестве инструмента семантического моделирования используются различные варианты диаграмм сущность-связь (ER - Entity-Relationship), которые могут быть относительно легко отображены в любую систему баз данных.
Первый вариант модели сущность-связь был предложен в 1976 г. Питером Пин-Шэн Ченом. В дальнейшем многими авторами были разработаны свои варианты подобных моделей (нотация Мартина, нотация IDEF1X, нотация Баркера и др.). Кроме того, различные CASE-средства используют несколько отличающиеся друг от друга нотации ERD. Одна из наиболее распространенных нотаций предложена Баркером и используется в Oracle Designer. В CASE-средстве SilverRun используется один из вариантов нотации Чена. CASE-средства ERwin, ER / Studio, Design / IDEF используют методологию IDEF 1Х. По сути, все варианты диаграмм сущность-связь исходят из одной идеи - рисунок всегда нагляднее текстового описания. Все такие диаграммы используют графическое изображение сущностей предметной области, их свойств (атрибутов), и взаимосвязей между сущностями.
Мы опишем работу с ER-диаграммами близко к нотации Баркера, как довольно легкой в понимании основных идей.
Основные понятия er-диаграмм
Определение 1. Сущность (Entity) это реальный или представляемый объект, имеющий существенное значение для рассматриваемой предметной области, информация о котором должна сохраняться и быть доступна.
Каждая сущность должна иметь уникальное наименование, выраженное существительным в единственном числе. Примерами сущностей могут быть такие классы объектов как "Поставщик", "Сотрудник", "Накладная". Каждая сущность в модели изображается в виде прямоугольника с наименованием (рис.1). При этом имя сущности – это имя типа, а не некоторого конкретного экземпляра этого типа.
Рис. 1. Обозначение сущности.
Для сущностей различают тип сущности и экземпляр. Тип характеризуется именем и списком свойств, а экземпляр – конкретными значениями свойств.
Определение 2. Экземпляр сущности это конкретный представитель данной сущности.
Например, представителем сущности "Сотрудник" может быть "Сотрудник Иванов".
Каждый экземпляр сущности должен быть отличим от любого другого экземпляра той же сущности, т.е. сущности должны иметь некоторые свойства, уникальные для каждого экземпляра этой сущности (это требование в некотором роде аналогично требованию отсутствия кортежей-дубликатов в реляционных таблицах).
Типы сущностей можно классифицировать как сильные и слабые. Сильные сущности существуют сами по себе, а существование слабых сущностей зависит от существования сильных. Например, читатель библиотеки – сильная сущность, а абонемент этого читателя – слабая, которая зависит от наличия соответствующего читателя. Слабые сущности называют подчинёнными (дочерними), а сильные – базовыми (основными, родительскими).
Также сущности разделяют на независимые и зависимые. Сущность является независимой, если каждый экземпляр ее может быть однозначно идентифицирован без определения его отношений с другими сущностями. Независимая сущность изображается прямоугольником с четко выраженными углами. Сущность является зависимой, если однозначная идентификация экземпляра сущности зависит от его отношения к другой сущности. Зависимая сущность изображается прямоугольником со скругленными углами.
Сущности бывают как физически существующие (например, СОТРУДНИК или АВТОМОБИЛЬ), так и абстрактные (например, ЭКЗАМЕН или ДИАГНОЗ).
Каждая сущность может обладать любым количеством связей с другими сущностями модели.
Определение 3. Атрибут сущности - это именованная характеристика, являющаяся некоторым значимым для рассматриваемой предметной области свойством сущности.
Наименование атрибута должно быть выражено существительным в единственном числе (возможно, с характеризующими прилагательными).
Примерами атрибутов сущности "Сотрудник" могут быть такие атрибуты как "Табельный номер", "Фамилия", "Имя", "Отчество", "Должность", "Зарплата" и т.п.
Атрибуты изображаются в пределах прямоугольника определяющего сущность, причем каждый атрибут занимает отдельную строку, и отделяются от названия сущности линией (рис. 2).
Рис. 2. Указание атрибутов сущности.
Рядом с именем атрибута можно приводить примеры значений данного атрибута.
Различают:
Идентифицирующие и описательные атрибуты. Идентифицирующие атрибуты имеют уникальное значение для сущностей данного типа и являются потенциальными ключами. Они позволяют однозначно распознавать экземпляры сущности. Из потенциальных ключей выбирается один первичный ключ. В качестве первичного ключа обычно выбирается потенциальный ключ, по которому чаще происходит обращение к экземплярам сущности. Кроме того, ПК должен включать в свой состав минимально необходимое для идентификации количество атрибутов. Остальные атрибуты называются описательными и заключают в себе интересующие свойства сущности.
Составные и простые атрибуты. Простой атрибут состоит из одного компонента, его значение неделимо. Составной атрибут является комбинацией нескольких компонентов, возможно, принадлежащих разным типам данных (например, ФИО или адрес). Решение о том, использовать составной атрибут или разбивать его на компоненты, зависит от характера его обработки и формата пользовательского представления этого атрибута.
Однозначные и многозначные атрибуты. Могут иметь соответственно одно или много значений для каждого экземпляра сущности. На диаграмме изображаются с двойным подчеркиванием.
Основные и производные атрибуты. Значение основного атрибута не зависит от других атрибутов. Значение производного атрибута вычисляется на основе значений других атрибутов (например, возраст студента вычисляется на основе даты его рождения и текущей даты).
Спецификация атрибута состоит из его названия, указания типа данных и описания ограничений целостности – множества значений (или домена), которые может принимать данный атрибут.
Атрибут может быть либо обязательным, либо необязательным. Обязательность означает, что атрибут не может принимать неопределенных значений (Null). Обязательный атрибут помечается звездочкой, а необязательный кружком (рис. 3).
Рис. 3. Указание обязательных и необязательных атрибутов сущности.
Определение 4. Ключ сущности - это неизбыточный набор атрибутов, значения которых в совокупности являются уникальными для каждого экземпляра сущности. Неизбыточность заключается в том, что при удалении любого атрибута из ключа нарушается его уникальность.
Сущность может иметь несколько различных ключей. Различают первичный, альтернативный и внешний ключи.
Первичный ключ (Primary Key) - это атрибут или группа атрибутов, используемых для однозначной идентификации экземпляра сущности. На диаграмме атрибуты первичного ключа размещаются первыми в списке атрибутов и подчеркиваются или предваряются # (рис. 4):
Рис. 4. Указание ключевого атрибута.
Альтернативный ключ (Alternate Key) - потенциальный ключ, не ставший первичным. На диаграмме альтернативный ключ обозначается AK n. m, где n - порядковый номер ключа, m - порядковый номер атрибута в ключе.
Внешние ключи (Foreign Key) создаются, когда сущности соединяются связью при построении физических ER-диаграмм. Происходит миграция атрибутов первичного ключа родительской сущности в дочернюю сущность. Появившийся таким образом в дочерней сущности атрибут будет являться внешним ключом. (Из родительской сущности атрибут не исчезает, а просто копируется в дочерней сущности). Внешний ключ обозначается ВК.
Первичный ключ может быть абсолютный или относительный. Если все атрибуты, составляющие первичный ключ, принадлежат сущности, то он является абсолютным. Если один или более атрибутов первичного ключа принадлежат другой сущности, то он является относительным. Когда первичный ключ является относительным, сущность определяется как зависимая сущность, поскольку ее идентификатор зависит от другой сущности. В примере на рисунке 5 первичный ключ сущности Компания является относительным. Он включает первичный ключ сущности Список компаний.
Рис. 5. Относительный первичный ключ.
Определение 5. Связь - это графически изображаемая ассоциация, устанавливаемая между двумя сущностями, значимая для рассматриваемой предметной области.
Эта ассоциация всегда является бинарной и может существовать между двумя разными сущностями или между сущностью и ей же самой (рекурсивная связь).
Определение связи в методе Баркера несколько отличается от данного Ченом.
Определение 5.1. Связь - это ассоциация между сущностями, при которой, как правило, каждый экземпляр одной сущности, называемой родительской сущностью, ассоциирован с произвольным (в том числе и нулевым) количеством экземпляров второй сущности, называемой сущностью-потомком, а каждый экземпляр сущности-потомка ассоциирован в точности с одним экземпляром сущности-родителя. Таким образом, экземпляр сущности-потомка может существовать только при существовании сущности-родителя.
Связи позволяют по одной сущности находить другие сущности, связанные с нею.
В любой связи выделяются два конца (в соответствии с существующей парой связываемых сущностей), на каждом из которых указывается:
имя конца связи,
тип конца связи (сколько экземпляров данной сущности связывается),
обязательность связи (т.е. любой ли экземпляр данной сущности должен участвовать в данной связи).
Связь представляется в виде линии, связывающей две сущности или ведущей от сущности к ней же самой. При этом в месте "стыковки" связи с сущностью используется трехточечный вход в прямоугольник сущности, если для этой сущности в связи могут использоваться много (many) экземпляров сущности, и одноточечный вход, если в связи может участвовать только один экземпляр сущности. Обязательный конец связи изображается сплошной линией, а необязательный – прерывистой линией (рис. 6).
Рис. 6. Изображение связи между сущностями.
Наименование связи обычно выражается в неопределенной глагольной форме: "иметь", "принадлежать" и т.п. Каждое из наименований относится к своему концу связи. Иногда наименования не пишутся ввиду их очевидности. Имя каждой связи между двумя сущностями должно быть уникальным, но имена связей в модели не обязаны быть уникальными. Имя связи всегда формируется с точки зрения родителя, так что может быть образовано предложение соединением имени сущности-родителя, имени связи, выражения степени и имени сущности-потомка.
Каждая связь может иметь один из следующих типов связи (рис. 7):
Рис. 7. Типы связей и их изображение на диаграмме.
Связь типа один-к-одному означает, что один экземпляр первой сущности (левой) связан с одним экземпляром второй сущности (правой). Связь один-к-одному чаще всего свидетельствует о том, что на самом деле мы имеем всего одну сущность, неправильно разделенную на две.
Связь типа один-ко-многим означает, что один экземпляр первой сущности (левой) связан с несколькими экземплярами второй сущности (правой). Это наиболее часто используемый тип связи. Левая сущность (со стороны "один") называется родительской, правая (со стороны "много") дочерней. Характерный пример такой связи приведен на рис. 6.
Связь типа много-ко-многим означает, что каждый экземпляр первой сущности может быть связан с несколькими экземплярами второй сущности, и каждый экземпляр второй сущности может быть связан с несколькими экземплярами первой сущности. Тип связи много-ко-многим является временным типом связи, допустимым на ранних этапах разработки модели. В дальнейшем этот тип связи должен быть заменен двумя связями типа один-ко-многим путем создания промежуточной сущности.
Каждая связь может иметь одну из двух модальностей связи (рис. 8):
Рис. 8. Модальность связи и ее изображение на диаграмме.
Модальность "может" означает, что экземпляр одной сущности может быть связан с одним или несколькими экземплярами другой сущности, а может быть и не связан ни с одним экземпляром.
Модальность "должен" означает, что экземпляр одной сущности обязан быть связан не менее чем с одним экземпляром другой сущности.
Связь может иметь разную модальность с разных концов (как на рис. 6).
Тип связи в совокупности с модальностью называют еще кардинальностью связи (табл. 1):
Таблица 1. Кардинальность связи.
Обозначение |
Кардинальность |
|
0,1 |
|
1,1 |
|
0,N |
|
1,N |
Описанный графический синтаксис позволяет однозначно читать диаграммы, пользуясь следующей схемой построения фраз:
<Каждый экземпляр СУЩНОСТИ 1> <МОДАЛЬНОСТЬ СВЯЗИ> <НАИМЕНОВАНИЕ СВЯЗИ> <ТИП СВЯЗИ> <экземпляр СУЩНОСТИ 2>.
Каждая связь может быть прочитана как слева направо, так и справа налево. Связь на рис. 6 читается так:
Слева направо: "каждый сотрудник может иметь несколько детей".
Справа налево: "Каждый ребенок обязан принадлежать ровно одному сотруднику".
На следующем примере (рис. 9) изображена рекурсивная связь, связывающая сущность ЧЕЛОВЕК с ней же самой. Конец связи с именем "являться сыном" определяет тот факт, что один человек может быть сыном не более чем одному отцу. Конец связи с именем "являться отцом" означает, что один человек может являться отцом для одного или более ЛЮДЕЙ (т.е не каждый человек имеет детей).
Рисунок 9. Пример рекурсивной связи.
Более сложные элементы er-модели
Ранее были представлены только основные и наиболее очевидные понятия ER-модели данных. К числу более сложных элементов модели относятся следующие:
Подтипы и супертипы сущностей. Как в языках программирования с развитыми типовыми системами (например, в языках объектно-ориентированного программирования), вводится возможность наследования типа сущности, исходя из одного или нескольких супертипов. Интересные нюансы связаны с необходимостью графического изображения этого механизма.
Уточняемые степени связи. Иногда бывает полезно определить возможное количество экземпляров сущности, участвующих в данной связи (например, служащему разрешается участвовать не более, чем в трех проектах одновременно). Для выражения этого семантического ограничения разрешается указывать на конце связи ее максимальную или обязательную степень.
Каскадные удаления экземпляров сущностей. Некоторые связи бывают настолько сильными (конечно, в случае связи "один-ко-многим"), что при удалении опорного экземпляра сущности (соответствующего концу связи "один") нужно удалить и все экземпляры сущности, соответствующие концу связи "многие". Соответствующее требование "каскадного удаления" можно сформулировать при определении сущности.
Домены. Как и в случае реляционной модели данных бывает полезна возможность определения потенциально допустимого множества значений атрибута сущности (домена).
Эти и другие более сложные элементы модели данных "Сущность-Связи" делают ее существенно более мощной, но одновременно несколько усложняют ее использование.
Разработка er - моделей.
Семантическое моделирование начинается с разбивки предметной области на ряд локальных областей, каждая из которых (в идеале) включает в себя информацию, достаточную для обеспечения запросов отдельной группы будущих пользователей или решения отдельной задачи (подзадачи). Каждое локальное представление моделируется отдельно, затем они объединяются.
Выбор локального представления зависит от масштабов предметной области. Обычно она разбивается на локальные области таким образом, чтобы каждая из них соответствовала отдельному внешнему приложению и содержала 6-7 сущностей.
Разработка ERD включает следующие основные этапы:
Идентификация сущностей, их атрибутов, а также первичных и альтернативных ключей.
Идентификация отношений между сущностями и указание типов отношений.
Разрешение неспецифических отношений (отношений многие-ко-многим).
Процесс выделения сущностей, атрибутов и связей является итерационным. Разработав первый приближенный вариант диаграмм, мы уточняем их, опрашивая экспертов предметной области. При этом документацией, в которой фиксируются результаты бесед, являются сами ER-диаграммы.
Как и в реляционных схемах баз данных, в ER -диаграммах вводится понятие нормальных форм, причем их смысл очень близко соответствует смыслу реляционных нормальных форм. Приведем только краткие и неформальные определения трех первых нормальных форм.
В первой нормальной форме ER-схемы устраняются повторяющиеся атрибуты или группы атрибутов, т.е. производится выявление неявных сущностей, "замаскированных" под атрибуты.
Во второй нормальной форме устраняются атрибуты, зависящие только от части уникального идентификатора. Эта часть уникального идентификатора определяет отдельную сущность.
В третьей нормальной форме устраняются атрибуты, зависящие от атрибутов, не входящих в уникальный идентификатор. Эти атрибуты являются основой отдельной сущности.
После того, как созданы локальные представления, выполняется их объединение. При небольшом количестве локальных областей (не более пяти) они объединяются за один шаг. В противном случае обычно выполняют бинарное объединение в несколько этапов.
При объединении проектировщик может формировать конструкции, производные по отношению к тем, которые были использованы в локальных представлениях. Такой подход может преследовать следующие цели:
объединение в единое целое фрагментарных представлений о различных свойствах одного и того же объекта;
введение абстрактных понятий, удобных для решения задач системы, установление их связи с конкретными понятиями, использованными в модели;
образование классов и подклассов подобных объектов (например, класс "изделие" и подклассы типов изделий, производимых на предприятии).
На этапе объединения необходимо выявить и устранить все противоречия. Например, одинаковые названия семантически различных объектов или связей или несогласованные ограничения целостности на одни и те же атрибуты в разных приложениях. Устранение противоречий вызывает необходимость возврата к этапу моделирования локальных представлений с целью внесения в них соответствующих изменений.
По завершении объединения результаты проектирования являют собой концептуальную инфологическую модель предметной области. Модели локальных представлений – это внешние инфологические модели.
Концептуальные и физические er-диаграммы.
Различают концептуальные и физические ER-диаграммы. Концептуальные диаграммы не учитывают особенностей конкретных СУБД. Физические диаграммы строятся по концептуальным и представляют собой прообраз конкретной базы данных. Сущности, определенные в концептуальной диаграмме становятся таблицами, атрибуты становятся колонками таблиц (при этом учитываются допустимые для данной СУБД типы данных и наименования столбцов), связи реализуются путем миграции ключевых атрибутов родительских сущностей и создания внешних ключей.
При правильном определении сущностей, полученные таблицы будут сразу находиться в 3НФ.
Лекция 9.
ТЕМА: Определение языка разработки, среды реализации, инструментов разработки.
Литература: 1. http://nit.miem.edu.ru/sbornik/2009/plen/008.html - Чернышов Л.Н. Среды разработки программного обеспечения: история и перспективы.
2. http://mf.grsu.by/Kafedry/kaf001/academic_process/048/invisible/25 - Кадан А.М. Современные системы программирования. Их использование при разработке программного обеспечения.
3. Алексей Гультяев. Виртуальные машины. Несколько компьютеров в одном.
Программная среда разработки пользовательской программы
Программную среду (программное окружение) разработки пользовательской программы составляет совокупность программных средств (системных программ), используемых при создании и исполнении программы в данной аппаратно-операционной среде.
В понятие аппаратно-операционной среды входит набор устройств компьютера и средств операционной системы. Основные устройства персонального компьютера:
Процессор - выполняет выборку команд программы, выборку аргументов команды, ее исполнение и отсылку на запоминание полученных результатов.
Оперативная память - служит для хранения кода программы и ее данных. Представляет собой последовательность перенумерованных элементов (слов, байтов, битов), номер - адрес элемента. Оперативная память выделяется программе и ее данным только на время исполнения программы.
Внешняя память - предназначена для долговременного хранения большого объема информации. Информация, хранящаяся во внешней памяти, используется процессором только через оперативную память; для обмена информацией между этими двумя видами памяти имеются специальные команды.
Внешние устройства (клавиатура, дисплей, принтер и др.) - служат для взаимодействия компьютера с пользователем и другими устройствами.
Управлением всеми устройствами компьютера занимается операционная система (например, DOS, Windows и т.п.). Как и всякая программная система, она состоит из набора компонент (программ и данных). Основное отличие операционной системы от других программных систем - исполнение ее программ инициируется сигналами (прерываниями), поступающими от устройств компьютера. В свою очередь, программы операционной системы вырабатывают сигналы, заставляющие эти устройства выполнять "пользовательские" программы в соответствии с определенными правилами, определяемыми в данной операционной системе.
В целом аппаратные устройства и операционные средства создают ту операционную среду, в которой работают системные и пользовательские программы.
Среда разработки программного обеспечения - совокупность программных средств, используемая программистами для разработки программного обеспечения. Системы программирования представляют собой единство средств статической (инструментальной) и динамической (исполнительной) поддержки. Простая среда разработки включает в себя:
редактор текста с подсветкой синтаксиса конкретного языка программирования - в нем программист пишет текст программы, так называемый программный код;
компилятор и/или интерпретатор - транслирует программу, написанную на высокоуровневом языке программирования в машинный язык (машинный код), непосредственно понятный компьютеру. Язык С++ относится к компилируемым языкам, поэтому для обработки текстов его программ служит компилятор, иногда вместо компилятора (либо вместе с ним) используется интерпретатор, для программ, написанных на интерпретируемых языках программирования;
отладчик - служит для отладки программ. Ошибки в программах могут быть синтаксическими (обычно они выявляются еще на стадии компиляции) и логическими. Для тестирования программы и выявления в ней логических ошибок служит отладчик;
средства автоматизации сборки.
Когда эти компоненты собраны в единый программный комплекс, говорят об интегрированной среде разработки (Integrated development environment - IDE). Такая среда представлена одной программой, не выходя из которой можно производить весь цикл разработки. В состав комплекса кроме перечисленных выше компонент могут входить средства управления проектами, система управления версиями, разнообразные инструменты для упрощения разработки интерфейса пользователя, стандартные заготовки («мастера»), упрощающие разработку стандартных задач, и др. Современные среды разработки, поддерживающие объектно-ориентированную разработку ПО, также включают браузер классов, инспектор объектов и диаграмму иерархии классов.
Обычно среда разработки предназначается для одного определённого языка программирования, хотя существуют среды разработки, предназначенные для нескольких языков - такие как Eclipse или Microsoft Visual Studio.
Системы программирования по типу предоставляемого программного интерфейса можно классифицировать на:
Имеющие интерфейс командной строки (Command Line Interface - CLI). Это традиционный интерфейс систем программирования в операционной системе Unix. В современных диалектах Unix практически все инструменты имеют и надстройку с графическим пользовательским интерфейсом.
Имеющие графический пользовательский интерфейс (Graphic User Interface - GUI). Этот интерфейс традиционен для систем программирования в Windows.
Если IDE включает в себя возможность визуального редактирования интерфейса программы, она называется визуальной средой.
Системы визуальной разработки приложений
Системы визуальной разработки приложений объединяют в себе возможности систем программирования и систем разработки интерфейсов.
Системы разработки интерфейсов в начале 90-х годов ХХ века составляли большую долю в инструментарии. Сейчас такие системы входят составной частью в CASE-средства. Самые известные из них:
C++ Visual Studio (компании Microsoft);
C++ WorkShop Visual (компании Sun Microsystems);
Delphi Suite (компании Borland Inc.);
Средства построения графического интерфейса в Java (компоненты и контейнеры).
Общая схема работы в среде визуального программирования предполагает:
Выбор типа разрабатываемого приложения из имеющегося набора прототипов.
Создание в визуальной манере интерфейса приложения.
Настройку свойств интерфейсных элементов.
Написание кода обработчиков событий для использованных интерфейсных элементов, который позволил бы объединить их в единую систему. Именно эта фаза разработки приложения является самой ответственной и требует квалификации и наибольших усилий со стороны программиста.
Структуру визуальной среды программирования рассмотрим на примере системы Delphi.
Визуальная среда программирования Delphi - наиболее распространенный инструмент для быстрого создания эффективных Windows-приложений. Она проста в освоении, так как большинство средств программирования в ней визуализированы, а в основе лежит достаточно простой для изучения язык Object Pascal.
Основным достоинства данной среды программирования является то, что Delphi - это комбинация нескольких важнейших технологий:
Высокопроизводительный компилятор;
Объектно-ориентированная модель компонент;
Визуальное построение приложений из программных прототипов;
Быстрая разработка работающего приложения из прототипов.
Среда Delphi включает в себя полный набор визуальных инструментов для быстрой разработки приложений (RAD - rapid application development), поддерживающий разработку пользовательского интерфейса и подключение к корпоративным базам данных. К их числу относятся:
Визуальный построитель интерфейсов (Visual User-interface builder) - дает возможность быстро создавать приложения визуально, просто выбирая компоненты из соответствующей палитры.
Библиотека визуальных компонентов (VCL – Visual Component Library) - эта библиотека объектов включает в себя стандартные объекты построения пользовательского интерфейса, графические объекты, объекты мультимедиа, диалоги, объекты управления файлами и управление DDE.
Delphi обладает удобным графическим отладчиком, позволяющим находить и устранять ошибки в коде. Можно устанавливать точки останова, проверять и изменять переменные, при помощи пошагового выполнения. Если же требуются возможности более тонкой отладки, то можно использовать отдельно доступный Turbo Debugger.
Среда Delphi следует спецификации, называемой Single Document Interface (SDI), и состоит из нескольких отдельно расположенных окон. Основные составные части интерфейса Delphi:
Дизайнер Форм (Form Designer);
Окно Редактора Исходного Текста (Editor Window);
Палитра Компонент (Component Palette);
Инспектор Объектов (Object Inspector);
Интерактивный Справочник (On-line help).
Дизайнер Форм в Delphi настолько интуитивно понятен и прост в использовании, что создание визуального интерфейса превращается в игру. Дизайнер Форм первоначально состоит из одного пустого окна, которое вы заполняете всевозможными объектами, выбранными на Палитре Компонент. Информация о формах хранится в двух типах файлов - .dfm и .pas, причем первый тип файла (двоичный) хранит образ формы и ее свойства, второй тип описывает функционирование обработчиков событий и поведение компонент. Оба файла автоматически синхронизируются Delphi, так что если добавить новую форму в проект, связанный с ним файл pas автоматически будет создан, и его имя будет добавлено в проект.
В дополнение к инструментам, обсуждавшимся выше, существует набор инструментальных средств, поставляемых вместе с Delphi:
Встроенный отладчик;
Внешний отладчик (поставляется отдельно);
Компилятор командной строки;
ReportSmith - генератор отчетов для баз данных;
Team Development Support: предоставляет контроль версий при помощи PVCS компании Intersolve (приобретается отдельно) или при помощи других программных продуктов контроля версий;
Visual Query Builder - средство визуального построения SQL-запросов;
и ряд других продуктов.
Иногда достаточно использовать только одну интегрированную среду разработки, но для больших проектов в среду разработки включаются разнородные продукты разных фирм, разных версий. Пример такого набора: файловый менеджер, набор вспомогательных утилит и пакетных файлов, С++Builder – как IDE, PLSQL Developer – для работы с СУБД Oracle, Cristal Reports – для создания отчетов , StarTeam – для ведения версий и поддержки коллективной работы.
Выбор среды разработки
Технология программирования во многом определяется языком программирования, на котором пишутся программы. В языке могут быть заложены средства, влияющие на технологичность и архитектуру разрабатываемой системы (например, объектно-ориентированность, модульность и т.п.). Обычно выбирают ту модель разработки и те языки программирования, которые хорошо знают члены коллектива разработчиков. Выбирать новую технологию, которую предстоит осваивать в процессе разработки – риск провалить проект.
У каждого программиста есть свой взгляд на модель разработки, определяющийся его прошлым опытом, степенью освоения тех или иных инструментальных средств.
Любая среда позволяет производить настройку и адаптацию под те или иные требования: изменение интерфейса, режимов работы, назначения горячих клавиш, установка дополнительных средств («плагинов») и т.п. В арсенале каждого опытного программиста есть свои приемы разработки, собственные вспомогательные средства. Он имеет собственные вкусы и предпочтения. Используя настройки, программист может сделать работу в среде более удобной для себя и, тем самым, более эффективной. Он как бы проецирует свою модель разработки на модель среды. Это особенно важно, когда среди инструментов есть программы с отличающимся интерфейсом (например, разное назначение горячих клавиш).
Создатели инструментальных средств закладывают, как правило, избыточный набор возможностей и программисты практически никогда не используют инструментальное средство на все 100%.
Обзор сред разработки
Рассмотрим следующие среды разработки: JDK (Java Development Kit), Блэкбокс, MS Visual Studio, Eclipse. Кратко их можно охарактеризовать так:
JDK – простая, многоплатформенная, широко распространенная;
Блэкбокс – простая, удобная для обучения;
Microsoft Visual Studio – сложная, многоязыковая, широко распространенная;
Eclipse – многоплатформенная, многоязыковая, перспективная;
MS Visual Studio с языком C# начинает использоваться в учебном процессе, вытесняя C и С++. Система BlackBox не так распространена, но имеет целый ряд технологических новаций, с положительной стороны отличающих ее от более распространенных систем.
JDK
JDK имеет простую модель среды разработки. Как таковой собственной IDE нет. Используется любой текстовый редактор. В состав JDK входят два основных исполняемых файла: компилятор javac.exe и исполнитель оттраслированных классов java.exe (собственно Java-машина). Исходные файлы имеют расширение java, оттранслированные – class. На уровне языка определяются иерархии пакетов, каждый из которых включает один или несколько классов. Иерархия пакетов естественным образом при трансляции отражается в файловую структуру. При трансляции файла с объявленным пакетом package s1.p1 создается подкаталог s1, в нем подкаталог p1, в который размещаются файлы с кодами классов. Каждому классу соответствует один файл, имя которого совпадает с именем класса.
В любом файловом менеджере легко настраиваются горячие клавиши, реагирующие на расширение файлов, для редактирования, компиляции и выполнения. Это дает такой же эффект, как использование какой-либо другой простой среды разработки. Такой стиль вполне подходит для целей обучения.
Наборы классов одного или нескольких пакетов могут объединяться и храниться в одном архиве файлов с расширением jar. Эти файлы также можно считать компонентами среды (подсистемами). При компиляции и запуске программ пути к этим подсистемам указываются в качестве параметров. Несколько приложений могут использовать общие подсистемы. В этом случае для запуска компилятора и программ лучше использовать bat-файлы, в которых указывается разный набор подсистем.
На базе JDK функционирую более развитые платформы, такие как IDEA, NetBeans, Eclipse. В процессе совершенствования к среде добавлялись все новые компоненты и в настоящее время среда имеет версию 1.6.
Примечание. Усложненность сред разработки может отрицательно сказаться на усвоении основ программирования, так как внимание учащегося сосредотачивается не на языке программирования, а на собственно среде и много времени уходит на ее освоение.
BlackBox Component Builder
Блэкбокс - это бесплатная и открытая система программирования для языка программирования Компонентный Паскаль, являющаяся вариантом языка Оберон и обладающая следующей комбинацией свойств:
очень простая (описание языка всего около 30 страниц);
очень быстрая (быстро компилирует даже на i386);
очень компактная (20 MB на диске в максимальной конфигурации)
очень мощная (в том числе за счет лучшей в промышленности поддержки технологий компонентно-ориентированного программирования);
бесплатная для некоммерческого использования (с конца 2004 г. система доступна с открытыми исходными кодами).
Блэкбокс имеет очень простую модель операционной среды. Разработчики системы следовали минималистскому принципу так же, как и при разработке языка системы. Например, в языке отсутствует вложенность модулей и классов и как следствие упрощается архитектурная модель. В отличие от языка Java, один модуль транслируется в один файл. Имеется два вида компонентов - модули и подсистемы. При успешной компиляции модуля образуются символьный и кодовый файлы, которые автоматически размещаются в подкаталогах Sym и Code соответственно. При этом эти каталоги размещаются в подкаталоге Блэкбокса с названием подсистемы. Имя подсистемы – префикс имени модуля. Сами исходные файлы хранится в подкаталоге Mod. Символьный файл содержит всю информацию о внешних связях модуля по экспорту-импорту. Кодовый файл - это двоичный файл с машинными командами. Он загружается в память для непосредственного выполнения при первом вызове какой-либо процедуры из этого модуля.
Поскольку сам Блэкбокс написан на Компонентном Паскале, любой скомпилированный модуль становится частью системы в том смысле, что как файл с машинным кодом, так и соответствующий символьный файл немедленно становятся доступными для импорта из других модулей, вызова процедур из меню Блэкбокса и т.п. Поэтому приложение может иметь такой же интерфейс, как и сама среда. Интерфейс представлен обычным многоуровневым меню и возможностью назначения горячих клавиш. Само меню хранится в файле Menus.odc и доступно для редактирования с помощью функций модуля Text. По команде «обновить меню» меню обновляется. Меню динамически формируется при запуске системы. Каждая подключаемая подсистема может иметь собственное подменю, которое динамически вставляется в основное меню, если в нем есть пункт, отмеченный символом «*». При такой технологии подключение подсистемы заключается в простом копировании файлов, входящим в подсистему. Если пользователь одновременно разрабатывает несколько приложений, каждое из которой располагается в своей папке, среда запускается с параметром, определяющим путь к этой папке. Подсистемы, находящиеся в основном каталоге, используются совместно. Такой подход значительно упрощает развертывание новой подсистемы в уже разработанном приложении.
В системе реализован оригинальный подход к разработке пользовательского интерфейса. Экранная форма представляется собой документ, в которой с помощью специализированного редактора размещаются элементы управления. Но в отличие от других систем это не приводит к генерации кода с описанием класса Form. В самих модулях также отсутствуют ссылки, какая экранная форма будет использована. В документе-форме для элемента можно указать свойство, привязывающее элемент к какой-либо переменной модуля (для текстового поля) или процедуре (для элемента типа кнопки). И этого оказывается достаточно, чтобы при запуске формы значения переменных отображались, а нажатие кнопки приводило к запуску нужной процедуры. Единственно, о чем должен позаботиться программист, это вызвать специальный метод для обновления значений переменных в экранной форме. Такой подход значительно упрощает программирование, избавляя, например, от многочисленных преобразований из текстовых значений в целые и наоборот, которыми изобилует программа в других средах. Экранная форма может быть быстро создана с помощью мастера, который выберет все экспортируемые объекты и построит по ним подходящие элементы управления.
Имеется в системе и ряд других технологических приемов, делающих разработку более удобной. Это так называемые командеры, рабочий журнал, оригинальная отметка ошибок, трассировщик и др. Все это делает систему привлекательной как для использования в обучении программированию, так и для промышленного программирования.
MS Visual Studio
Операционная среда разработки MS Visual Studio на сегодняшний день является предпочтительным выбором многих разработчиков, работающих на платформе Windows. Среда позволяет эффективно создавать сложные приложения в течение короткого периода времени. В отличие от рассмотренных выше операционных сред модель данной среды существенно богаче и использует такие понятия как решение (solution), проект, пространство имен (namespace) и сборка (assembly).
Понятие проекта присутствует во многих средах, например, в среде Delphi. Файл проекта содержит перечисление исходных файлов и прочих ресурсов, из которых система будет строить приложение. В решение среды Visual Studio входят несколько проектов, которые могут быть зависимыми или независимыми друг от друга. Выделяется стартовый проект. Понятие сборки исходит из общеязыковой исполнительной среды CLR (Common Language Runtime). Среда CLR является наиболее революционным изобретением, с появлением которого процесс написания и выполнения приложений становиться принципиально другим.
Компилятор преобразует файлы с исходными кодами в коды на промежуточном языке MSIL (Microsoft Intermediate Language). Вместе с метаданными эти коды записываются PE-файлы (Portal Executable), имеющие расширение exe или dll в зависимости от типа проекта. Также может быть получен модуль с расширением netmodule, который не содержит метаданных.
Всего имеется 12 типов проектов. При загрузке PE-файлы «на лету» транслируются в команды реального процессора.
Каркас Framework.NET, обеспечивающий выполнение программ, не входит в Visual Studio, а является настройкой над операционной системой. Это аналог виртуальной Java-машины.
Сборка является минимальной единицей для развертывания приложений. Каждый тип сборки характеризуется уникальным идентификатором, идентифицируется цифровой подписью автора и уникальным номером версии. Между сборками и пространствами имен существует следующее соотношение. Сборка может содержать несколько пространств имен. В то же время, пространство имен может занимать несколько сборок. Сборка может иметь в своем составе как один, так и несколько файлов, которые объединяются в так называемом манифесте или описании сборки.
На уровне языка C# пространства имен, аналогично пакетам в Java, служат для структурирования проекта. Пространство имен включает один или несколько классов. В одном исходном файле может определяться несколько пространств имен и в тоже время одно пространство имен может определяться в нескольких файлах. И даже класс может располагаться в нескольких файлах (partial classes).
Для начинающих программистов такое обилие возможностей может вызвать немалые затруднения. О масштабности и сложности среды можно судить по следующей сравнительной таблице трех сред:
Операционная среда |
Число каталогов |
Число типов файлов |
Блэкбокс |
85 |
9 |
C++Builder |
367 |
27 |
Visual Studio |
3450 |
188 |
Операционная среда Eclipse
Интересной и перспективной представляется операционная среда Eclipse, разработанная в фирме IBM. Первоначальной целью проекта было создание корпоративного стандарта IDE для разработки программ на разных языках под различные платформы. Потом проект был переименован в Eclipse и выделен в открытый доступ. Лицензия позволяет бесплатно использовать код и среду разработки, и при этом создавать закрытые коммерческие продукты. Благодаря этому система получила широкое распространение и для многих организаций стала корпоративным стандартом для разработки приложений.
Система реализована на языке Java и изначально представляла собой полноценную интегрированную среду для языка Java. В дальнейшем стали поддерживаться и другие языки. Первые версии были неудобны в том, что целевой продукт вынуждено включал лишнюю функциональность. Начиная с третьей версии, была переработана архитектура всей системы с целью максимального разделения модулей и взаимосвязи между ними. При этом модули Eclipse, образованные из согласованных наборов классов, давали функциональность целых подсистем, таких как подсистемы помощи, обновления продукта, обучения, презентации, многоязыковой поддержки и множество других. Разрабатывая приложение теперь можно постепенно наращивать функциональность, подключая уже готовые бесплатные компоненты. В терминологии Eclipse эти компоненты называются «подключаемыми модулями» или «плагинами» (Plugins). Такая технология становится типичной для развитых операционных сред. Платформа на основе этой технологии получила название RCP (Reach Client Platform), а приложения, соответственно, RCP-приложениями.
Графический интерфейс Eclipse построен с использованием графической библиотеки SWT, которая опирается на графику используемой операционной системы. Предусмотрена возможность создания настраиваемых стилей, которые позволяют произвольно менять как внешний вид приложения, так и поведение составляющих его компонентов. Многооконный интерфейс MDI, реализованный в ОС Windows еще в версии 3.1, заменен на альтернативный интерфейс, основанный на закладках. Этот интерфейс похож на интерфейс MS Visual Studio, но имеет и отличия. Режимы работы могут объединяться в одну панель, либо размещаться каждый в отдельной панели. Имеется встроенный механизм чередования панелей или механизм «быстрых» панелей, когда они не занимают место на экране, а вызываются кнопками по необходимости.
Интерфейс обладает богатыми и гибкими возможностями, некоторые из которых являются оригинальными и не встречаются в других операционных средах.
Данные, которые используются при разработке приложения, размещаются в одном каталоге – рабочей области. Графическая среда разработки (или просто рабочая среда) представляет собой инфраструктуру для управления ресурсами рабочей области и навигации по ресурсам. Рабочая среда включает одно или несколько окон, которые в свою очередь могут включать страницы. Каждая страница может включать так называемые проекции, которые управляют взаиморасположением редакторов и панелей для решения определенных задач.
Такая, сложная на первый взгляд, организация интерфейса, оказывается удобной именно благодаря использованию проекций. Проекцию можно рассматривать как набор визуальных компонент для выполнения конкретно поставленных задач. Например, проекция "Java" содержит необходимый набор для разработки java-приложений, а проекция "CVS" предназначена для оперирования удаленным репозитарием проектов при коллективной разработке. Каждая проекция имеет свой набор меню и панелей инструментов, состав которых можно настраивать и сохранять. Начальный макет проекции определяется разработчиком, но его можно изменять, открывая и закрывая панели и встраивая их в различные места окна Рабочей среды.
Основные визуальные компоненты рабочей среды – панели и редакторы.
Панель применяется для навигации по структурам объектов, отображения их свойств и внутренней структуры. В окне рабочей среды обычно размещается только один экземпляр определенного типа панели. Панели могут иметь собственные меню и панели инструментов, положение и внешний вид которых определяются выбранной темой представления. Действия, представленные элементами меню и кнопками панели инструментов, относятся только к входящим в панель элементам.
Редакторы чаще всего применяются для просмотра и редактирования ресурсов: файлов, таблиц базы данных и т.п. Любой ресурс в навигаторе ресурсов можно связать с необходимым для решения текущей задачи редактором. В отличие от панелей, изменения ресурсов в редакторах, подчиняются правилу открыть-сохранить-закрыть. В окне рабочей среды может быть размещено экземпляров одного и того же типа редактора для различных ресурсов. Для редакторов может быть создан отдельный раздел меню и панелей быстрого запуска, которые автоматически добавляются в главное меню и панель быстрого запуска приложения при активации редактора.
Процессы разработки приложений, которые включают модули расширений, также имеют отличия от традиционных подходов. При запуске среды разработки создается статический экземпляр рабочей среды, в котором пишется программный код, производится отладка и выполняются другие задачи производственного процесса. Когда же приложение запускается на тестирование, то создается динамический экземпляр рабочей среды, в который включаются выбранные в конфигурации запуска разрабатываемые модули, а так же модули целевой платформы. Целевая платформа – это сборка продукта Eclipse, предназначенная для развертывания разрабатываемых приложений.
За последнее время произошли существенные шаги по упрощению разработки собственных приложений. Кроме мастеров по созданию RCP-приложений, появился мастер конфигурирования и экспорта продукта. Это существенно упрощает подготовку продукта для развертывания клиентом. Доступен экспорт, как в целевой каталог, так и в архив, который можно передать клиенту. Дополнительных инсталляторов не требуется, достаточно просто распаковать архив и создать ярлык на запускаемый файл. Есть возможность создания сборки сразу для нескольких операционных платформ. При желании, можно произвести экспорт продукта в каталог, используя содержимое которого создать автоматический инсталлятор для упрощения жизни неподготовленным пользователям.
Виртуальные машины
Определение 1: Виртуальная машина - программная или аппаратная среда, исполняющая некоторый код (например, байт-код, шитый код, p-код или машинный код реального процессора), или спецификация такой системы (например: «виртуальная машина языка программирования Си»). [Википедия]
Определение 2: Виртуальная машина - это полностью изолированный программный контейнер, способный выполнять собственную операционную систему и приложения, как физический компьютер.
Виртуальная машина работает абсолютно так же, как физический компьютер, и содержит собственные виртуальные (т.е. программные) ЦП, ОЗУ, жесткий диск и сетевую интерфейсную карту (NIC).
Проще говоря, виртуальная машина – это программа, которую вы запускаете из своей операционной системы. Программа эмулирует реальную машину. На виртуальные машины, как и на реальные, можно ставить операционные системы. У неё есть BIOS, отведенное место на вашем жестком диске, сетевые адаптеры для соединения с реальной машиной, сетевыми ресурсами или другими виртуальными машинами.
Преимущества виртуальных машин
Самый просто пример: нынче, как мы знаем, вышли новые операционные системы Windows Vista и Windows 7. И как многие из вас убедились, некоторые приложения, в частности игры, на них не работают. Устанавливаем виртуальную машину с, допустим, операционной системой Windows XP. И всё прекрасно будет работать.
Второй пункт можно отнести к злобным хакерам или просто к компьютерным хулиганам. Имеется в виду, что на виртуальной машине можно спокойно написать вирус или вредоносное программное обеспечение, которое сможет повредить вам лишь гостевую операционную систему виртуальной машины.
На виртуальную машину можно ставить любое ПО, не опасаясь чего-либо. Вы можете экспериментировать с различными настройками и прочее.
Ну и одно из самых главных это то, что можно легко изучать новые операционные системы, не стирая свою старую.
Это конечно далеко не все преимущества виртуальных машин. Каждый пользователь может сам придумать, для чего ему нужна виртуальная машина.
Перед возможностью установки нескольких хостовых операционных систем на один компьютер с их раздельной загрузкой, виртуальные машины имеют следующие неоспоримые преимущества:
Возможность работать одновременно в нескольких системах, осуществлять сетевое взаимодействие между ними.
Возможность сделать «снимок» текущего состояния системы и содержимого дисков одним кликом мыши, а затем в течение очень короткого промежутка времени вернуться в исходное состояние.
Простота создания резервной копии операционной системы (не надо создавать никаких образов диска, всего лишь требуется скопировать папку с файлами виртуальной машины).
Возможность иметь на одном компьютере неограниченное число виртуальных машин с совершенно разными операционными системами и их состояниями.
Отсутствие необходимости перезагрузки для переключения в другую операционную систему.
Недостатки виртуальных машин
Потребность в наличии достаточных аппаратных ресурсов для функционирования нескольких операционных систем одновременно.
Операционная система работает несколько медленнее в виртуальной машине, нежели на «голом железе». Однако в последнее время показатели производительности гостевых систем значительно приблизились к показателям физических ОС (в пределах одних и тех же ресурсов), и вскоре, за счет улучшения технологий реализации виртуальных машин, производительность гостевых систем практически будет равна реальным.
Существуют методы определения того, что программа запущена в виртуальной машине (в большинстве случаев, производители систем виртуализации сами предоставляют такую возможность). Вирусописатели и распространители вредоносного программного обеспечения, конечно же, в курсе этих методов и в последнее время включают в свои программы функции обнаружения факта запуска в виртуальной машине, при этом никакого ущерба вредоносное ПО гостевой системе не причиняет.
Различные платформы виртуализации пока не поддерживают полную виртуализацию всего аппаратного обеспечения и интерфейсов. В последнее время количество поддерживаемого аппаратного обеспечения стремительно растет у всех производителей платформ виртуализации. Помимо основных устройств компьютера, уже поддерживаются сетевые адаптеры, аудиоконтроллеры, интерфейс USB 2.0, котроллеры портов COM и LPT и приводы CD-ROM. Но хуже всего обстоят дела с виртуализацией видеоадаптеров и поддержкой функций аппаратного ускорения трехмерной графики.
Все недостатки в принципе можно решить, да и преимущества виртуальных машин перевешивают их недостатки. Именно поэтому виртуализация сейчас продвигается семимильными шагами вперёд. А пользователи находят всё больше и больше причин их использовать.
Процессные и системные виртуальные машины
Разделение пошло от того, что система и процесс видят машину по-разному, поэтому и виртуальные машины бывают процессные и системные.
Процессная виртуальная машина - это виртуальная платформа для выполнения отдельного процесса. Она предназначена для поддержки процесса, создаётся при его активации и «умирает» после его окончания.
Системная виртуальная машина – полнофункциональная, постоянно действующая системная среда, служащая для поддержки операционной системы вместе с большим количеством её пользовательских процессов; она обеспечивает «гостевой» операционной системе доступ к виртуальным аппаратным средствам, в том числе к процессору и памяти, устройствам ввода/вывода, а иногда - и к графическому интерфейсу.
Определение 3: Гость - процесс или система, которые выполняются на виртуальной машине.
Определение 4: Хост - платформа, поддерживающая виртуальную машину.
Определение 5: Рабочая среда - программное обеспечение, реализующее процессную виртуальную машину.
Определение 6: Монитор виртуальной машины - программное обеспечение виртуализации системной виртуальной машины.
Процессные виртуальные машины создают среды ABI и API для пользовательских приложений, что позволяет в многозадачном режиме осуществлять репликацию операционной среды, эмулировать систему команд, оптимизировать код или выполнять программы на языках высокого уровня.
Системная виртуальная машина обеспечивает полнофункциональную среду, в которой могут сосуществовать операционная система и несколько процессов, относящихся к разным пользователям. С помощью них одна аппаратная платформа может поддерживать несколько гостевых операционных систем одновременно.
Примечание. Архитектура системы команд (ISA) - определяет границу между оборудованием и программным обеспечением и состоит из двух интерфейсов:
пользовательская часть ISA содержит функции, доступные прикладной программе;
системная часть ISA кроме пользовательских команд содержит функции, доступные только компонентам операционной системы, которые отвечают за управление оборудованием.
Двоичный интерфейс приложений (ABI) - предоставляет программе аппаратные ресурсы и услуги через пользовательскую часть ISA и интерфейс вызова системных процедур. Привилегированные машинные команды в ABI не входят. Все прикладные программы взаимодействуют с оборудованием опосредованно, обращаясь к услугам операционной системы через интерфейс вызова системных процедур. Посредством таких процедур ОС выполняет действия от имени пользовательской программы после подтверждения их аутентичности и безопасности.
Интерфейс прикладного программирования (API) - предоставляет программе аппаратные ресурсы и услуги через пользовательскую часть ISA, дополненную обращениями к библиотекам на языке высокого уровня. Все вызовы системных процедур обычно выполняются через библиотеки. Использование API позволяет путем перекомпиляции легко переносить прикладные программы на другие системы, поддерживающие тот же интерфейс прикладного программирования.
Репликация - механизм синхронизации содержимого нескольких копий объекта (например, содержимого базы данных). Это процесс, под которым понимается копирование данных из одного источника на другой (или на множество других) и наоборот. При репликации изменения, сделанные в одной копии объекта, могут быть распространены в другие копии.
Инструменты для создания и работы с виртуальными машинами
1. VMware Workstation 7 - чаще всего платформу VMware Workstation используют для повышения эффективности процесса разработки и тестирования. Создает и обеспечивает работу виртуальных машин, обладает многими функциями, имеет удобный интерфейс. Платная. Рекомендуется для профессионального использования.
2. Одними из главных конкурентов VMWare WorkStation являются VirtualBox компании InnoTek (бесплатен для домашнего использования), и Virtual PC компании Microsoft (бесплатен). Конечно, они пока имеют меньшее количество возможностей по сравнению с VMWare WorkStation, тем не менее они также хорошо подойдут для домашнего использования.
3. Для пользователей продукции Apple и бизнесменам оптимальным выбором будет виртуальная машина для Linux и Windows Parallels Desktop. Она является платной и обладает меньшими возможностями, чем VMware Workstation.
Лекция 10
ТЕМА: Инструментальные средства и технологии Windows. SDK.
Литература: 1. Гэри Неббет. Справочник по базовым функциям API Windows NT/2000 = Windows NT/2000 Native API Reference.
2. http://procoder.info/index.php/entry/bystroe-napisanie-programm-na-winapi - Алексей Шишкин. Быстрое написание программ на WinAPI //Некоммерческий электронный журнал «Программист».
3. А. Я. Архангельский. Программирование в Delphi для Windows. Версии 2006, 2007, Turbo Delphi.
SDK
SDK (SoftwareDevelopmentKit ‒ Набор Средств Разработки) ‒ комплект средств разработки, который предоставляет доступ программистам к соответствующему программному обеспечению. SDK помогают программистам в разработке приложений, а также в расширении возможностей готовых приложений.
Часто пакеты SDK распространяются бесплатно. Хороший SDK может стать определяющим фактором при выборе программного обеспечения.
Примеры SDK:
Platform SDK – содержит интерфейсы для разработки приложений.
DirectX SDK – интерфейсы для вывода графики и звука, работы с сетью, обработки входных сигналов от клавиатуры и мыши и т.д.
FarCry MOD SDK – инструментарий для разработки модов к игре FarCry.
MAX SDK – инструментарий для создания плагинов к 3DStudioMAX.
Tokamak SDK – позволяет разработчикам подключать функциональность игровой физики библиотеки Tokamak в свои проекты.
Примечание. Плагин – встраиваемый модуль, расширяющий функциональность программы.
Содержание SDK
Обычно в SDK входит сама программа (библиотека), позволяющая разрабатывать новые программы/игры на основе уже существующих и некий инструментарий для отладки. Далее – документация, состоящая, как правило, из 2 разделов: Tutorial (пошаговая инструкция) и Reference (справочник доступных для данного SDK возможностей). И, наконец, самое главное – примеры того, что делается при помощи SDK (своего рода шаблоны). То есть Вам совсем не обязательно вникать в тонкости работы SDK – можно просто взять готовый шаблон, изменить его соответственно своему вкусу и получить желаемый результат (программу или игру).
Стандартная структура папок SDK:
bin\ – папка инструментария разработчика, содержит исполняемые файлы и dll;
demo(или samples)\ – папка с примерами по работе с SDK;
include\ – папка с заголовочными файлами;
lib\ – библиотеки для подключения разработчиками;
doc\ – папка с документацией.
Применение SDK
При помощи SDK можно создавать действительно качественные игры без глубоких знаний о технологиях программирования под ЗD. Кроме того, можно создавать приложения для определенных пакетов программ, аппаратных платформ, компьютерных систем, операционных систем и многое другое.
DDK
Driver Development Kit – набор из средств разработки, заголовочных файлов, библиотек, утилит, программного кода примеров и документации, который позволяет программистам создавать драйверы для устройств по определённой технологии или для определённой платформы (программной или программно-аппаратной).
Создание драйвера возможно и без использования DDK, однако DDK содержит средства, упрощающие разработку драйвера (например, готовые примеры и шаблоны кода), обеспечивающие совместимость драйвера с операционной системой (символические определения констант, определения интерфейсных функций ОС, определения, зависящие от типа и версии ОС), а также установку и тестирование драйвера.
Для создания драйверов под Windows применяется WDK – Windows Driver Kit. Продукт доступен для бесплатной загрузки через сайт Microsoft Connect и содержит в себе средства построения программ как режима ядра, так и пользовательского режима.
Интерфейс программирования приложений (api)
Интерфейс программирования приложений (иногда интерфейс прикладного программирования – application programming interface, API [эй-пи-ай]) – набор готовых классов, процедур, функций, структур и констант, предоставляемых приложением (библиотекой, сервисом) для использования во внешних программных продуктах. Используется программистами для написания всевозможных приложений.
API – это обычно (но не обязательно) метод абстракции между низкоуровневым и высокоуровневым программным обеспечением. Т. е. API позволяет абстрагироваться от того, как именно реализована предоставляемая функциональность.
Одним из самых распространенных назначений API является предоставление набора широко используемых функций, например для рисования окна или иконок на экране. Программисты используют преимущества API в функциональности, так им не приходится разрабатывать все с нуля. API является абстрактным понятием - программа, которая предлагает некоторое API, часто называют реализацией данного API. Во многих случаях API является частью набора разработки программного обеспечения. Одновременно, набор разработки может включать как API, так и другие инструменты и аппаратное обеспечение, так что эти два термина не являются взаимозаменяемыми.
API как средство интеграции приложений
Если программу (модуль, библиотеку) рассматривать как чёрный ящик, то API - это множество «ручек», которые доступны пользователю данного ящика, которые он может вертеть и дёргать.
Программные компоненты взаимодействуют друг с другом посредством API. При этом обычно компоненты образуют иерархию - высокоуровневые компоненты используют API низкоуровневых компонентов, а те, в свою очередь, используют API ещё более низкоуровневых компонентов.
По такому принципу построены протоколы передачи данных по Интернет. Стандартный стек протоколов (сетевая модель OSI) содержит 7 уровней (от физического уровня передачи бит до уровня протоколов приложений, подобных протоколам HTTP и IMAP). Каждый уровень пользуется функциональностью предыдущего уровня передачи данных и, в свою очередь, предоставляет нужную функциональность следующему уровню.
Важно заметить, что понятие протокола близко по смыслу к понятию API. И то и другое является абстракцией функциональности, только в первом случае речь идёт о передаче данных, а во втором - о взаимодействии приложений.
API – библиотеки функций и классов включает в себя описание сигнатур и семантики функций.
Определение 1: Сигнатура функции - часть общего объявления функции, позволяющая средствам трансляции идентифицировать функцию среди других.
В различных языках программирования существуют разные представления о сигнатуре функции, что также тесно связано с возможностями перегрузки функций в этих языках.
Иногда различают сигнатуру вызова и сигнатуру реализации функции. Сигнатура вызова обычно составляется по синтаксической конструкции вызова функции с учётом сигнатуры области видимости данной функции, имени функции, последовательности фактических типов аргументов в вызове и типе результата. В сигнатуре реализации обычно участвуют некоторые элементы из синтаксической конструкции объявления функции: спецификатор области видимости функции, её имя и последовательность формальных типов аргументов.
Например, в языке программирования C++ простая функция однозначно опознаётся компилятором по её имени и последовательности типов её аргументов, что составляет сигнатуру функции в этом языке. Если функция является методом некоторого класса, то в сигнатуре будет участвовать и имя класса.
В языке программирования Java сигнатуру метода составляет его имя и последовательность типов параметров; тип значения в сигнатуре не участвует.
Определение 2: Семантика функции - это описание того, что данная функция делает. Семантика функции включает в себя описание того, что является результатом вычисления функции, как и от чего этот результат зависит. Обычно результат выполнения зависит только от значений аргументов функции, но в некоторых модулях есть понятие состояния. Тогда результат функции может зависеть от этого состояния, и, кроме того, результатом может стать изменение состояния. Логика этих зависимостей и изменений относится к семантике функции. Полным описанием семантики функций является исполняемый код функции или математическое определение функции.
API операционных систем. Проблемы, связанные с многообразием API
Практически все операционные системы (UNIX, Windows, Mac OS, и т. д.) имеют API, с помощью которого программисты могут создавать приложения для этой операционной системы. Главный API операционных систем – это множество системных вызовов.
Определение 3: Систе́мный вы́зов - обращение прикладной программы к ядру операционной системы для выполнения какой-либо операции.
Современные операционные системы предусматривают разделение времени между выполняющимися вычислительными процессами (многозадачность) и разделение полномочий, препятствующее исполняемым программам обращаться к данным других программ и оборудованию. Ядро ОС исполняется в привилегированном режиме работы процессора. Для выполнения межпроцессной операции или операции, требующей доступа к оборудованию, программа обращается к ядру, которое, в зависимости от полномочий вызывающего процесса, исполняет либо отказывает в исполнении такого вызова.
С точки зрения программиста системный вызов обычно выглядит как вызов подпрограммы или функции из системной библиотеки. Однако системный вызов как частный случай вызова такой функции или подпрограммы следует отличать от более общего обращения к системной библиотеке, поскольку последнее может и не требовать выполнения привилегированных операций.
В индустрии программного обеспечения общие стандартные API для стандартной функциональности имеют важную роль, так как они гарантируют, что все программы, использующие общий API, будут работать одинаково хорошо или, по крайней мере, типичным привычным образом. В случае API графических интерфейсов это означает, что программы будут иметь похожий пользовательский интерфейс, что облегчает процесс освоения новых программных продуктов.
С другой стороны, отличия в API различных операционных систем существенно затрудняют перенос приложений между платформами. Существуют различные методы обхода этой сложности - написание «промежуточных» API (API графических интерфейсов WxWidgets, Qt, GTK, и т. п.), написание библиотек, которые отображают системные вызовы одной ОС в системные вызовы другой ОС (такие среды исполнения, как Wine, cygwin, и т. п.), введение стандартов кодирования в языках программирования (например, стандартная библиотека языка C), написание интерпретируемых языков, реализуемых на разных платформах (sh, python, perl, php, tcl, Java, и т. д.).
Также необходимо отметить, что в распоряжении программиста часто находится несколько различных API, позволяющих добиться одного и того же результата. При этом каждый API обычно реализован с использованием API программных компонент более низкого уровня абстракции.
Например, для того, чтобы увидеть в браузере строчку «Hello, world!», достаточно лишь создать HTML-документ с минимальным заголовком и простейшим телом, содержащим данную строку. Когда браузер откроет этот документ, программа-браузер передаст имя файла (или уже открытый дескриптор файла) библиотеке, обрабатывающей HTML-документы, та, в свою очередь, при помощи API операционной системы прочитает этот файл и разберётся в его устройстве, затем последовательно вызовет через API-библиотеки стандартных графических примитивов операции типа «очистить окошко», "написать выбранным шрифтом «Hello, world!»". Во время выполнения этих операций библиотека графических примитивов обратится к библиотеке оконного интерфейса с соответствующими запросами, уже эта библиотека обратится к API операционной системы, чтобы записать данные в буфер видеокарты.
При этом практически на каждом из уровней реально существует несколько возможных альтернативных API. Например, мы могли бы писать исходный документ не на HTML, а на LaTeX, для отображения могли бы использовать любой браузер. Различные браузеры, вообще говоря, используют различные HTML-библиотеки, и, кроме того, всё это может быть (вообще говоря) собрано с использованием различных библиотек примитивов и на различных операционных системах.
Основными сложностями существующих многоуровневых систем API, таким образом, являются:
Сложность портирования программного кода с одной системы API на другую (например, при смене ОС);
Потеря функциональности при переходе с более низкого уровня на более высокий. Грубо говоря, каждый «слой» API создаётся для облегчения выполнения некоторого стандартного набора операций. Но при этом реально затрудняется, либо становится принципиально невозможным выполнение некоторых других операций, которые предоставляет более низкий уровень API.
Примечание. Порти́рование - в программировании под порти́рованием понимают адаптацию некоторой программы или её части, с тем чтобы она работала в другой среде, отличающейся от той среды, под которую она была изначально написана с максимальным сохранением её пользовательских свойств.
Примеры API
Операционных систем: Windows API, Linux Kernel API, OS/2 API.
Графических интерфейсов: OpenGL, DirectX, GDI.
Звуковых интерфейсов: DirectX, OpenAL.
Web API.
Windows API
Windows API - общее наименование целого набора базовых функций интерфейсов программирования приложений операционных систем семейств Windows и совместимой с ними свободной бесплатной операционной системы ReactOS. Является самым прямым способом взаимодействия приложений с Windows и ReactOS.
Для создания программ, использующих Windows API, Майкрософт выпускает комплект разработчика программного обеспечения, который называется Platform SDK, и содержит документацию, набор библиотек, утилит и других инструментальных средств для разработки.
Windows API был изначально спроектирован для использования в программах, написанных на языке Си или C++. Работа через Windows API - это наиболее близкий к системе способ взаимодействия с ней из прикладных программ. Более низкий уровень доступа, необходимый только для драйверов устройств, в текущих версиях Windows предоставляется через Windows Driver Model.
Версии
1. Win16 - первая версия Windows API для 16-разрядных версий Windows. Изначально назывался просто Windows API, затем стал называться Win16 для отличия от Win32.
2. Win32s - подмножество Win32, устанавливаемое на семейство 16-разрядных систем Windows 3.x, и реализующее ограниченный набор функций Win32 API для этих систем.
3. Win32 - 32-разрядный API для современных версий Windows. Самая популярная ныне версия. Базовые функции этого API реализованы в динамически подключаемых библиотеках kernel32.dll и advapi32.dll; базовые модули графического интерфейса пользователя - в user32.dll и gdi32.dll. Win32 появился вместе с Windows NT и затем был перенесён в несколько ограниченном виде в системы серии Windows 9x. В современных версиях Windows, происходящих от Windows NT, работу Win32 GUI обеспечивают два модуля: csrss.exe (процесс исполнения клиент-сервер), работающий в пользовательском режиме, и win32k.sys - в режиме ядра. Работу же системных Win32 API обеспечивает ядро ntoskrnl.exe.
4. Win64 - 64-разрядная версия Win32, содержащая дополнительные функции для использования на 64-разрядных компьютерах. Win64 API можно найти только в 64-разрядных версиях Windows XP, Windows Server 2003, Windows Vista, Windows Server 2008, Windows Server 2008 R2, Windows 7 и Windows 8.
Windows API состоит из нескольких тысяч вызываемых функций, которые разбиты на следующие основные категории:
1. Базовые службы (Base Services).
2. Службы компонентов (Component Services).
3. Службы пользовательского интерфейса (User Interface Services).
4. Графические и мультимедийные службы (Graphics and Multimedia Services).
5. Обмен сообщениями и совместная работа (Messaging and Collaboration).
6. Сеть (Networking).
7. Веб-службы (Web Services).
Описание Windows API можно найти в документации по набору инструментальных средств разработки программного обеспечения Windows Software Development Kit (SDK). Эта документация доступна на веб-сайте www.msdn.microsoft.com. Она также включена со всеми уровнями подписки в сеть Microsoft Developer Network (MSDN), предназначенную для разработчиков.
Технологии, доступные через Windows API
1. Bluetooth
2. Component Object Model
3. COM Plus
4. Device I/O
5. DLL, процессы и многопоточность
6. Messaging Application Programming Interface (MAPI)
7. OLE DB
8. Windows File Protection
9. Windows GDI
10. Windows GDI+
11. Windows Management Instrumentation
12. И др.
Например, для запуска приложений из программы в Delphi можно использовать следующие функции API Windows:
1. ShellExecute - запускает оболочку, ассоциированную с расширением файла, передаваемого ей в качестве параметра. Например, если передать файл с расширением doc, то запустится редактор MS Word (если он установлен в системе). В качестве параметра функции можно передать и файл с раширением exe. Для использования ShellExecute в раздел uses надо добавить модуль ShellAPI.
2. CreateProcess - создает новый процесс и его первичный поток. Данная функция используется в Win32 для запуска других приложений. По сравнению с ShellExecute, функция дает дополнительные возможности по управлению процессом: можно установить начальный приоритет первого потока процесса, выставить положение и размер окна приложения, дождаться завершения процесса, завершить процесс. Для использования CreateProcess в раздел uses надо добавить модуль Windows.
3. WinExec , LoadModule - достались в наследство от Windows 3.x, Microsoft не рекомендует использовать их в приложениях Win32 (они работают через вызов CreateProcess).
DLL
Библиотеки DLL (dynamic-link libraries - динамически подключаемые библиотеки). Набор вызываемых подпрограмм, связанных вместе в виде двоичного файла, который может быть загружен в динамическом режиме приложениями, которые используют эти подпрограммы. В качестве примера можно привести Msvcrt.dll (библиотеку времени выполнения для приложений, написанных на языке C) и Kernel32.dll (одну из библиотек подсистемы Windows API). DLL-библиотеки широко используются компонентами и приложениями Windows, которые работают в пользовательском режиме. Преимущество, предоставляемое DLL-библиотеками по сравнению со статическими библиотеками, заключается в том, что они могут использоваться сразу несколькими приложениями, и Windows обеспечивает наличие в памяти только одной копии кода DLL-библиотеки для тех приложений, в которых имеются ссылки на эту библиотеку.
Уровни абстракции
Аппаратные и программные структуры большинства современных компьютеров - многоуровневые. Детали нижних уровней скрываются, чтобы обеспечить более простые модели для верхнего уровня. Данный принцип абстракции - способ, благодаря которому проектировщики аппаратных и программных средств справляются со сложностью вычислительных систем.
Любую современную программу или программную технологию можно представить как совокупность программных "слоев". Каждый из этих слоев производит свою собственную работу, которая заключается в повышении уровня абстракции производимых операций. Так, самый низший слой (слои) вводит понятия, которые позволяют абстрагироваться от используемого оборудования; следующий слой (слои) позволяет программисту абстрагироваться от сложной последовательности вызовов функций, вводя такое понятие как протокол и т.д. Практически в любом современном программном продукте можно обнаружить и выделить около десятка последовательных слоев абстракции.
На верхнем уровне абстракции прикладные программы пользователей обращаются к интерфейсу прикладного программирования (application programming interface – API), который представляет собой набор библиотечных функций. API структурирован по подсистемам, причем набор подсистем расширяем. Нижние уровни абстракции реализуют ядро ОС, выполняющее функции управления памятью, диспетчеризации задач и управления устройствами. Наконец, на самом низком уровне абстракции реализованы драйверы устройств.
Современной тенденцией является необходимость абстрагирования и от самих операционных систем, что будет позволять переносить программы с одной операционной системы на другую путем простой перекомпиляции (транслируемые программы, в основном, вообще не требуют никаких действий по переносу).
Абстракцию, которая доступна программисту в виде библиотек API можно назвать базовой. Это самый низкий уровень абстракции, который доступен для прикладного программирования. На уровне ядра системы доступны и более низкие уровни абстракции, однако для их использования необходимо разрабатывать специализированные программы (драйвера, модули). Базовый уровень абстракции (API) предоставляет максимально широкие возможности для прикладного программирования и является наиболее гибким. Однако, программирование с использованием API является гораздо более трудоемким и приводит к значительно большим объемам исходного кода программы, чем программирование с использованием дополнительных библиотек.
Дополнительные библиотеки поставляются со многими средствами разработки с целью уменьшения трудоемкости и сроков разработки программ, что в итоге приводит к повышению их конкурентоспособности. Но применение дополнительных библиотек абстракций приводит к резкому увеличению размеров откомпилированных программ, из-за того что в программу включается код используемых библиотек, к тому же это включение зачастую бывает неэффективным, т.к. в программу включаются неиспользуемые участки кода. Кроме того, чем больше уровень абстракции библиотеки, тем сложнее ее код, и тем больше трудностей возникает при решении сложных задач. Приходится учитывать множество взаимосвязей и взаимных влияний отдельных элементов и процессов библиотеки друг на друга. Кроме того, структура и функциональность любой библиотеки обычно рассчитывается на удовлетворение всех потенциально возникающих задач, что приводит к ее громоздкости и неэффективности.
В Delphi используется очень мощная и сложная библиотека VCL (Visual Components Library), которая помимо непосредственных абстракций вводит также и множество своих функциональных классов. В этой библиотеке находятся компоненты для визуального отображения информации, работы с базами данных, с системными объектами, компоненты для работы с Internet-протоколами, классы для написания своих COM-объектов и многое другое. Модули библиотеки подключаются к компиляции по мере необходимости, однако базовый размер простейшего диалогового проекта с одной формой превышает 300кБ (со статически скомпонованной библиотекой). И такой размер во многих случаях может оказаться слишком большим, особенно если программа не требует большой функциональности в интерфейсе.
Для решения этой проблемы можно отказаться от использования библиотеки VCL, и программировать, используя базовый набор функций Win32 API. Однако, если при разработке линейных, недиалоговых, нерезидентных программ не возникает никаких трудностей, то разработка программ, требующих активного взаимодействия с пользователем или системой, становится трудоемкой. Структурное программирование, рекомендуемое в таких случаях, оказывается неэффективным и трудоемким.
Рассмотрим плюсы и минусы визуального программирования и программирования без использования графических компонентов, т.е. использование функций Win32 API напрямую для создания окон, работы с графикой и т.д.
Плюсы визуального программирования:
1. Быстрое написание программ (достаточно накидать кнопок на форму и написать их процедуры).
2. Удобство использования (это удобнее, чем описывать каждый шаг на низком уровне, не нужно заботиться о памяти).
Минусы визуального программирования:
1. Огромный размер приложений - «пустое» приложение на Delphi «весит» примерно300 кб.
2. Из-за огромного количества надстроек быстродействием приходится пренебречь, особенно в плане графики на Canvas.
Плюсы низкоуровневого программирования на Win32 API:
1. Малый размер конечных файлов (между 30 кб и 300 кб есть большая разница);
2. Быстродействие на достаточно высоком уровне (быстрее получится только, если писать целиком на ассемблере).
Минусы низкоуровневого программирования на Win32 API:
1. Нет визуализации (труднее представить, что создать, что уничтожить, что где писать).
2. Для создания обычного окна потребуется примерно 50 строк кода (создать и зарегистрировать само приложение, создать окно, позаботиться обо всех параметрах).
3. Необходимо помнить о необходимости следить за использованием памяти - освобождением, взятием.
Лекция 11
ТЕМА: Процедура физического проектирования – порядок, инструменты, ресурсы, документы.
Литература: 1. Марченко А.Л. C++. Бархатный путь.
2. Молчанов А. Ю. Системное программное обеспечение: Учебник для вузов. 3-е изд.
3. Фленов М.Е. Библия Delphi.- 3-е изд.
Основные этапы физического проектирования программного продукта
Программа - это последовательность инструкций, предназначенных для выполнения компьютером. В настоящее время программы оформляются в виде текста, который записывается в файлы. Этот текст является результатом деятельности программиста и, несмотря на специфику формального языка, остаётся программой для программиста.
Процесс создания программы предполагает несколько этапов:
1 этап - за этапом разработки проекта программы следует этап программирования. На этом этапе пишется программа. Программистами этот текст воспринимается легче двоичного кода, поскольку различные мнемонические сокращения и имена заключают дополнительную информацию.
2 этап - файл с исходным текстом программы (его также называют исходным модулем) обрабатывается транслятором, который осуществляет перевод программы с языка программирования в понятную машине последовательность кодов.
Процесс трансляции разделяется на несколько этапов.
На первом этапе исходный текст (он обычно хранится в виде текстового файла) подвергается лексической обработке. Программа разделяется на предложения, предложение делится на элементарные составляющие (лексемы). Каждая лексема распознаётся (имя, ключевое слово, литерал, символ операции или разделитель) и преобразуется в соответствующее двоичное представление. Этот этап работы транслятора называют лексическим анализом.
Задачи лексического анализа:
выделить в исходном тексте цепочку символов, представляющую лексему;
удалить пробельные символы и комментарии;
зафиксировать в специальных таблицах для хранения разных типов лексем факт появления соответствующих лексем в анализируемом тексте;
преобразовать цепочку символов, представляющих лексему, в пару (тип лексемы, указатель на информацию о ней).
Организация классов таблиц лексического анализатора
На этапе лексического анализа создаются следующие таблицы:
служебных слов языка;
ограничителей;
идентификаторов анализируемой программы;
чисел-констант.
Организация таблиц:
массив записей (упорядоченных или нет);
массив указателей на записи (если структуры разных типов);
список;
бинарное дерево;
хеш-таблицы (чаще всего используются на практике).
Затем наступает этап синтаксического анализа.
Определение 1. Синтаксический анализ – это процесс сопоставления линейной последовательности лексем (слов) языка с его формальной грамматикой. Результатом обычно является дерево разбора.
На этом этапе из лексем собираются выражения, а из выражений - операторы. В ходе трансляции последовательности терминальных символов преобразуются в нетерминалы. Невозможность достижения очередного нетерминала является признаком синтаксической ошибки в тексте исходной программы.
Примечание 1. Символы, встречающиеся только в правой части, называются терминальными символами. Символы, встречающиеся и в левой, и в правой части правил, называются нетерминальными символами, или синтаксическими единицами языка.
После синтаксического анализа наступает этап поэтапной генерации кода. На этом этапе происходит замена операторов языка высокого уровня инструкциями ассемблера, а затем последовательностями машинных команд. Результат преобразования исходного текста программы записывается в виде двоичного файла (его называют объектным модулем) с расширением ".obj" для С++ и ".dcu" для Delphi.
Современные системы программирования поддерживают возможность разработки многомодульных программ, которые строятся из отдельных фрагментов. Модули располагаются в различных файлах, часть из которых может быть независимо от других обработана транслятором. На этапе сборки часть модулей может быть собрана в так называемые загрузочные модули, которые и выполняются процессором.
Процесс разработки многомодульных программ эффективнее, особенно если разрабатывается программа большого размера, когда над реализацией проекта может работать несколько программистов, каждый из которых имеет возможность модифицировать фрагменты программы, не мешая работе остальных.
Обычно структура программы описывается специальными неязыковыми средствами и зависит от конкретной реализации системы программирования. Межмодульные связи поддерживаются специальными файлами проектов, в которых и фиксируется вся необходимая для создания многомодульной программы информация.
3 этап - объектный модуль можно выполнять лишь после специальной дополнительной обработки (компоновки), которая осуществляется специальной программой-компоновщиком.
Определение 2. Компоновщик – модуль системы программирования или самостоятельная программа, которая собирает результирующую программу из объектных модулей и стандартных библиотечных модулей.
Результатом компоновки будет исполняемый файл. С процедурой интерпретации компоновка не связана.
Определение 3. Исполняемый файл – это файл, который может быть обработан или выполнен компьютером без предварительной трансляции. Обычно исполняемый файл получается в результате компиляции и компоновки объектных модулей и содержит машинные команды и/или команды операционной системы. Т.е. он содержит программу ровно в том виде, который способен обработать загрузчик конкретной операционной системы. Чаще всего это файлы с расширением exe или dll.
Рассмотрим в общих чертах процесс компоновки. Программа строится из инструкций и операторов. В свою очередь, операторы включают выражения, которые состоят из операций и операндов. По крайней мере, части операндов в выражениях должны соответствовать отдельные "участки" оперативной памяти, предназначаемые, например, для сохранения результатов вычислений.
В ходе трансляции устанавливается соответствие между операндами и адресами областей памяти вычислительной машины. Так вот задача компоновщика и состоит в согласовании адресов во всех фрагментах кода, из которых собирается готовая к выполнению программа. Компоновщик отвечает за то, чтобы конкретному операнду выражения соответствовала определённая область памяти.
Компоновщик также добавляет к компонуемой программе коды так называемых библиотечных функций (они обеспечивают выполнение конкретных действий - вычисления, вывод информации на экран дисплея и т.д.), а также код, обеспечивающий размещение программы в памяти, её корректное начало и завершение.
Преобразованная компоновщиком программа называется загрузочным или выполнимым модулем. Файлы, содержащие загрузочные модули, называют загрузочными или выполнимыми файлами.
Библиотеки
Языки программирования предназначены для написания программ. Однако было бы странно писать всякий раз одни и те же программы или даже одни и те же подпрограммы (например, подпрограмму вывода информации на дисплей или на принтер - эта подпрограмма требуется практически в каждой программе).
К счастью, проблема многократного использования программного кода уже очень давно и успешно решена.
Практически каждая система, реализующая тот или иной язык программирования (транслятор, компоновщик и прочее программное окружение) имеет набор готовых к использованию фрагментов программного кода. Этот код может находиться в разной степени готовности. Это могут быть фрагменты текстов программ, но, как правило, это объектный код, располагаемый в особых файлах. Такие файлы называются библиотечными файлами.
Для использования библиотечного кода программисту бывает достаточно указать в программе требуемый файл и обеспечить вызов соответствующих функций. Для использования библиотечного кода бывает достаточно стандартного набора языковых средств. Решение всех остальных проблем транслятор и компоновщик берут на себя. Разумеется, программисту должно быть известно о существовании подобных библиотек и о содержании библиотечных файлов.
Тип связывания или тип компоновки
Как правило, связывание осуществляется в два прохода. Первый проход определяет размер каждого модуля и их объединения и создает таблицу имен. Таблица имен связывает каждый символ (например, имя переменной) с адресом, что позволяет компоновщику выполнить разрешение ссылок. На втором проходе компоновщик присваивает адреса блокам данных и команд, а также выполняет разрешение внешних ссылок на символы. Компоновщики также выполняют разрешение внешних ссылок (symbol resolution), при котором внешние ссылки одного модуля привязываются к соответствующим внешним именам другого модуля.
Момент выполнения компоновки программы зависит от среды. Программа может быть скомпонована во время компиляции, если программист обладает всем необходимым кодом в исходном файле, для того чтобы разрешить все ссылки на внешние имена. При этом выполняется поиск в исходном коде всех внешних ссылок и размещения соответствующих внешних символов в итоговом объектном файле. Как правило, этот метод не позволяет получить полноценный результат, поскольку многие программы полагаются на совместно используемые библиотеки (shared library), содержащие совокупность функций, которые могут быть совместно использованы различными процессами. Многие программы могут ссылаться на одни и те же функции (такие как функции библиотеки, которые управляют входными и выходными потоками), не включая их в свой объектный код. Как правило, этот тип связывания осуществляется после компиляции, но перед загрузкой. Связывание также может происходить во время выполнения программы - процесс, называемый динамическим связыванием (dynamic linking). В этом случае разрешение ссылок на внешние функции не выполняется, пока процесс не загрузится в память или не выдаст запрос функции. Это полезно для больших программ, использующих программы сторонних разработчиков, так как при таком подходе нет необходимости перекомпоновывать динамически связанную программу, когда используемая ей библиотека модифицируется. Более того, так как динамически связанные программы не связываются, пока не будут загружены в память, код совместно используемой библиотеки может храниться отдельно от кода других программ. Динамическое связывание также позволяет экономить место на вторичных запоминающих устройствах, поскольку только одна копия совместно используемой библиотеки хранится для любого количества использующих ее программ.
Тип связывания или тип компоновки определяет соответствие имени объекту или функции в программе, исходный текст которой располагается в нескольких модулях. Различают статическое и динамическое связывание.
Статическое связывание бывает внешним или внутренним. Оно обеспечивается на стадии формирования исполнительного модуля, ещё до этапа выполнения программы. Если объект локализован в одном модуле, то используется внутреннее связывание. Тип компоновки специальным образом не обозначается, а определяется компилятором по контексту, местоположению объявлений и использованию спецификаторов класса памяти. Внешнее связывание выполняется компоновщиком, который на этапе сборки многомодульной программы устанавливает связь между уникальным объектом и обращениями к объекту из разных модулей программы.
При динамическом связывании компоновщик не имеют никакого представления о том, какой конкретно объект будет соответствовать данному обращению. Динамическое связывание обеспечивается транслятором в результате подстановки специального кода, который выполняется непосредственно в ходе выполнения программы.
Загрузка
Определение 4. Загрузчик (loader) – часть операционной системы, которая создает для программы отдельный процесс, загружает в оперативную память (в область оперативной памяти, выделенную для процесса) данные исполняемого файла, инициализирует регистры процессора и стартует процесс. С этого момента программа начинает выполняться.
После того как компоновщик создал загрузочный модуль, он передает его программе-загрузчику (loader). Загрузчик отвечает за размещение каждого блока команд и данных по определенному адресу в памяти - процесс, называемый адресным связыванием (address binding'). Существует несколько методов загрузки программ в основную память, большая часть из которых имеют значение только для систем, не поддерживающих виртуальную память. Если загрузочный модуль уже определяет физические адреса в памяти, загрузчик просто размещает блоки команд и данных по адресам, определенным программистом или компилятором (при условии, что эти адреса памяти доступны). Данный процесс называется абсолютной загрузкой (absolute loading). Перемещающая (настраивающая) загрузка (relocatable loading) выполняется в случае, если в загрузочном модуле содержатся относительные адреса, которые необходимо трансформировать в абсолютные адреса памяти. Загрузчик отвечает за запрашивание блока памяти для размещения в нем программы, а затем, за преобразование адресов программы, чтобы та соответствовала своему расположению в памяти. Например, если операционная система выделила программе блок памяти, начиная с адреса 10000, то когда программа загружается, загрузчик должен прибавить 10000 к каждому адресу в загрузочном модуле. Загрузчик поменяет исходный относительный адрес 450 переменной Example на 10450.
Динамическая загрузка (dynamic loading) - это подход, при котором программные модули загружаются при первом использовании. Во многих системах с виртуальной памятью каждому процессу присвоен собственный набор виртуальных адресов, начинающихся с нуля, а загрузчик отвечает за загрузку программы в действительную область памяти.
Трансляция, компиляция и интерпретация
Определение 5. Транслятор - это программа или техническое средство, которая переводит программу на исходном (входном) языке в эквивалентную ей программу на результирующем (выходном) языке.
Транслятор - это часть программного обеспечения, он представляет собой набор машинных команд и данных и выполняется компьютером, как и все прочие программы в рамках операционной системы. Все составные части транслятора представляют собой динамически загружаемые библиотеки или модули этой программы со своими входными и выходными данными.
Исходными данными для работы транслятора служит программа на исходном языке программирования - некоторая последовательность предложений входного языка. Эта программа называется входной или исходной программой. Обычно это символьный файл, но этот файл должен содержать текст программы, удовлетворяющий синтаксическим и семантическим требованиям входного языка. Кроме того, этот файл несет в себе некоторый смысл, определяемый семантикой входного языка. Часто файл, содержащий текст исходной программы, называют исходным файлом.
Выходными данными транслятора является программа на результирующем языке. Эта программа называется результирующей программой. Результирующая программа строится по синтаксическим правилам выходного языка транслятора, а ее смысл определяется семантикой выходного языка. Результирующая программа транслятора в общем случае может быть написана на любом языке - возможен, например, транслятор программ с языка Pascal на язык C. Если текст исходной программы является неправильным, т.е. содержит ошибок с точки зрения синтаксиса и семантики входного языка, то результатом работы транслятора будет сообщение об ошибке (с дополнительными пояснениями и указанием места ошибки в исходной программе).
Важным пунктом в определении транслятора является эквивалентность исходной и результирующей программ. Эквивалентность этих двух программ означает совпадение их смысла с точки зрения семантики входного языка (для исходной программы) и семантики выходного языка (для результирующей программы). Без выполнения этого требования сам транслятор теряет всякий практический смысл.
Определение 6. Компилятор - это транслятор, который осуществляет перевод исходной программы в эквивалентную ей результирующую программу на языке машинных команд или на языке ассемблера.
Входной информацией для компилятора является описание алгоритма или программа на языке программирования. На выходе компилятора – эквивалентное описание алгоритма на машинно-ориентированном языке (объектный код).
Определение 7. Компиляция - преобразование программы, представленной на одном из языков программирования, в коды на машинно-ориентированном языке, которые принимаются и исполняются непосредственно процессором.
Результатом компиляции является объектный файл с необходимыми внешними ссылками для компоновщика. Программа уже переведена в машинные инструкции, однако еще не полностью готова к выполнению. В объектном файле имеются ссылки на различные системные функции. Даже если в программе явно не упомянута ни одна функция, необходим, по крайней мере, один вызов системной функции – завершение программы и освобождение всех принадлежащих ей ресурсов.
Результирующая программа, созданная компилятором, строится на языке машинных кодов или ассемблера, то есть на языках, которые обязательно ориентированы на определенную вычислительную систему. Следовательно, такая результирующая программа всегда предназначена для выполнения на вычислительной системе с определенной архитектурой.
Вычислительная система, на которой должна выполняться результирующая (объектная) программа, созданная компилятором, называется целевой вычислительной системой. В понятие целевой вычислительной системы входит не только архитектура аппаратных средств компьютера, но и операционная система, а зачастую также и набор динамически подключаемых библиотек, которые необходимы для выполнения объектной программы. При этом следует помнить, что объектная программа ориентирована на целевую вычислительную систему, но не может быть непосредственно выполнена на ней без дополнительной обработки. Целевая вычислительная система не всегда является той же вычислительной системой, на которой работает сам компилятор. Часто они совпадают, но бывает так, что компилятор работает под управлением вычислительной системы одного типа, а строит объектные программы, предназначенные для выполнения на вычислительных системах совсем другого типа.
Примечание 2. В современных системах программирования существуют компиляторы, в которых результирующая программа создается не на языке машинных команд и не на языке ассемблера, а на некотором промежуточном языке. Сам по себе этот промежуточный язык не может непосредственно исполняться на компьютере, а требует специального промежуточного интерпретатора для выполнения написанных на нем программ. Хотя в данном случае термин «транслятор» был бы, наверное, более правильным, в литературе употребляется понятие «компилятор», поскольку промежуточный язык является языком низкого уровня, будучи родственным машинным командам и языкам ассемблера.
Этапы компиляции. Общая схема работы компилятора
На рис. 1 представлена общая схема работы компилятора. Из нее видно, что в целом процесс компиляции состоит из двух основных этапов: анализа и синтеза.
На этапе анализа выполняется распознавание текста исходной программы, создание и заполнение таблиц идентификаторов. Результатом его работы служит некое внутреннее представление программы , понятное компилятору.
На этапе синтеза на основании внутреннего представления программы и информации, содержащейся в таблице идентификаторов, порождается текст результирующей программы. Результатом этого этапа является объектный код (модуль). Если программа обращалась к функциям и данным другого модуля, компилятор транслирует эти обращения во внешние ссылки (external reference). Если же программа предоставляет доступ к своим данным и функциям другим программам, каждый доступный элемент представляется как внешнее имя (external name). Объектные модули хранят эти внешние имена и ссылки в структуре данных, называемой таблицей имен (symbol table).
Кроме того, в составе компилятора присутствует часть, ответственная за анализ и исправление ошибок, которая при наличии ошибки в тексте исходной программы должна максимально полно информировать пользователя о типе ошибки и месте ее возникновения. В лучшем случае компилятор может предложить пользователю вариант исправления ошибки.
Эти этапы, в свою очередь, состоят из более мелких этапов, называемых фазами компиляции. Состав фаз компиляции на рис. 1 приведен в самом общем виде, их конкретная реализация и процесс взаимодействия могут, конечно, различаться в зависимости от версии компилятора. Однако в том или ином виде все представленные фазы практически всегда присутствуют в каждом конкретном компиляторе.
Компилятор в целом выполняет две основные функции. Во-первых, он является распознавателем для языка исходной программы. То есть он должен получить на вход цепочку символов входного языка, проверить ее принадлежность языку и, более того, выявить правила, по которым эта цепочка построена. Во-вторых, компилятор является генератором для языка результирующей программы. Он должен построить на выходе цепочку выходного языка по определенным правилам, предполагаемым языком машинных команд или языком ассемблера. В случае машинных команд распознавателем этой цепочки будет выступать целевая вычислительная система, под которую создается результирующая программа.
Рассмотрим основные фазы компиляции.
Лексический анализ (сканер) - это часть компилятора, которая читает литеры программы на исходном языке и строит из них слова (лексемы) исходного языка. На вход лексического анализатора поступает текст исходной программы, а выходная информация передается для дальнейшей обработки компилятором на этапе синтаксического разбора.
Синтаксический разбор - это основная часть компилятора на этапе анализа. Она выполняет выделение синтаксических конструкций в тексте исходной программы, обработанном лексическим анализатором. На этой же фазе компиляции проверяется синтаксическая правильность программы. Синтаксический разбор играет главную роль - роль распознавателя текста входного языка программирования.
Семантический анализ - это часть компилятора, проверяющая правильность текста исходной программы с точки зрения семантики входного языка. Кроме непосредственно проверки, семантический анализ должен выполнять преобразования текста, требуемые семантикой входного языка (например, такие как добавление функций неявного преобразования типов). В различных реализациях компиляторов семантический анализ может частично входить в фазу синтаксического разбора, частично - в фазу подготовки к генерации кода.
Задачи семантического анализа:
каждый используемый в программе идентификатор должен быть описан, но не более одного раза в одной зоне описания;
При вызове функций число фактических параметров и их типы должны соответствовать числу и типам формальных параметров;
Обычно в языке накладываются ограничения на типы операндов любой операции, определенной в этом языке, на типы левой и правой части в присваивании, на тип параметра цикла, на тип условия в операторе цикла и условном операторе, и т п.
Рис. 1. Общая схема работы компилятора.
Подготовка к генерации кода - это фаза, на которой компилятором выполняются предварительные действия, необходимые для синтеза результирующей программы, но не ведущие к порождению текста на выходном языке. Обычно в эту фазу входят действия, связанные с идентификацией элементов языка, распределением памяти и т. п.
Генерация кода - это фаза, непосредственно связанная с порождением текста результирующей программы. Это основная фаза на этапе синтеза результирующей программы. Кроме непосредственно порождения текста результирующей программы генерация обычно включает в себя также оптимизацию - процесс, связанный с обработкой уже порожденного текста. Иногда оптимизацию выделяют в отдельную фазу компиляции, так как она оказывает существенное влияние на качество и эффективность результирующей программы.
Таблицы идентификаторов (иногда «таблицы символов») - это специальным образом организованные структуры данных, служащие для хранения информации об элементах исходной программы, которые затем используются для порождения текста результирующей программы. В конкретной реализации компилятора может быть как одна, так и несколько таблиц идентификаторов. Элементами исходной программы, информацию о которых необходимо хранить в процессе компиляции, являются переменные, константы, функции и т. п. - конкретный состав этих элементов зависит от входного языка. Термин «таблицы» вовсе не предполагает, что это хранилище данных должно быть организовано именно в виде таблиц или массивов информации.
Понятие прохода. Многопроходные и однопроходные компиляторы
Представленное на рис. 1 деление процесса компиляции на фазы на практике может не соблюдаться столь строго. Во-первых, в фазе лексического анализа лексемы выделяются из текста входной программы постольку, поскольку они необходимы для фазы синтаксического разбора. Во-вторых, синтаксический разбор и генерация кода могут выполняться одновременно. Таким образом, эти три фазы компиляции могут работать комбинированно, а вместе с ними может выполняться и подготовка к генерации кода. В реальных компиляторах состав этих фаз может несколько отличаться: некоторые из них могут быть разбиты на составляющие, другие, напротив, объединены в одну фазу. Порядок выполнения фаз компиляции также может меняться в разных вариантах компиляторов. В одном случае компилятор просматривает текст исходной программы, сразу выполняет все фазы компиляции и получает результат - объектный код. В другом варианте он выполняет над исходным текстом только некоторые из фаз компиляции и получает не конечный результат, а набор некоторых промежуточных данных. Эти данные затем снова подвергаются обработке, причем этот процесс может повторяться несколько раз. Реальные компиляторы, как правило, выполняют трансляцию текста исходной программы за несколько проходов.
Проход - это процесс последовательного чтения компилятором данных из внешней памяти, их обработки и помещения результата работы во внешнюю память. Чаще всего один проход включает в себя выполнение одной или нескольких фаз компиляции. Результатом промежуточных проходов является внутреннее представление исходной программы, результатом последнего прохода - объектная программа.
В качестве внешней памяти могут выступать любые носители информации - оперативная память компьютера, накопители на магнитных дисках, магнитных лентах и т. п. Современные компиляторы, как правило, стремятся максимально использовать для хранения данных оперативную память компьютера, и только при недостатке объема доступной оперативной памяти используются накопители на жестких магнитных дисках. Другие носители информации в современных компиляторах не используются из-за невысокой скорости обмена данными.
При выполнении каждого прохода компилятору доступна информация, полученная в результате всех предыдущих проходов. Как правило, он стремится использовать в первую очередь только информацию, полученную на проходе, непосредственно предшествовавшем текущему, но в принципе может обращаться и к данным от более ранних проходов вплоть до исходного текста программы. Информация, получаемая компилятором при выполнении проходов, недоступна пользователю. Она либо хранится в оперативной памяти, которая освобождается компилятором после завершения процесса трансляции, либо оформляется в виде временных файлов на диске, которые также уничтожаются после завершения работы компилятора. Поэтому человек, работающий с компилятором, может даже не знать, сколько проходов выполняет компилятор, - он всегда видит только текст исходной программы и результирующую объектную программу.
Количество выполняемых проходов - это важная техническая характеристика компилятора, солидные фирмы-разработчики компиляторов обычно указывают ее в описании своего продукта. Понятно, что разработчики стремятся максимально сократить количество проходов, выполняемых компиляторами. При этом увеличивается скорость работы компилятора, сокращается объем необходимой ему памяти. Однопроходный компилятор, получающий на вход исходную программу и сразу же порождающий результирующую объектную программу, - это идеальный вариант. Однако сократить число проходов не всегда удается. Количество необходимых проходов определяется, прежде всего, грамматикой и семантическими правилами исходного языка. Чем сложнее грамматика языка и чем больше вариантов предполагают семантические правила - тем больше проходов будет выполнять компилятор. Например, именно поэтому обычно компиляторы с языка Pascal работают быстрее, чем компиляторы с языка C - грамматика Pascal более проста, а семантические правила более жесткие.
Однопроходные компиляторы - редкость, они возможны только для очень простых языков. Реальные компиляторы выполняют, как правило, от двух до пяти проходов. Таким образом, реальные компиляторы являются многопроходными. Наиболее распространены двух- и трехпроходные компиляторы, например: первый проход - лексический анализ, второй - синтаксический разбор и семантический анализ, третий - генерация и оптимизация кода. В современных системах программирования нередко первый проход компилятора (лексический анализ кода) выполняется параллельно с редактированием кода исходной программы.
Принципы работы современных компиляторов
Главная проблема перевода программы с исходного кода в объектный заключается в том, что, в отличие от человека, ни один компилятор не способен понять смысл всей исходной программы в целом. Компилятор способен обнаруживать только самые простейшие семантические (смысловые) ошибки в исходной программе, а бо́льшую часть такого рода ошибок должен обнаруживать человек (разработчик программы или ее пользователь). Этот подход используется при отладке программы. Отладка программы может происходить непосредственно в программно-аппаратной среде этого компьютера либо под управлением специализированного программного обеспечения, предназначенного для отладки и тестирования программ - отладчика. При этом предполагается, что компилятор переводит программу с языка программирования на язык машинных команд без изменения ее смысла (исключаются из рассмотрения ошибки компиляции), а также не рассматриваются ошибки и сбои функционирования самого компьютера - целевой вычислительной системы. Но оценку результатов выполнения программы при отладке выполняет человек.
Многие современные компиляторы позволяют выявить сомнительные с точки зрения смысла места в исходной программе. Такими подозрительными на наличие семантической (смысловой) ошибки местами являются недостижимые операторы, неиспользуемые переменные, неопределенные результаты функций и т. п. Компилятор указывает такие места в виде предупреждений, которые разработчик может принимать или не принимать во внимание. Для достижения этой цели компилятор должен иметь представление о том, как программа будет выполняться, и во время компиляции отследить пути выполнения отдельных фрагментов исходной программы - там, где это возможно. Возможности компиляторов по проверке осмысленности предложений входного языка существенно ограничены. Именно поэтому большинство из них в лучшем случае ограничиваются только рекомендациями по тем местам исходного текста программ, которые вызывают сомнения с точки зрения семантики. Компиляторы обнаруживают только незначительный процент от общего числа смысловых (семантических) ошибок, а следовательно, подавляющее число такого рода ошибок всегда, к большому сожалению, остается на совести автора программы.
Интерпретаторы
Определение 8. Интерпретатор - это программа, которая воспринимает исходную программу на входном (исходном) языке и выполняет ее.
Интерпретатор, так же как и транслятор, анализирует текст исходной программы. Однако он не порождает результирующую программу, а сразу же выполняет исходную программу в соответствии с ее смыслом, заданным семантикой входного языка. Таким образом, результатом работы интерпретатора будет результат, определенный смыслом исходной программы, в том случае, если эта программа синтаксически и семантически правильная с точки зрения входного языка, или сообщение об ошибке в противном случае.
Интерпретатор выполняет программу покомандно, по мере поступления ее исходного кода на вход интерпретатора.
Алгоритм работы простого интерпретатора:
1) прочитать инструкцию;
2) проанализировать инструкцию и определить соответствующие действия;
3) выполнить соответствующие действия;
4) если не достигнуто условие завершения программы, прочитать следующую инструкцию и перейти к пункту 2.
Режим интерпретации можно использовать при отладке программ на языке высокого уровня.
В большинстве случаев интерпретируемая программа работает намного медленнее, чем скомпилированная программа, но не требует затрат на компиляцию, что в случае небольших программ может повышать общую производительность.
Существенное отличие интерпретатора от компилятора: если интерпретатор исполняет команды по мере их поступления, то он не может выполнять оптимизацию исходной программы. Следовательно, фаза оптимизации в общей структуре интерпретатора будет отсутствовать. В остальном структура интерпретатора будет мало отличаться от структуры аналогичного компилятора. Следует только учесть, что на последнем этапе - генерации кода - машинные команды не записываются в объектный файл, а выполняются по мере их порождения.
Далеко не все языки программирования допускают построение интерпретаторов, которые могли бы выполнять исходную программу по мере поступления команд. Отсутствие шага оптимизации ведет к тому, что выполнение программы с помощью интерпретатора является менее эффективным, чем с помощью аналогичного компилятора. Кроме того, при интерпретации исходная программа должна заново разбираться всякий раз при ее выполнении, в то время как при компиляции она разбирается только один раз, а после этого всегда используется объектный файл. Также очевидно, что объектный код будет исполняться всегда быстрее, чем происходит интерпретация аналогичной исходной программы. Таким образом, интерпретаторы всегда проигрывают компиляторам в производительности.
Преимуществом интерпретатора является независимость выполнения программы от архитектуры целевой вычислительной системы. В результате компиляции получается объектный код, который всегда ориентирован на определенную целевую вычислительную систему. Для перехода на другую целевую вычислительную систему исходную программу требуется откомпилировать заново. А для интерпретации программы необходимо иметь только ее исходный текст и интерпретатор с соответствующего языка.
Интерпретаторы долгое время значительно уступали в распространенности компиляторам. Как правило, интерпретаторы существовали для ограниченного круга относительно простых языков программирования (таких, например, как Basic). Высокопроизводительные профессиональные средства разработки программного обеспечения строились на основе компиляторов. Новый импульс развитию интерпретаторов придало распространение глобальных вычислительных сетей. Такие сети могут включать в свой состав компьютеры различной архитектуры, и тогда требование единообразного выполнения на каждом из них текста исходной программы становится определяющим. Поэтому с развитием глобальных сетей и распространением Всемирной сети Интернет появилось много новых систем, интерпретирующих текст исходной программы. Многие языки программирования, применяемые во Всемирной сети, предполагают именно интерпретацию текста исходной программы без порождения объектного кода. Некоторые современные языки программирования предполагают две стадии разработки: сначала исходная программа компилируется в промежуточный код (некоторый язык низкого уровня), а затем этот результат компиляции выполняется с помощью интерпретатора данного промежуточного языка. Широко распространенным примером интерпретируемого языка может служить HTML (Hypertext Markup Language) - язык описания гипертекста. На его основе в настоящее время функционирует практически вся структура сети Интернет. Другой пример - языки Java и JavaScript сочетают в себе функции компиляции и интерпретации (текст исходной программы компилируется в промежуточный код, не зависящий от архитектуры целевой вычислительной системы, этот код распространяется по сети и интерпретируется на принимающей стороне).
Макроязыки и макрогенерация
Разработка программ на языке ассемблера - достаточно трудоемкий процесс, требующий зачастую простого повторения одних и тех же многократно встречающихся операций. Для облегчения труда разработчика были созданы так называемые макрокоманды.
Определение 9. Макрокоманда - текстовая подстановка, в ходе выполнения которой каждый идентификатор определенного вида заменяется на цепочку символов из некоторого хранилища данных.
Определение 10. Макрогенерация - процесс выполнения макрокоманды.
Определение 11. Макрорасширение - цепочка символов, получаемая в результате выполнения макрокоманды.
Процесс выполнения макрокоманд заключается в последовательном просмотре тек-ста исходной программы, обнаружении в нем определенных идентификаторов и замене их на соответствующие строки символов. Причем выполняется именно текстовая замена одной цепочки символов (идентификатора) на другую цепочку символов (строку). Такая замена называется макроподстановкой. Для того чтобы указать, какие идентификаторы на какие строки необходимо заменять, служат макроопределения.
Макроопределения присутствуют непосредственно в тексте исходной программы. Они выделяются специальными ключевыми словами либо разделителями, которые не могут встречаться нигде больше в тексте программы. В процессе обработки все макроопределения полностью исключаются из текста входной программы, а содержащаяся в них информация запоминается в хранилище данных для обработки в процессе выполнения макрокоманд.
Макроопределение может содержать параметры. Тогда каждая соответствующая ему макрокоманда должна при вызове содержать строку символов вместо каждого параметра. Эта строка подставляется при выполнении макрокоманды везде, где в макроопределении встречается соответствующий параметр. В качестве параметра макрокоманды может оказаться другая макрокоманда, тогда она будет рекурсивно вызвана всякий раз, когда необходимо выполнить подстановку параметра. В принципе макрокоманды могут образовывать последовательность рекурсивных вызовов, аналогичную последовательности рекурсивных вызовов процедур и функций, но только вместо вычислений и передачи параметров они выполняют текстовые подстановки.
Макрокоманды и макроопределения обрабатываются специальным модулем, называемым макропроцессором или макрогенератором. Макрогенератор получает на вход текст исходной программы, содержащий макроопределения и макрокоманды, а на выходе его появляется текст макрорасширения исходной программы, не содержащий макроопределений и макрокоманд. Оба текста являются текстами исходной программы на входном языке, никакая другая обработка не выполняется.
Пример макрокоманды: синтаксис макрокоманд и макроопределений зависит от реализации макропроцессора. Но сам принцип выполнения макроподстановок в тексте программы неизменен и не зависит от их синтаксиса. Например, следующий текст макроопределения определяет макрокоманду push_0 в языке ассемблера процессора типа Intel 8086:
push_0 macro xor ax,ax push ax endm
Семантика этой макрокоманды заключается в записи числа «0» в стек через регистр процессора ax.
Тогда везде в тексте программы, где встретится макрокоманда push_0, она будет заменена в результате макроподстановки на последовательность команд xor ax,axpush ax.
Макроязыки и препроцессоры
Макроопределения и макрокоманды нашли применение не только в языках ассемблера, но и во многих языках высокого уровня. Там их обрабатывает специальный модуль, называемый препроцессором языка (например, широко известен препроцессор языка С ). Принцип обработки остается тем же самым, что и для программ на языке ассемблера, - препроцессор выполняет текстовые подстановки непосредственно над строками самой исходной программы.
В языках высокого уровня макроопределения должны быть отделены от текста самой исходной программы, чтобы препроцессор не мог спутать их с синтаксическими конструкциями входного языка. Для этого используются либо специальные символы и команды (команды препроцессора), которые никогда не могут встречаться в тексте исходной программы, либо макроопределения встречаются внутри незначащей части исходной программы - входят в состав комментариев (такая реализация существует, например, в компиляторе с языка Pascal, созданном фирмой Borland). Макрокоманды, напротив, могут встречаться в произвольном месте исходного текста программы, и синтаксически их вызов может не отличаться от вызова функций во входном языке. В совокупности все макроопределения и макрокоманды в тексте исходной программы являются предложениями некоторого входного языка, который называется макроязыком.
Макроязык является чисто формальным языком, поскольку он лишен какого-либо смысла. Семантика макроязыка полностью определяется макрорасширением текста исходной программы, которое получается после обработки всех предложений макроязыка, а сами по себе предложения макроязыка не несут никакой семантики. Препроцессор макроязыка (макрогенератор) в общем случае представляет собой транслятор, на вход которого поступает исходная программа, содержащая макроопределения и макрокоманды, а результирующей программой является макрорасширение программы на исходном языке. Общая схема работы макропроцессора соответствует схеме работы транслятора - он содержит лексический и синтаксический анализаторы для выделения предложений макроязыка в тексте исходной программы, таблицы идентификаторов для хранения макроопределений, генератор предложений исходного языка для выполнения макроподстановок. Отличием является отсутствие семантической обработки входного макроязыка и фазы подготовки к генерации кода.
Макрогенератор, или препроцессор, чаще всего не существует в виде отдельного программного модуля, а входит в состав компилятора. Именно макрорасширение исходной программы поступает на вход компилятора и является для него исходной программой.
Макрорасширение исходной программы обычно недоступно ее разработчику. Более того, макроподстановки могут выполняться последовательно при разборе исходного текста на первом проходе компилятора вместе с разбором всего текста программы, и тогда макрорасширение исходной программы в целом может и вовсе не существовать как таковое. Следует помнить, что, несмотря на схожесть синтаксиса вызова, макрокоманды принципиально отличаются от процедур и функций, поскольку не порождают результирующего кода, а представляют собой текстовую подстановку, выполняемую прямо в тексте исходной программы.
Препроцессор
Определение 12. Препроцессор - это набор функций, предназначенных для предварительной обработки входного текста с целью выделения некоторых конструкций и исключения их из перевода.
При переводе указанные конструкции могут оставаться неизменными или преобразовываться в соответствии с нормами выходного языка. Например, с помощью стандартного препроцессора можно исключать из процесса перевода такие конструкции, как:
адреса электронной почты (E-mail);
универсальные идентификаторы ресурсов (URL);
имена файлов;
даты;
время;
конструкции, не требующие перевода.
В ряде случаев перечисленные выше конструкции неправильно интерпретируются программой перевода и без использования препроцессора могут подвергаться ненужной модификации.
Препроцессор работает следующим образом:
Перед началом перевода препроцессор получает очередную порцию текста, в которой он распознает обрабатываемые им конструкции, запоминает их и заменяет на уникальные метки.
Затем текст переводится, при этом метки остаются неизменными. Метки содержат признаки, отражающие лингвистическую роль данной конструкции в тексте, что позволяет правильно их обрабатывать модулем перевода.
Переведенный текст с информацией о форматировании вновь передается препроцессору, и он заменяет метки на запомненные ранее конструкции, возможно преобразованные в соответствии с особенностями направления перевода, и корректирует форматную информацию.
Функции основных инструментов интегрированной среды разработки
Основные функции редактора текста:
подготовка текста программы (обычные действия по созданию, редактированию, сохранению файла с текстом программы);
многооконный интерфейс с поддержкой режима «буксировки» фрагментов текста мышкой (drag&drop);
интеграция с компилятором: визуализация текста с выделением лексем (синтаксическая подсветка элементов языка); дополнение кода, интерактивная подсказка; шаблоны кода (на «горячих клавишах» – часто используемые программные конструкции); всплывающие подсказки об атрибутах идентификаторов, если на них установить курсор, отображение ошибок, обнаруженных на этапе компиляции, в тексте программы;
интеграция с отладчиком: отображение контрольных точек останова при отладке; отображение текущего значения объекта, при наведении курсора на идентификатор.
Основные функции отладчика:
пошаговое выполнение программы (шаг = строка, с трассировкой внутри вызываемой функции или без нее);
выполнение программы до строки, в которой в редакторе стоит курсор;
выделение выполняемой строки в данный момент;
приостановка выполнения программы;
можно запросить значение переменной;
можно заказать вычисление некоторого выражения;
можно изменить значение переменной и продолжить выполнение программы;
расставить/снять точки останова, которые визуализируются в текстовом редакторе;
вся информация должна выдаваться в терминах исходной программы.
Назначение и функционирование редактора связей.
Он должен разрешить межмодульные связи (для объектных файлов, порождаемых компилятором при раздельной трансляции модулей, составляющих программу).
Должен связать объектные файлы, порожденные компилятором, и библиотечные файлы, входящие в состав системы программирования (для статически связываемых библиотек).
Загрузчик.
Загрузчик обеспечивает подготовку готовой программы к выполнению, обрабатывают ресурсы, полученные с выхода компиляторов. Модуль, выполняющий преобразование относительных адресов в абсолютные непосредственно в момент запуска программы на выполнение.
Библиотеки. Основные типы библиотек.
Библиотеки функций (определяют возможности системы) - различают библиотеки для языков программирования и библиотеки для решения задач в конкретной проблемной области, представляют собой откомпилированные объектные модули.
Библиотеки классов (все ее классы должны быть написаны на том же языке программирования, на котором пишется программа) - различают конкретные классы, абстрактные классы, шаблоны классов, включаются в программу на этапе компиляции.
Библиотеки компонент - это готовые откомпилированные программные модули, предназначенные для использования в качестве составной части программы, и которыми можно манипулировать во время разработки программы, компоненты бывают локальные и распределенные; содержат объявление и реализацию интерфейса.
Профайлер.
Этот инструмент используется для оптимизации программного кода по скорости его выполнения и занимаемой им оперативной памяти. С помощью него можно собрать статистику, какая часть кода выполняется чаще всего, и сколько времени и ресурсов на ее выполнение тратит компьютер. На основе этой статистики можно выявить “узкие места” вашей программы и направить свои усилия на их оптимизацию.
Компиляция и выполнение проекта Delphi
В процессе компиляции проекта создается готовый к использованию файл, которым может быть приложение (ЕХЕ) или динамически загружаемая библиотека (DLL). Далее будем рассматривать только файл-приложение. Имя приложения, получаемого в результате компиляции, совпадает с именем файла проекта, а само приложение является автономным и не требует для своей работы дополнительных файлов Delphi.
Запуск процесса компиляции выполняется по команде Project | Compile <Project1> (Проект | Компилировать <проект1>) или нажатием комбинации клавиш <Ctrl>+<F9>. В этой команде содержится имя проекта, разработка которого выполняется в настоящий момент, первоначально это Projectl. При сохранении проекта под другим именем соответственно должно измениться имя проекта в команде меню.
Компиляция проекта для получения приложения может быть выполнена на любой стадии разработки проекта. Это удобно для проверки вида и правильности функционирования отдельных компонентов формы, а также для проверки отдельных фрагментов создаваемого кода.
При компиляции проекта выполняются следующие действия:
компилируются файлы всех модулей, содержимое которых изменилось со времени последней компиляции. В результате для каждого файла с исходным текстом модуля создается файл с расширением DCU. Если исходный текст модуля по каким-либо причинам недоступен компилятору, то он не перекомпилируется;
если в модуль были внесены изменения, то перекомпилируется не только этот модуль, но и использующие его с помощью директивы uses модули;
перекомпиляция модуля происходит также при изменениях объектного файла или подключаемого файла (INC), используемых данным модулем;
после компиляции всех модулей проекта компилируется файл проекта и создается исполняемый файл приложения с именем файла проекта.
Кроме компиляции может быть выполнена сборка проекта. При сборке компилируются все файлы, входящие в проект, независимо от того, были в них внесены изменения или нет. Для сборки проекта используется команда меню Project | Build <Project1> (Проект | Собрать <проект1>).
Запустить проект на выполнение можно из среды Delphi и из среды Windows. Выполнение проекта из среды Delphi осуществляется командой Run | Run (Выполнение | Выполнить) или нажатием клавиши <F9>. При этом созданное приложение начинает свою работу. Если в файлы проекта вносились изменения, то предварительно выполняется компиляция проекта. Запущенное приложение работает так же, как и запущенное вне среды Delphi, однако имеются некоторые особенности:
нельзя запустить вторую копию приложения;
продолжить разработку проекта можно только после завершения работы приложения;
При зацикливании (зависании) приложения его завершение необходимо выполнять средствами Delphi с помощью команды Run | Program Reset (Выполнение | Остановить программу) или комбинации клавиш <Ctrl>+<F2>.
Для отладки приложений в среде Delphi можно использовать средства отладчика.
Из среды Windows созданное приложение можно запустить как и любое другое приложение, например, с помощью Проводника.
Лекция 12 тема: Управление версиями пп.
Литература: 1. Бен Коллинз-Сассман, Брайан У. Фитцпатрик, К. Майкл Пилато Управление версиями в Subversion. URL: http://svnbook.red-bean.com/nightly/ru/index.html.
2. Наталия Елманова. Borland StarTeam 6.0// КомпьютерПресс 6'2004.
3. IBM Rational ClearCase. Управление версиями файлов, сборкой. URL: http://cmcons.com/tech_rational/IBM_Rational_ClearCase.
Управление изменениями в проектах производится на всех этапах создания приложения. Это одна из самых важных составных частей проекта, ведь изменения могут происходить и в требованиях, и в коде, и в моделях, созданных на этапах бизнес-моделирования и проектирования. Без отслеживания изменений и своевременного оповещения о них тех участников проекта, которых касаются внесенные изменения, управлять проектом сложно: руководитель проекта должен быть в курсе того, что именно происходит на данном этапе и что уже реализовано в проекте, иначе он рискует вообще никогда не завершить проект.
Если же вы хотите знать, кто и когда поменял ту или иную строчку в файле исходного кода программы или сайта, сделать работу двух или более программистов над одним проектом прозрачной, простой и легко контролируемой, или хотите иметь возможность отменить изменения в коде, сделанные неделю назад, а также иметь возможность доступа к исходникам старой версии программы, при этом не заботясь о своевременном создании резервных копий, то вам необходимо использовать систему управления версиями.
Современные системы для управления версиями должны отслеживать изменения не только исходного кода приложений, но и моделей, документов, наборов данных и многого другого.
На данный момент предлагаются различные системы для управления версиями, такие как Microsoft Visual SourceSafe, StarBase’s StarTeam, Rational ClearCase, Subversion и т.д.
Microsoft Visual SourceSafe (VSS) поддерживает клиент-серверный режим работы и предназначен для небольших команд разработчиков, позволяет хранить в общем хранилище файлы, разделяемые несколькими пользователями, для каждого файла хранится история версий. VSS входил в состав пакета Microsoft Visual Studio 2005 и был интегрирован с продуктами этого пакета. Доступен только для платформы Windows. Версию для Unix поддерживает компания MainSoft. Сегодня на замену SourceSafe предлагается новый продукт Майкрософт - Team Foundation Server.
Borland StarTeam - масштабируемое средство управления конфигурациями программного обеспечения, хранящее в централизованном репозитарии все необходимые данные и облегчающее взаимодействие сотрудников, ответственных за выполнение различных задач. Этот продукт предоставляет команде участников проекта разнообразные средства для публикации требований, управления задачами, планирования, работы, обсуждения изменений, контроля версий, организации документооборота. Поддерживаются разные клиентские интерфейсы (Windows, UNIX, Linux и Web-интерфейсы), интегрируется с Microsoft Project.
ClearCase – инструментальное средство Rational Software для осуществления конфигурационного управления. Оно осуществляет управлением версий, рабочим пространством, сборкой и процессами. ClearCase является масштабируемым средством клиент/сервер и отвечает за хранение и отслеживание всех артефактов проекта. Разработчики и менеджеры могут следить за ходом изменений в реальном масштабе времени, получая нужные версии для редактирования и просмотра без дополнительной синхронизации с базой, что в разы увеличивает «поворотливость» больших систем, позволяя быстро переходить от версии к версии, от проекта к проекту. ClearCase позволяет достаточно просто вернуться к предыдущей версии, получив при этом полный набор артефактов проекта (версии исходных файлов, документация, требования, модели, скрипты тестирования), соответствующий предыдущей версии, и на его основе построить новую версию.
Как правило, ClearCase выбирают компании с большим числом сотрудников и с большим числом проектов, которые необходимо поддерживать одновременно. Данный выбор вполне оправдан, так как на данный момент CC является единственным средством, позволяющим работать с любым подмножеством проектов как локально, так и распределено (объединение географически удаленных команд разработчиков).
Subversion - это свободная система управления версиями с открытым исходным кодом. Она позволяет управлять файлами и каталогами во времени и представляет собой систему общего назначения, которую можно использовать для управления любым набором файлов. Для Вас это будут исходники Ваших программ, а для кого-то другого это будет список продуктов или сведённое цифровое видео. Дерево файлов помещается в центральное хранилище, которое похоже на обычный сервер файлов с тем отличием, что оно запоминает каждое изменение, внесённое в файл или каталог. Это позволяет восстановить ранние версии данных, исследовать историю изменений данных. Благодаря этому, многие считают систему управления версиями своеобразной «машиной времени». Subversion разработана специально для замены CVS, самой распространённой открытой системы управления версиями. Она обладает всеми основными функциями CVS (хотя некоторые из них выполняет другими способами) и лишена ряда её недостатков. Subversion часто называют «svn», по названию клиентской программы, входящей в её дистрибутив.
Работа с Subversion Возможности
Хранение полной истории изменений отслеживаемых объектов (файлов, каталогов, символьных ссылок) в централизованном хранилище (репозитории), в том числе при изменении атрибутов («метаданных»), перемещении, переименовании и удалении. Метаданные: каждый файл и каталог имеет собственный набор свойств, представленных в виде названия и значения. Вы можете создавать и сохранять любые необходимые пары названий свойств и их значений. Свойства файлов точно так же находятся под управлением версиями, как и их содержимое.
Копирование объектов с разветвлением истории - при копировании в хранилище появляются два отдельных объекта с общей историей.
Настоящая история версий: в Subversion можно заменить файл, помещённый под управление версиями, другим файлом с тем же именем, но совершенно иным содержанием, возможно никак не связанным со старым объектом, без наследования таким элементом всей истории изменений. Subversion делает возможным добавление, удаление, копирование и переименование как файлов, так и каталогов. При этом каждый вновь добавленный файл начинает жизнь с чистого листа, сохраняя собственную историю изменений.
Поддержка переноса изменений между копиями объектов, в том числе полного слияния копий (в рабочей копии; без объединения истории).
Поддержка ветвления:
создания ветвей (копированием директорий) и работы с ними;
слияние ветвей (переносом изменений).
Поддержка меток (копированием директорий).
История изменений и копии объектов (в том числе ветви и метки) хранятся в виде связанных разностных копий - «дешёвых» (не требующих больших временны́х и дисковых ресурсов) при создании и хранении. Subversion создаёт ветки и метки путём простого копирования проекта, используя механизм, похожий на жёсткие ссылки в файловых системах. Благодаря этому, операции по созданию веток и меток занимают немного времени.
Поддержка конкурентной (в том числе одновременной, с изоляцией транзакций) многопользовательской работы с хранилищем и, в большинстве случаев, автоматическим слиянием изменений различных разработчиков (в рабочей копии).
Фиксации изменений в хранилище (в том числе многообъектные) организуются в виде атомарных транзакций: каждый набор изменений либо попадает в хранилище целиком, либо не попадает туда вовсе. Это позволяет разработчикам создавать и фиксировать изменения логически оправданными кусками, предотвращая тем самым проблемы, которые могут возникать в тех случаях, когда только часть необходимых изменений помещается в хранилище успешно.
Сетевой обмен между сервером и клиентом предусматривает передачу только различий между рабочей копией и хранилищем.
Обеспечивается одинаково эффективная работа как с текстовыми, так и с двоичными файлами. Subversion обнаруживает различия между файлами с помощью специального бинарного алгоритма, который одинаково работает как с текстовыми, так и с бинарными файлами. Файлы записываются в хранилище в сжатом виде независимо от их типа, а различия между отдельными версиями могут передаваться по сети в обоих направлениях.
Различные варианты доступа к хранилищу, в том числе:
непосредственный доступ на локальной файловой системе;
имеется лёгкий самостоятельный сервер Subversion, который использует собственный протокол взаимодействия с клиентами и может легко туннелировать данные через SSH.
Subversion может быть подключена к серверу HTTP Apache по протоколу WebDAV/DeltaV в виде модуля, что даёт ей огромное преимущество с точки зрения устойчивости работы и способности к взаимодействию, а также предоставляет прямой доступ к существующим возможностям этого сервера, включая установление личности, проверку прав доступа и сжатие информации при передаче.
Два возможных внутренних формата хранилища: база данных или набор обычных файлов.
Дружелюбность по отношению к разработчикам: Subversion реализована в виде набора динамических библиотек на языке C, API которых хорошо известен. Это делает Subversion чрезвычайно удобной для сопровождения системой, пригодной для взаимодействия с другими приложениями и языками программирования.
И др.
Модель работы
Subversion - централизованная система (в отличие от распределённых систем, таких как Git или Mercurial), то есть данные хранятся в едином хранилище. Хранилище может располагаться на локальном диске или на сетевом сервере.
Работа в Subversion мало отличается от работы в других централизованных системах управления версиями. Клиенты копируют файлы из хранилища, создавая локальные рабочие копии, затем вносят изменения в рабочие копии и фиксируют эти изменения в хранилище. Несколько клиентов могут одновременно обращаться к хранилищу. Для совместной работы над файлами в Subversion преимущественно используется модель копирование - изменение - слияние. Кроме того, для файлов, не допускающих слияние (различные бинарные форматы файлов), можно использовать модель блокирование - изменение - разблокирование.
При сохранении новых версий используется дельта-компрессия: система находит отличия новой версии от предыдущей и записывает только их, избегая дублирования данных.
При использовании доступа с помощью WebDAV также поддерживается прозрачное управление версиями - если любой клиент WebDAV открывает для записи и затем сохраняет файл, хранящийся на сетевом ресурсе, то автоматически создаётся новая версия.
Типы репозиториев
Subversion предлагает два варианта организации репозиториев. Репозитории первого типа используют для хранения базы данных на основе Berkeley DB, репозитории второго типа - обычные файлы специального формата (доступ к данным организуется с помощью собственных библиотек, без использования сторонних баз данных). Разработчики Subversion часто называют хранилище «файловой системой», поэтому второй тип получил название FSFS, то есть версионированная файловая система поверх обычной файловой системы.
Оба типа репозиториев обеспечивают достаточную надёжность при правильной организации (Berkeley DB использует блокировки файлов, поэтому её нельзя использовать на некоторых сетевых файловых системах, не поддерживающих блокировок), каждая из них обладает своими преимуществами и недостатками. Считается, что FSFS легче правильно настроить, она требует меньшего внимания от администратора. Кроме того, до релиза 1.4 хранилища, использующие Berkeley DB могли при определённых условиях оказаться в так называемом заклиненном состоянии; требовалось вмешательство администратора для восстановления его работоспособности. Начиная с релиза 1.2 для новых хранилищ по умолчанию используется FSFS.
Файловая система
Рис. 1. Двумерное представление файловой системы в Subversion.
С точки зрения пользователя хранилище Subversion представляет собой «двумерную» файловую систему. Объекты в хранилище (файлы и директории) идентифицируются двумя «координатами»: именем и номером ревизии. Другими словами, хранилище представляет собой массив мгновенных снимков (ревизий) дерева файлов и директорий, индексируемый номером ревизии. Каждый такой снимок - обычная (одномерная) файловая система.
При необходимости указания на конкретную ревизию объекта используется запись вида: имя@ревизия, например, /main.c@29 - файл /main.c в ревизии 29. Такое указание ревизии, используемое для уточнения имени, называется стержневая ревизия.
Также различают еще оперативные ревизии. Ревизия называется оперативной, если она указывается как ревизия или диапазон ревизий, к которому должна быть применена команда, например:
svn log -r 199:230 http://some.path
В данном примере выполняется команда svn log для диапазона ревизий 199:230, который и является диапазоном оперативных ревизий.
На рис. 1 показано графическое представление файловой системы: вертикальная ось соответствует множеству имён, горизонтальная - множеству ревизий.
Номера ревизий
Номер ревизии в Subversion - это натуральное число (или 0 для самой первой ревизии), адресующее номер состояния хранилища в процессе изменения содержащихся в нём данных. Каждая успешная фиксация изменений порождает ровно одну новую ревизию в хранилище, то есть N-я ревизия - это состояние хранилища после N-й фиксации.
В Subversion ревизия характеризует состояние не отдельного файла, а всего хранилища в целом. Например, ревизия 32 (обведено пунктиром на рисунке) - это состояние трёх файлов и двух директорий, существовавших в хранилище на тот момент.
Номер ревизии является аналогом времени в том смысле, что меньшие номера ревизий соответствуют более ранним состояниям хранилища, а бо́льшие - поздним.
Минимальный номер ревизии 0 (ноль) соответствует изначальному состоянию хранилища, когда ещё не было зафиксировано ни одной правки. В нулевой ревизии хранилище содержит только пустую корневую директорию.
Максимальный номер ревизии соответствует самому последнему состоянию хранилища, то есть состоянию после фиксации последней правки. Вместо указания номера последней ревизии можно использовать ключевое слово HEAD (головная ревизия); это удобно, поскольку номер головной ревизии увеличивается при каждой фиксации изменений. Указание номера ревизии удобнее, чем указание времени, так как нет путаницы с часовыми поясами, запись номера короче и номер ревизии не может быть изменён.
Операции над файловой системой
Над объектами файловой системы в хранилище Subversion могут быть произведены перечисленные ниже операции (см. рис. 1). В скобках указано краткое именование операции в обозначениях команды svn status.
Добавление (A). Добавление объекта в файловую систему. Добавленный объект не имеет истории ревизий. Пример на рисунке: файл /main.c был добавлен в ревизии 27.
Модификация (M). Модификация объекта, например, изменение содержимого файла или изменение свойств файла или директории. Пример на рисунке: файл /main.c был модифицирован в ревизии 28.
Удаление (D). Удаление файла из головной и последующих ревизий. При этом файл остаётся в предыдущих ревизиях. Пример на рисунке: файл /main.c был удалён в ревизии 30.
Добавление с историей (A+). Представляет собой копирование объекта внутри файловой системы хранилища, то есть объект имя_источника@ревизия_источника копируется в имя_копии@HEAD. Скопированный объект наследует от источника историю ревизий до момента копирования (наследование истории показано на рисунке пунктирными связями). Примеры на рисунке: в ревизии 29 директория /tags/R1 была скопирована с директории /trunk@27;
в ревизии 31 файл /main.c был скопирован с /main.c@29, то есть с более ранней ревизии самого себя, таким образом, произведено восстановление ранее удалённого (в ревизии 30) файла с сохранением истории ревизий.
Замена (R+). Имеет место в случае, когда в одной ревизии произведено и удаление объекта (D), и добавление с историей (A+) объекта с тем же самым именем. Хотя имя при операции замены остаётся неизменным, Subversion рассматривает объект до и после замены как два различных объекта с различными историями ревизий (история старого заканчивается в точке замены, история нового наследуется от источника копирования и продолжается далее). Пример на рисунке: в ревизии 30 файл /file.txt был заменён: старый файл /file.txt удалён, а новый файл с тем же именем скопирован с файла /bar.txt@29.
Фиксация изменений Рабочая копия
Рабочая копия - это созданная клиентской программой Subversion локальная копия части данных из хранилища, содержащая помимо собственно данных некоторую служебную информацию (скрытые директории с именем .svn). Служебная информация необходима для правильного функционирования рабочей копии; что-либо менять в служебных данных нельзя. Минимальной единицей данных, которую можно получить из хранилища как рабочую копию, является директория (можно также извлечь директорию без поддиректорий, а потом докачивать их по мере необходимости). Другими словами, в Subversion рабочая копия всегда соответствует ровно одной директории хранилища. Извлечь из хранилища отдельный файл как рабочую копию невозможно.
Любая поддиректория рабочей копии Subversion 1.6 и более ранних версий также является полноценной рабочей копией, поскольку в каждой директории хранятся её служебные данные (каталоги .svn). Начиная с версии 1.7 в каждой рабочей копии присутствует только одна директория .svn в корне её каталога.
Рабочая копия является самодостаточной в том смысле, что Subversion не хранит каких-либо данных, относящихся к рабочей копии, вне её. Поэтому, имея одну рабочую копию, можно сделать ещё несколько копий простым копированием без затрат сетевого трафика.
В служебных директориях рабочей копии, помимо прочего, хранится так называемая чистая копия - файлы рабочей копии в неизменённом виде, как они были извлечены из хранилища (для svn это ревизия с именем BASE). Наличие чистой копии позволяет быстро и без обращения к хранилищу выполнять операции просмотра и отката локальных изменений. Однако размер рабочей копии на диске примерно в два раза больше (данные + чистая копия данных), чем размер самих данных. Такой подход обусловлен тем, что дисковые ресурсы дешевле и доступнее, чем ресурсы сети передачи данных.
Как правило, создание рабочей копии является первым и необходимым этапом для фиксации локальных изменений, поскольку зафиксировать в хранилище можно только изменения, сделанные в рабочей копии. Исключением являются операции, которые могут быть выполнены прямо в хранилище без создания рабочей копии.
Структура хранилища Создание хранилища
Для создания хранилища используется команда svnadmin create. Эта операция создаст пустое хранилище в указанной директории.
Структура проекта в хранилище
Формально Subversion не накладывает каких-либо ограничений на файловую структуру проекта - она может быть какой угодно в рамках правил именования объектов файловой системы. Тем не менее, существуют рекомендации, призванные облегчить работу с ветвями и метками. В простейшем случае в корневой директории хранилища рекомендуется создать как минимум три поддиректории:
/.
trunk
branches
tags
где вся файловая структура проекта (основной линии разработки) должна содержаться в поддиректории trunk, поддиректория branches должна содержать ветви проекта, а tags - метки. Например, следующая структура директорий хранилища:
/.
trunk
branches
branch_1
tags
tag_1
tag_2
предполагает наличие у проекта (trunk) одной ветви «branch_1» и двух меток «tag_1» и «tag_2». Каждая из этих директорий (trunk, branch_1, tag_1 и tag_2) содержит копию соответствующей версии проекта.
Если проектов в хранилище несколько, то такая структура поддиректорий создается для каждого проекта:
/.
project1
trunk
branches
tags
project2
trunk
branches
tags
то есть в корневой директории находятся директории проектов, и в каждом из них есть свои trunk, branches, tags, относящиеся только к этому проекту. Описанные структуры директорий хранилища являются лишь примерами, на практике хранилище можно организовать таким способом, который оптимально подходит в данном конкретном случае.
Другим способом хранения нескольких проектов является создание нескольких хранилищ. В разных хранилищах следует располагать проекты, которые никак не связаны между собой, поскольку между хранилищами нельзя будет выполнить операции копирования, перемещения и слияния. Несколько хранилищ можно при необходимости объединить в одно с сохранением истории ревизий (путём импорта командой svnadmin load с параметром --parent-dir).
Ветви
Subversion использует «файловую» модель для реализации ветвей и меток, то есть ветвь является обычной директорией (можно также сделать ветвь из одного файла, а не директории). Новая ветвь создаётся командой svn copy. Ветвь может быть создана в любой директории хранилища, однако имеет смысл придерживаться описанных выше соглашений о том, где создавать новые ветви.
Ветвь - направление разработки, независимое от других. Ветвь представляет собой копию части хранилища (например, одного каталога), в которую можно вносить изменения, не влияющие на другие ветви. Документы в разных ветвях имеют одинаковую историю до точки ветвления и разные - после неё.
Системы управления версиями предоставляют инструменты для манипуляции ветвями, прежде всего создание ветви и слияние изменений с другой ветвью.
Существуют стволовая, релизная и функциональная ветви.
Стволовая ветвь
История изменений каждого документа в хранилище представляет собой древовидную структуру. Стволовая ветвь (trunk) - это основное направление разработки. Большая часть ветвлений и слияний происходит с ней. Стволовая ветвь создаётся один раз при создании нового хранилища и существует на протяжении всего жизненного цикла проекта. Все остальные ветви создаются для определённых целей и различаются по своему назначению.
Релизная ветвь
Перед выпуском очередной версии релиза программного обеспечения недопустимо вносить потенциально дестабилизирующие изменения в исходный код. Поэтому перед выпуском обычно создаётся релизная ветвь (release branch или tag), изменения в которой строго регламентированы. В основном в неё попадают исправления серьёзных ошибок, обнаруженных при подготовке версии. Все остальные изменения вносятся в стволовую ветвь. Таким образом, стабильность кода на релизной ветви не нарушается, и релиз выпускается из кода этой ветви. В дальнейшем можно путем слияния перенести исправления, сделанные на релизной ветви, и на стволовую ветвь. Как правило, релизная ветвь после выпуска версии не удаляется. Она может понадобиться для воспроизведения состояния проекта на момент выпуска.
Функциональная ветвь
Функциональная ветвь (functional branch) создаётся для выполнения серии дестабилизирующих изменений без влияния на стволовую ветвь. Например, нужно добавить в код новую функциональность, но изменения настолько сложны, что их нельзя выполнить за одну фиксацию. Либо требуется участие нескольких человек. В этом случае создаётся ветвь, в которую вносятся дестабилизирующие изменения. При этом код на ветви может продолжительное время пребывать в нестабильном состоянии. Когда изменения выполнены и код приведён в стабильное состояние, производится слияние изменений в стволовую ветвь. Таким образом, на стволовой ветви изменения, сделанные на функциональной ветви, выглядят как одна фиксация (фиксация, которой выполнено слияние), при этом нестабильных промежуточных состояний на стволовой ветви нет. Они есть только на функциональной ветви, где их можно посмотреть при необходимости. После слияния жизненный цикл функциональной ветви закончен, её можно удалить.
Метки
Создание метки также производится командой svn copy, то есть технически не отличается от создания ветви. Отличие только в способе использования: предполагается, что никто не будет изменять данные в метке (фиксировать в неё изменения). Например, на рис. 1 метка создана в ревизии 29: директория /trunk из ревизии 27 скопирована под именем /tags/R1. Теперь, если не изменять данные в директории /tags/R1, то она навсегда останется точной копией директории /trunk@27, то есть будет меткой.
Концепция меток, используемая в Subversion, отличается от концепции меток в других системах управления версиями. Обычно метка является символическим именем, которое адресует набор файлов и их состояние. В Subversion метка копирует набор файлов и их состояние. Метки-копии в Subversion имеют свои достоинства и недостатки.
Достоинство: метка видна в структуре директорий, можно сделать удобную древовидную организацию меток.
Недостатки:
трудно узнать, в какие метки вошёл файл (то же для директории);
если права доступа установлены индивидуально для директорий, то метка эти права не наследует;
содержимое метки может быть изменено;
если из метки создать рабочую копию и зафиксировать из этой рабочей копии какие-либо изменения, то это изменит саму метку, а не те данные, которые были помечены. Правильным способом работы «от метки» является создание рабочей копии не из метки, а из того, что является источником этой метки.
Ветвление
Ветвление является важным аспектом работы систем управления версиями, поскольку типичные приёмы управления версиями (по крайней мере, при разработке программного обеспечения) подразумевают использование ветвей. Subversion обладает достаточно развитыми возможностями для ветвления и слияния (однако не поддерживает слияние переименованных файлов и директорий).
Рис. 2. Пример эволюции ветвей в Subversion.
На рис. 2 условно показан пример эволюции ветвей в хранилище. Зелёным цветом показана основная линия разработки проекта, жёлтым - ветви, синим - метки, пурпурным - ветвь, разработка которой прекращена. Красными стрелками показаны слияния изменений.
Создание ветвей
Новая ветвь (а также метка) создаётся командой svn copy, которая создаёт в хранилище копию с наследованием истории ревизий источника (операция A+). Для создания ветвей всегда следует использовать «удалённую» форму команды svn copy, например:
svn copy http://.../trunk/dir http://.../branches/branch_name -m "Creating a branch of dir"
Полученная копия будет ветвью (или меткой, в зависимости от способа работы с ней). В дальнейшем изменения, сделанные на ветви, могут быть внесены в источник, из которого была создана эта ветвь, такое распространение изменений называется слияние.
Операции копирования в Subversion дешёвые, то есть требуют небольшого фиксированного количества времени и дискового пространства. Хранилище спроектировано таким образом, что при любом копировании происходит не дублирование данных, а создание ссылки на источник (аналогично жёсткой ссылке), однако этот механизм чисто внутренний - с точки зрения пользователя происходит именно создание копии. Благодаря высокой эффективности создания ветвей их можно создавать настолько часто, насколько это необходимо (однако слияние - необходимое дополнение к ветвлению - выполняется в Subversion медленнее, чем в других современных системах управления версиями).
Работа с ветвями
В целом работа на ветви не отличается от работы на основной линии разработки (trunk). Специфичные команды требуются только для действий, в которых задействовано более одной ветви. К таким командам относятся (помимо команды создания ветви svn copy):
svn switch - переключение рабочей копии на другую ветвь - используется для того, чтобы переключить имеющуюся рабочую копию на другую ветвь. В результате переключения служебные данные рабочей копии изменяются так, как будто эта рабочая копия получена операцией svn checkout из той ветви, на которую она переключена. При этом объём сетевого трафика меньше, чем при svn checkout, так как передается только разница между данными в рабочей копии и целевой ветвью;
svn merge - копирование набора изменений между ветвями - используется для слияния ветвей.
Как правило, полный цикл работы с ветвями включает следующие этапы:
создание ветви (svn copy);
переключение имеющейся рабочей копии на ветвь (svn switch) или создание новой рабочей копии путём закачки (svn checkout);
изменение файлов и директорий в рабочей копии, фиксация этих изменений (svn commit);
копирование в ветвь свежих изменений из родительской ветви, сделанных после ветвления (svn merge, svn commit);
копирование изменений из ветви в родительскую ветвь (svn merge, svn commit);
удаление ветви (svn delete), если её жизненный цикл закончен.
Слияние - Копирование изменений между ветвями
Слияние в Subversion - это применение к ветви набора изменений, сделанных на другой (или той же самой) ветви. Для осуществления слияния необходимо использовать команду svn merge - она применяет набор изменений к рабочей копии; затем нужно зафиксировать внесённые изменения (svn commit).
Терминология, связанная со слиянием, несколько запутана. Термин слияние является не совсем точным, поскольку как такового объединения ветвей не происходит. Кроме того, не следует отождествлять слияние и команду svn merge: во-первых, для слияния нужно выполнить (помимо svn merge) разрешение конфликтов и фиксацию, во-вторых, применение svn merge не ограничивается слиянием.
Другие применения команды svn merge
Команду svn merge можно использовать не только для слияния. Фактически команда производит внесение в рабочую копию изменений, равных разнице между двумя директориями или файлами в хранилище, поэтому svn merge является универсальным средством для переноса изменений. Можно привести такие примеры использования команды:
откат уже зафиксированных изменений, в том числе целого диапазона ревизий;
удобный просмотр (в виде изменений в рабочей копии) разницы между двумя состояниями репозитория.
Простейший рабочий цикл
Типичный рабочий цикл выглядит примерно так:
Обновление рабочей копии из хранилища (svn update) или ее создание (svn checkout).
Внесение изменений в рабочую копию. Изменения директорий и информации о файлах производится средствами Subversion, в изменении же содержимого файлов Subversion никак не задействован - изменения производятся программами, предназначенными для этого (текстовые редакторы, средства разработки и т. п.):
новые (еще не зафиксированные в хранилище) файлы и директории нужно добавить (svn add), то есть передать под управление версиями;
если файл или директорию в рабочей копии нужно удалить, переименовать, переместить или скопировать, необходимо использовать средства Subversion (svn mkdir, svn delete, svn move, svn copy);
Анализ изменений.
Просмотр состояния рабочей копии и локальных (ещё не зафиксированных) изменений (svn info, svn status, svn diff);
любые локальные изменения, если они признаны неудачными, можно откатить (svn revert).
Слияние изменений, выполненных другими, с вашей рабочей копией
svn update
svn resolved
Фиксация изменений или результатов слияния в хранилище.
svn commit
Обновление рабочей копии
При командной работе над проектом обновление рабочей копии необходимо для получения любых изменений, внесенных другими разработчиками проекта с момента вашего последнего обновления. Используйте svn update для синхронизации вашей рабочей копии с последней правкой в хранилище.
$ svn update
U foo.c
U bar.c
Updated to revision 2.
В данном случае, кто-то другой зафиксировал изменения в файлах foo.c и bar.c после вашего последнего обновления, и Subversion обновила вашу рабочую копию, включив эти изменения.
Рассмотрим поподробнее информацию, выводимую командой svn update. Когда сервер отправляет изменения в вашу рабочую копию, для каждого элемента выводится латинская буква - код, определяющий действие, выполненное Subversion для приведения вашей рабочей копии в актуальное состояние:
U foo
Файл foo был Updated - обновлен (получил изменения с сервера).
A foo
Файл или каталог foo был Added - добавлен в рабочую копию.
D foo
Файл или каталог foo был Deleted - удален из рабочей копии.
R foo
Файл или каталог foo был Replaced - заменен в рабочей копии; это значит, что foo был удален, а новый элемент с таким же именем был добавлен. Несмотря на то, что они могут иметь одинаковое имя, хранилище рассматривает их как разные объекты с отдельной историей.
G foo
Файл foo получил новые изменения из хранилища, однако ваша локальная копия в то же время содержит ваши собственные изменения. Изменения, полученные из хранилища, либо не пересекаются, либо они точно такие же как ваши локальные изменения, поэтому Subversion успешно выполнила merGed - слияние изменений хранилища с файлом.
C foo
Файл foo получил от сервера Conflicting - конфликтующие изменения. Изменения с сервера пересекаются с вашими изменениями файла. Такое перекрытие просто нуждается в разрешении человеком (вами).
Внесение изменений в рабочую копию
Как правило, целесообразнее всего работать отдельно по каждому частному изменению (или набору изменений), такому как реализация новой функциональной возможности, исправление ошибки и т. п. Здесь вы будете пользоваться такими командами Subversion как svn add, svn delete, svn copy и svn move. Однако, если вы просто редактируете файлы, которые уже находятся под контролем Subversion, ни одна из этих команд вам не нужна. В своей рабочей копии вы можете делать следующие изменения:
Изменения файлов
Это самый простой вид изменений. Вам не нужно сообщать Subversion о своем намерении изменить файл; просто берите и вносите изменения. Subversion сможет автоматически определить измененные файлы.
Изменения в структуре
Вы можете попросить Subversion «отметить» файлы и каталоги для удаления, добавления, копирования или перемещения. Хотя эти изменения сразу же отразятся в рабочей копии, в хранилище не произойдет ни добавления, ни удаления до тех пор, пока вы не выполните фиксацию изменений.
Для внесения изменений в файлы используйте свой текстовый редактор, текстовый процессор, графическую программу или любой другой инструмент, который вы обычно используете. Subversion обрабатывает бинарные файлы так же легко как и текстовые - и настолько же эффективно.
Рассмотрим четыре подкоманды Subversion, которые вы чаще всего будете использовать при внесении изменений в структуру.
svn add foo
Запланировать файл, каталог или символьную ссылку foo для добавления в хранилище.
При следующей фиксации foo станет компонентом своего родительского каталога. Обратите внимание на то, что если foo является каталогом, то все содержащееся в foo будет запланировано для добавления. Чтобы добавить отдельно только сам каталог foo, воспользуйтесь параметром --non-recursive (-N).
svn delete foo
Запланировать удаление из хранилища файла, каталога или символьной ссылки foo.
Если foo является файлом или ссылкой, он сразу же удаляется из вашей рабочей копии. Если foo является каталогом, он не удаляется, но Subversion запланирует его удаление. foo будет удален из рабочей копии и хранилища при фиксации изменений.
svn copy foo bar
Создать новый элемент bar как копию foo.
bar будет автоматически запланирован для добавления. Когда при следующей фиксации bar будет добавлен в хранилище, в его истории будет отмечено копирование (то, что первоисточником является foo).
svn move foo bar
Эта команда полностью аналогична выполнению svn copy foo bar; svn delete foo.
Поэтому, bar будет запланирован для добавления как копия foo, а foo будет запланирован для удаления.
Предупреждение. Хотя вы можете редактировать файлы любыми программными средствами, не следует менять структуру рабочей копии, не проинформировав о своих действиях Subversion. Для изменения структуры рабочей копии используйте команды svn copy, svn delete и svn move, а для добавления новых файлов и каталогов под контроль версий используйте svn add.
Анализ изменений
После внесения изменений вы должны зафиксировать их в хранилище, но перед этим было бы неплохо посмотреть, что же, собственно, вы изменили. Проанализировав перед фиксацией свои изменения, вы сможете составить более аккуратное лог-сообщение. Кроме того, вы можете обнаружить, что изменили файл непреднамеренно, что позволит еще до фиксации вернуть файл к предыдущему состоянию. К тому же, это хорошая возможность пересмотреть и проверить изменения перед их публикацией. Чтобы увидеть все сделанные изменения, вы можете воспользоваться svn status, svn diff и svn revert. Первые две команды вы можете использовать для того, чтобы найти измененные файлы рабочей копии, а затем, при помощи третьей, отменить некоторые (или все) изменения.
Все эти три команды (svn status, svn diff и svn revert) могут использоваться при полном отсутствии сетевого доступа. Для этого Subversion использует первоначальную версию каждого версионированного файла, кэшированную в административной области .svn. Этот кэш (называемый «текстовой базой»), кроме всего прочего, позволяет Subversion при фиксации отправлять локальные пользовательские изменения в виде сжатых «различий» первоначальной версии.
Subversion была оптимизирована для решения такой задачи и способна выполнять множество действий без обращения к хранилищу. В частности, рабочая копия содержит в .svn-области скрытую кэшированую «нетронутую» копию каждого версионированного файла. За счет этого Subversion может быстро показать, как изменились ваши рабочие файлы или даже предоставить, не связываясь с хранилищем, возможность откатить изменения.
svn status
Наверное, команду svn status вы будете использовать чаще, чем любую другую команду Subversion.
При запуске svn status без параметров из корневого каталога рабочей копии будут найдены все сделанные вами изменения файлов и структуры. Ниже приведены примеры различных буквенных кодов, возвращаемых svn status. (Обратите внимание, что текст, следующий за #, на самом деле svn status не печатает.)
L some_dir # svn оставила блокировку в .svn-области для some_dir
M bar.c # содержимое bar.c имеет локальные изменения
M baz.c # в baz.c есть изменения в свойствах, а в содержимом нет
X 3rd_party # каталог является частью внешней зависимости
? foo.o # svn не управляет foo.o
! some_dir # svn управляет этим элементом, но он отсутствует или поврежден
~ qux # элемент версионировался как файл/каталог/ссылка, но тип был изменен
I .screenrc # svn не управляет этим элементом и настроена на его игнорирование
A + moved_dir # добавлен с историей своего происхождения
M + moved_dir/README # добавлен с историей и имеет локальные изменения
D stuff/fish.c # файл запланирован для удаления
A stuff/loot/bloo.h # файл запланирован для добавления
C stuff/loot/lump.c # файл имеет текстовый конфликт с момента обновления
C stuff/loot/glub.c # файл имеет конфликт в свойствах с момента обновления
R xyz.c # файл запланирован для замены
S stuff/squawk # файл или каталог были переключены на ветку
K dog.jpg # файл заблокирован локально; присутствует маркер блокирования
O cat.jpg # файл заблокирован в хранилище другим пользователем
B bird.jpg # файл заблокирован локально, но блокировка была нарушена
T fish.jpg # файл заблокирован локально, но блокировка была снята
svn status печатает пять колонок букв, затем несколько пробелов, затем имя файла или каталога. Первая колонка показывает статус файла или каталога и/или ее содержимого. При этом используются следующие коды:
A item
Файл, каталог или символьная ссылка item был запланирован для добавления в хранилище.
C item
Файл item находится в состоянии конфликта. Это означает, что изменения, полученные от сервера, при обновлении пересекаются с локальными изменениями, имеющимися в рабочей копии. Перед фиксацией изменений вам необходимо разрешить этот конфликт.
D item
Файл, каталог или символьная ссылка item запланирован для удаления из хранилища.
M item
Содержимое файла item было изменено.
R item
Файл, каталог или символьная ссылка запланирован для замены item в хранилище. Это значит, что сначала объект был удален, а затем другой объект с таким же именем был добавлен, все в одной правке.
X item
Каталог item не версионирован, но относится к внешним зависимостям Subversion.
? item
Файл, каталог или символьная ссылка не находится под контролем версий. Вы можете убрать знаки вопроса либо воспользовавшись параметром --quiet (-q) команды svn status, либо установив свойство svn:ignore родительского каталога.
! item
Файл, каталог или символьная ссылка item находится под контролем версий, но отсутствует в рабочей копии или поврежден. Элемент может отсутствовать, если он был удален без использования команд Subversion. В частном случае, каталог может оказаться поврежденным, если вы прервали создание рабочей копии или обновление. Быстрый запуск svn update заново вытащит файл или каталог из хранилища, либо svn revert file восстановит отсутствующий файл.
~ item
Файл, каталог или символьная ссылка item в хранилище является объектом одного типа, а то, что на самом деле находится в рабочей копии, является чем-то другим. Например, в хранилище Subversion может иметь файл, а вы удалили файл и создали на его месте каталог, не используя для этого команды svn delete или svn add.
I item
Файл, каталог или символьная ссылка item находится под контролем версий, и Subversion настроена на его игнорирование при операциях svn add, svn import и svn status. Этот символ появляется при использовании опции --no-ignore для svn status - иначе файл игнорируется и не показывается вообще!
Вторая колонка показывает статус свойств файлов и каталогов. Если во второй колонке показывается M, свойства были изменены. Если в этой колонке показывается C, то это означает, что свойства файла находятся в состоянии конфликта, который должен быть разрешен до фиксации изменений в хранилище. Во всех других случаях будет выведен пробел.
Третья колонка может содержать только пробел или L, это значит, что у каталога заблокирована рабочая область .svn. Вы увидите L, если запустите svn status в каталоге, в котором выполняется svn commit - например, когда вы редактируете лог-сообщение.
Четвертая колонка может содержать только пробел или +, это означает, что элемент был запланирован для «добавления с историей». Это может быть файл или корень скопированного каталога. + означает, что элемент является частью поддерева, запланированного для «добавления с историей», т. е. один из родительских каталогов был скопирован, и этот элемент просто его часть. M + означает, что элемент является частью поддерева, запланированного для «добавления с историей», и имеет локальные изменения. При выполнении фиксации вначале будет «добавлен с историей» родительский каталог, что означает автоматическое наличие файла в копии. После этого в копию будут загружены локальные изменения.
Пятая колонка может содержать только пробел или S. Это означает, что файл или каталог был переключен с пути остальной рабочей копии на ветку (используя svn switch).
Шестая колонка показывает информацию о блокировках.
Если вы укажете конкретный путь для svn status, то получите информацию только об этом элементе:
$ svn status stuff/fish.c
D stuff/fish.c
Кроме того, svn status имеет параметр --verbose (-v), который покажет вам статус каждого элемента в рабочей копии, даже если он не менялся:
$ svn status --verbose
M 44 23 sally README
44 30 sally INSTALL
M 44 20 harry bar.c
44 18 ira stuff
44 35 harry stuff/trout.c
D 44 19 ira stuff/fish.c
44 21 sally stuff/things
A 0 ? ? stuff/things/bloo.h
44 36 harry stuff/things/gloo.c
Это «длинная форма» представления вывода svn status. Первая колонка осталась та же самая, а вот вторая колонка показывает рабочую правку элемента. Третья и четвертая колонки показывают правку, в которой элемент последний раз изменялся и автора этих изменений.
Ни один из указанных выше вызовов svn status не обращается к хранилищу, они работают только локально, сравнивая метаданные каталога .svn с рабочей копией. Отметим, что есть параметр --show-updates (-u), указывающий на соединение с хранилищем и добавляющий информацию об устаревании элементов:
$ svn status --show-updates --verbose
M * 44 23 sally README
M 44 20 harry bar.c
* 44 35 harry stuff/trout.c
D 44 19 ira stuff/fish.c
A 0 ? ? stuff/things/bloo.h
Status against revision: 46
Обратите внимание на две звездочки: если сейчас вы запустите svn update вы получите изменения для README и trout.c. Это очень полезная информация - перед фиксацией вам необходимо обновить и получить изменения с сервера для README, или же хранилище отклонит вашу фиксацию как не соответствующую актуальному состоянию.
svn diff
Еще один механизм для анализа изменений - это команда svn diff. Запустив svn diff без аргументов, можно увидеть, какие именно изменения вы внесли, в результате будут выведены изменения файлов в едином формате представления различий:
$ svn diff
Index: bar.c
===================================================================
--- bar.c (revision 3)
+++ bar.c (working copy)
@@ -1,7 +1,12 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <stdio.h>
int main(void) {
- printf("Sixty-four slices of American Cheese...\n");
+ printf("Sixty-five slices of American Cheese...\n");
return 0;
}
Index: README
===================================================================
--- README (revision 3)
+++ README (working copy)
@@ -193,3 +193,4 @@
+Note to self: pick up laundry.
Index: stuff/fish.c
===================================================================
--- stuff/fish.c (revision 1)
+++ stuff/fish.c (working copy)
-Welcome to the file known as 'fish'.
-Information on fish will be here soon.
Index: stuff/things/bloo.h
===================================================================
--- stuff/things/bloo.h (revision 8)
+++ stuff/things/bloo.h (working copy)
+Here is a new file to describe
+things about bloo.
Команда svn diff формирует свой вывод, сравнивая ваши рабочие файлы с кэшированными «нетронутыми» копиями из .svn. Весь текст запланированных для добавления файлов показывается как добавленный, а весь текст запланированных для удаления файлов показывается как удаленный.
Вывод происходит в едином формате представления различий. При этом удаленные строки предваряются знаком «-», а добавленные - знаком «+». Кроме этого svn diff печатает имена файлов и информацию о сдвиге информации, которая необходима программе patch, и, следовательно, вы можете получать «патчи», перенаправив вывод различий в файл:
$ svn diff > patchfile
Вы можете, например, отправить по электронной почте файл патча другому разработчику для ознакомления или тестирования перед фиксацией.
svn revert
Теперь предположим, что, просмотрев вывод команды diff, вы обнаружили, что изменения в README ошибочны - к примеру, потому, что в своем редакторе вы случайно набрали текст, предназначавшийся для другого файла.
В такой ситуации как нельзя кстати окажется команда svn revert.
$ svn revert README
Reverted 'README'
Subversion возвращает файл в состояние, предшествующее модификации, путем замены файла его кэшированной «первоначальной» копией из .svn-области. Кроме того, обратите внимание, что svn revert может отменить любые запланированные операции - например, вы можете прийти к решению всё-таки не добавлять новый файл:
$ svn status foo
? foo
$ svn add foo
A foo
$ svn revert foo
Reverted 'foo'
$ svn status foo
? foo
Или, допустим, вы ошибочно удалили файл из-под контроля версий:
$ svn status README
README
$ svn delete README
D README
$ svn revert README
Reverted 'README'
$ svn status README
README
Разрешение конфликтов (при слиянии с чужими изменениями)
Мы уже видели, как svn status -u может предупредить о конфликтах. Предположим, вы запустили svn update и увидели кое-что интересное:
$ svn update
U INSTALL
G README
C bar.c
Updated to revision 46.
Коды U и G интереса не представляют; эти файлы без проблем поглотили изменения из хранилища. Файлы, отмеченные U, локальных изменений не содержат и были обновлены изменениями из хранилища. Отмеченные G были слиты, это значит, что файл имел локальные изменения, но изменения, пришедшие из хранилища, не перекрываются с локальными изменениями.
А вот файлы, отмеченные C, имеют конфликт. Это значит, что изменения с сервера пересеклись с вашими личными, и теперь вам нужно вручную сделать между ними выбор.
Всякий раз, когда возникает конфликт, в его обнаружении и разрешении вам, как правило, помогают три вещи:
Subversion печатает C во время обновления и запоминает, что файл в состоянии конфликта.
Если Subversion считает, что тип файла допускает слияние изменений, она включает в него маркеры конфликта - специальные текстовые строки, отделяющие «стороны» конфликта - чтобы визуально показать пересекающиеся области. (Subversion использует свойство svn:mime-type для определения возможности контекстного, построчного слияния.)
Для каждого конфликтного файла Subversion добавляет в рабочую копию до трех не версионированных дополнительных файлов:
1. filename.mine - это ваш файл в том виде, в каком он присутствовал в рабочей копии до обновления - без маркеров конфликта. Этот файл содержит в себе только ваши изменения и ничего больше. (Если Subversion решает, что файл не пригоден для слияния изменений, то файл .mine не создается, так как он будет идентичным рабочему файлу.)
2. filename.rOLDREV - это файл правки BASE, где BASE - правка, которая была до обновления рабочей копии. Иными словами, это файл, который был у вас до внесения изменений.
3. filename.rNEWREV - это файл, который ваш Subversion-клиент получил с сервера при обновлении рабочей копии. Этот файл соответствует правке HEAD хранилища.
Здесь OLDREV — это номер правки файла в каталоге .svn, а NEWREV — номер правки HEAD хранилища.
Например, Салли внесла изменения в файл sandwich.txt из хранилища. Одновременно Гарри изменил файл в своей рабочей копии и зафиксировал его. Салли обновляет свою рабочую копию перед фиксацией и получает конфликт:
$ svn update
C sandwich.txt
Updated to revision 2.
$ ls -1
sandwich.txt
sandwich.txt.mine
sandwich.txt.r1
sandwich.txt.r2
Теперь Subversion не позволит зафиксировать файл sandwich.txt, пока не будут удалены три временных файла.
$ svn commit --message "Add a few more things"
svn: Commit failed (details follow):
svn: Aborting commit: '/home/sally/svn-work/sandwich.txt' remains in conflict
Для разрешения конфликта у вас есть три варианта:
Объединить конфликтующий текст «вручную» (путем анализа и редактирования маркеров конфликта в файле).
Скопировать один из временных файлов поверх своего рабочего файла.
Выполнить svn revert <filename> для отказа от всех ваших локальных изменений.
После разрешения конфликта вам нужно известить об этом Subversion, выполнив svn resolved. Эта команда удалит три временных файла, и Subversion больше не будет считать, что файл находится в состоянии конфликта.
$ svn resolved sandwich.txt
Resolved conflicted state of 'sandwich.txt'
Слияние конфликтов вручную
Возьмем пример. По недоразумению вы и ваш соразработчик Салли одновременно редактируете файл sandwich.txt. Салли зафиксировала свои изменения, и поэтому при обновлении своей рабочей копии вы получите конфликт, для разрешения которого вам необходимо отредактировать sandwich.txt. Для начала посмотрим на файл:
$ cat sandwich.txt
Top piece of bread
Mayonnaise
Lettuce
Tomato
Provolone
<<<<<<< .mine
Salami
Mortadella
Prosciutto
=======
Sauerkraut
Grilled Chicken
>>>>>>> .r2
Creole Mustard
Bottom piece of bread
Строки, начинающиеся со знаком «меньше чем», «равно» и «больше чем», являются маркерами конфликта. Перед следующей фиксацией вам нужно будет убедиться, что они удалены из файла. Текст между первыми двумя маркерами состоит из ваших изменений в конфликтующей области:
<<<<<<< .mine
Salami
Mortadella
Prosciutto
=======
Текст между вторым и третьим маркером конфликта - это текст из фиксации Салли:
=======
Sauerkraut
Grilled Chicken
>>>>>>> .r2
Скорее всего вы не захотите просто удалить маркеры конфликта и изменения, сделанные Салли, - она ужасно удивится, когда дойдет до сандвича и не увидит того, что ожидала. Это как раз тот случай, когда вы снимаете трубку или пересекаете офис и объясняете Салли, что не можете получить из итальянского гастронома квашеную капусту. После того, как вы согласуете изменения, нужно будет выполнить фиксацию. Для этого отредактируйте ваш файл и удалите маркеры конфликта.
Top piece of bread
Mayonnaise
Lettuce
Tomato
Provolone
Salami
Mortadella
Prosciutto
Creole Mustard
Bottom piece of bread
Теперь выполните svn resolved, и вы готовы к фиксации изменений:
$ svn resolved sandwich.txt
$ svn commit -m "Go ahead and use my sandwich, discarding Sally's edits."
Обратите внимание на то, что svn resolved, в отличие от большинства команд, с которыми мы имели дело в этой главе, требует аргумент. В любом случае будьте осторожны и выполняйте svn resolved тогда, когда вы убеждены, что исправили конфликт в файле. После того как временные файлы будут удалены, Subversion позволит вам зафиксировать файл, даже если он все еще содержит маркеры конфликта.
Если вы испытываете затруднения при редактировании конфликтующего файла, всегда можно обратиться к тем трем файлам, которые Subversion создает в рабочей копии - включая ваш файл в том виде, в каком он был до обновления. Для анализа этих трех файлов вы даже можете воспользоваться программами для слияния изменений от сторонних разработчиков.
Копирование файла поверх вашего рабочего файла
Если вы получили конфликт и решили отказаться от своих изменений, вы можете просто скопировать один из временных файлов, созданных Subversion, поверх файла в рабочей копии:
$ svn update
C sandwich.txt
Updated to revision 2.
$ ls sandwich.*
sandwich.txt sandwich.txt.mine sandwich.txt.r2 sandwich.txt.r1
$ cp sandwich.txt.r2 sandwich.txt
$ svn resolved sandwich.txt
Использование svn revert
Если вы получили конфликт и, проанализировав, решили отбросить изменения и начать сначала, просто отмените ваши изменения:
$ svn revert sandwich.txt
Reverted 'sandwich.txt'
$ ls sandwich.*
sandwich.txt
Обратите внимание, что при возврате конфликтующего файла к исходному состоянию вам не нужно выполнять svn resolved.
Фиксация изменений
Если вы закончили с редактированием и слили все изменения с сервера, то можно зафиксировать их в хранилище.
Команда svn commit отправляет все ваши изменения в хранилище. При фиксации изменений необходимо описать ваши изменения в тексте лог-сообщения. Лог-сообщение будет присоединено к созданной правке. Если ваше лог-сообщение короткое, вы можете указать его в командной строке, используя опцию --message (или -m):
$ svn commit --message "Corrected number of cheese slices."
Sending sandwich.txt
Transmitting file data .
Committed revision 3.
Однако, если вы заранее составляли лог-сообщение в процессе работы, можно попросить Subversion взять его из файла, передав имя этого файла в параметре --file:
$ svn commit --file logmsg
Sending sandwich.txt
Transmitting file data .
Committed revision 4.
Если вы не укажете ни опции --message, ни опции --file, для составления лог-сообщения Subversion автоматически запустит редактор, в котором нужно набрать сообщение. Если, набирая сообщение в редакторе, вы решите отменить фиксацию, то можете просто выйти из редактора без сохранения изменений. Если вы уже сохранили сообщение, просто удалите текст и выполните сохранение еще раз.
$ svn commit
Waiting for Emacs...Done
Log message unchanged or not specified
a)bort, c)ontinue, e)dit
a
$
Хранилище, в общем-то, не знает ничего о смысле ваших изменений; оно только контролирует, чтобы никто не изменил те же файлы, что и вы. Если это все-таки случилось, вся фиксация будет отклонена, и вы получите сообщение о том, что один или несколько файлов устарели:
$ svn commit --message "Add another rule"
Sending rules.txt
svn: Commit failed (details follow):
svn: Out of date: 'rules.txt' in transaction 'g'
В таком случае вам нужно выполнить svn update, разобраться со всеми слияниями и конфликтами и попытаться выполнить фиксацию снова.
Лекция 13 тема: Open тooLs api.
Литература: 1. Гопанюк Олег "Эксперты в Delphi, или Программист, упростите себе жизнь. - http://articles.org.ru/cn/showdetail.php?cid=8639.
2. Тейксейра Стив, Пачеко Ксавье. Borland Delphi 6. Руководство разработчика.
3. Семак Андрей. Borland Delphi/C++Builder ToolsAPI, или взгляд на Borland IDE изнутри – http://www.delphiplus.org/articles/expert/toolsapi/index.html.
Понятие эксперта
Определение 1. Open Tools API (OTA) - это набор интерфейсов (функций), предназначенных для управления средой программирования, расширения функциональных возможностей среды разработчика.
Open Tools API позволяют разработчикам добавлять новые функции в Delphi и C++Builder. Эти дополнения называются мастерами или экспертами. Мастера могут использовать Open Tools API для изменения ИСР, получения информацию о статусе ИСР, получения уведомления о важных событиях. Этот набор позволяет нам создавать свои собственные меню, кнопки управления и так далее.
Примечание. В Delphi создание мастера доступно в редакциях Professional, Enterprise и Architect, т. к. эти версии включают определения интерфейсов Open Tools. Исходный код интерфейсов Open Tools размещен в каталоге \Delphi\Source\ToolsAPI в ToolsAPI.pas. В версии Standard возможно применение дополнений, созданных с помощью интерфейса API Open Tools, но их невозможно создавать, поскольку в этой версии содержатся лишь модули для разработки компонентов и редакторов свойств.
Если не хватает возможностей среды или какие-то операции кажутся слишком громоздкими, то эксперты помогут решить эти проблемы. С помощью экспертов вы словно проникаете внутрь среды Delphi и без труда дополняете ее. Естественно, такое проникновение должно быть осторожным и аккуратным, потому как неправильное обращение с объектами и интерфейсами может вызвать сбои в работе среды или даже ее разрушение.
Особенности ToolsAPI в Delphi/C++ Builder различных версий
Внутренних отличий в ToolAPI IDE Delphi и C++Builder практически нет. То есть, ToolsAPI Delphi5 тождественен ТoolsAPI С++Builder5 и так далее. Расширения, собранные на Delphi без проблем работают в C++Builder аналогичной версии. Создав полезную функцию для IDE Delphi, вы всегда сможете использовать её в С++Builder той же версии.
При создании расширений, которые будут использоваться в нескольких версиях IDE, нужно использовать ToolsAPI от самой низкой версии используемого IDE. Это связано с тем, что развитие интерфейсов порождает правило о совместимости сверху вниз (от высшей версии к низшей).
Стили экспертов Delphi
Всего существует четыре стиля экспертов Delphi (см. таблицу 1). Главное отличие между стилями заключается в способе вызова эксперта пользователем.
Таблица 1. Стили экспертов в Delphi.
Стиль |
Способ вызова |
Стандартный |
Добавить в подменю Help пункт меню |
Надстройки |
Добавить в определенное экспертом подменю пункт меню |
Формы |
Добавить во вкладку Forms диалогового окна New Items пиктограмму эксперта |
Проекта |
Добавить во вкладку Projects диалогового окна New Items пиктограмму эксперта |
В экспертах Open Tools API может использоваться для:
получения информации о проекте;
получения информации о модуле или форме;
управления модулями проекта (дл открытия, закрытия, добавления, создания);
управления ресурсами проекта;
модификации меню Delphi;
регистрации изменений в проекте;
регистрации изменений в модуле.
Примечание. Следует заметить, что интерфейс Open Tools API доступен только из программ, запущенных как часть интегрированной среды Delphi.
"Старый стиль" и "новый стиль" ToolsApi
Существует две модели интерфейсов ToolsAPI, или как их называют – "старый" и "новый" стили. Старый стиль был реализован в средах Delphi 3-4 и C++ Builder 3.-4. Начиная с Delphi 5 и C++ Builder 5, в среду встроена реализация нового стиля. Мы будем рассматривать только "новый стиль", т.к. фирма Borland более не развивает интерфейсы "старого" стиля. Это не значит, что расширения, написанные в старом стиле, не будут работать. Разработчики Borland оставили API старого стиля для совместимости с предыдущими версиями IDE. Интерфейсы "старого" ToolsAPI находятся в директории Source\Toolsapi\ в следующих файлах:
editintf.pas
exptintf.pas
fileintf.pas
istreams.pas
toolintf.pas
vcsintf.pas
Интерфейсы нового стиля имеют префикс "IOTA" и "INTA", а "старого" содержат префикс "TI".
Модули интерфейса api Open Tools
Интерфейс API Open Tools состоит из четырнадцати модулей, каждый из которых содержит один или несколько объектов, обеспечивающих взаимодействие со многими средствами интегрированной среды разработки. С помощью этих интерфейсов можно создавать собственные мастера Delphi, диспетчеры управления версиями, а также компоненты и редакторы свойств.
За исключением интерфейсов, разработанных для компонентов и редакторов свойств, объекты интерфейса Open Tools обеспечивают полностью виртуальный интерфейс с внешним миром. Это означает, что можно использовать лишь виртуальные функции таких объектов. Ни к полям данных подобного объекта, ни к его свойствам или статическим функциям нельзя получить доступ, поскольку объекты интерфейса Open Tools создаются на основе стандарта COM. После небольшой доработки эти интерфейсы могут использоваться с любым языком программирования, поддерживающим технологию COM.
Примечание. Термин интерфейс в данном случае не означает встроенный в Delphi тип interface. Поскольку API Open Tools обеспечивает поддержку интерфейсов Delphi, в качестве замены “настоящих” интерфейсов он использует обычные классы Delphi с виртуальными абстрактными методами. Использование стандартных интерфейсов в API Open Tools Delphi в каждой новой версии увеличивалось, и текущая версия основывается практически только на них.
В таблице 2 приведены модули, которые, в сущности, и составляют интерфейс API Open Tools. Модули таблицы 3 сохранены лишь для обеспечения совместимости с предыдущими версиями Delphi.
Таблица 2. Модули интерфейса API Open Tools.
Имя модуля |
Назначение |
ToolsAPI |
Содержит новейшие элементы интерфейса API Open Tools. Этот модуль, по существу, заместил абстрактные классы, применяемые в предыдущих версиях Delphi для управления дополнениями меню, системы уведомления, файловой системы, редактора и мастеров. В нем содержатся также новые интерфейсы для управления отладчиком, комбинациями клавиш интегрированной среды разработки, проектами, группами проектов, пакетами и списком To Do. |
VCSIntf |
Определяет класс TIVCSClient, обеспечивающий взаимодействие Delphi с программным обеспечением управления версиями. |
DesignConst |
Содержит строки, используемые API Open Tools. |
DesignEditors |
Обеспечивает поддержку редактора свойств. |
DesignIntf |
Заменяет модуль DsgnIntf предыдущих версий и обеспечивает базовую поддержку интерфейсов IDE времени разработки. IDE использует интерфейс IProperty для редактирования свойств. Интерфейс IDesignerSelections применяется для манипулирования объектами, выбранными в списке конструктора форм (заменяет TDesignerSelectionList, использовавшийся в предыдущих версиях Delphi). IDesigner - это один из первичных интерфейсов, который применяется мастерами для общих служб IDE. IDesignNotification поддерживает такие уведомления событий конструктора, как вставка, удаление и модификация элементов. Интерфейс IComponentEditor реализуется редакторами компонентов для обеспечения редактирования компонента во время разработки, а ISelectionEditor обеспечивает те же возможности для группы выбранных компонентов. Класс TbaseComponentEditor является базовым классом для всех редакторов компонентов. Интерфейс ICustomModule и класс TBaseCustomModule обеспечивают возможность установки модулей, которые могут быть отредактированы в конструкторе форм IDE. |
DesignMenus |
Содержит интерфейсы IMenuItems, IMenuItem, а также другие подобные интерфейсы, предназначенные для манипулирования меню IDE во время разработки. |
DesignWindows |
Содержит объявление класса TDesignWindow, являющегося базовым классом для всех новых окон проекта, которые добавляются в IDE. |
PropertyCategories |
Содержит классы, обеспечивающие категоризацию свойств специальных компонентов. Используется инспектором объектов для представления свойств по категориям. |
TreeIntf |
Поддерживает класс TSprig и связанные с ним классы, а также интерфейсы, обеспечивающие узлы и ветвления в объекте TreeView IDE. |
VCLSprigs |
Содержит реализацию ветвления компонентов VCL. |
VCLEditors |
Содержит объявления базовых интерфейсов ICustomPropertyDrawing и ICustomPropertyListDrawing, обеспечивающих специальное представление свойств и списков свойств в инспекторе объектов IDE. Содержит также объявления специальных объектов для представления свойств VCL. |
ClxDesignWindows |
Содержит объявление класса TClxDesignWindow, являющегося эквивалентом CLX-класса TDesignWindow. |
ClxEditors |
Эквивалент CLX-модуля VCLEditors, содержащего редакторы свойств для компонентов CLX. |
ClxSprigs |
Реализация ветвления для компонентов CLX. |
Таблица 3. Устаревшие модули API Open Tools.
Имя модуля |
Назначение |
FileIntf |
Определяет класс TIVirtualFileSystem, используемый IDE Delphi для работы с файлами. Мастера, диспетчеры управления версиями, а также редакторы свойств и компоненты могут использовать этот интерфейс для выполнения в Delphi специальных файловых операций. |
EditIntf |
Определяет классы, необходимые для управления редактором кода и конструктором форм. Класс TIEditReader предоставляет доступ к буферу редактора “для чтения”, а класс TIEditWriter - предоставляет доступ к буферу редактора “для записи”. Класс TIEditView предоставляет возможность просмотра буфера редактирования. Класс TIEditInterface - это базовый интерфейс редактора, который может использоваться для доступа к уже упоминавшимся интерфейсам. Класс TIComponentInterface - это интерфейс с отдельным компонентом, помещенным в форму или модуль в режиме разработки. Класс TIFormInterface - основной интерфейс с формой или модулем данных в режиме разработки, позволяет создавать компоненты или искать их в отдельной форме или модуле. Класс TIResourceEntry обеспечивает взаимодействие с данными в файле ресурсов (*.res) проекта. Класс TIResourceFile определяет высокоуровневый интерфейс с файлом ресурсов проекта, позволяет создавать, удалять или отыскивать ресурсы в файле ресурсов проекта. Класс TIModuleNotifier определяет сообщения, используемые при появлении в конкретном модуле различных событий. Класс TIModuleInterface обеспечивает взаимодействие с любым файлом или модулем, открытым в интегрированной среде разработки. |
ExptIntf |
Определяет абстрактный класс TIExpert, от которого происходят все мастера. |
VirtIntf |
Определяет базовый класс TInterface, от которого происходят другие интерфейсы. В этом модуле определен также класс TIStream, который инкапсулирует класс VCL TStream. |
IStreams |
Определяет классы TIMemoryStream, TIFileStream и TIVirtualStream, которые являются потомками класса TIStream. Эти интерфейсы могут использоваться для включения в IDE своего собственного механизма работы с потоками. |
ToolIntf |
Определяет классы TIMenuItemIntf и TIMainMenuIntf, которые позволяют разработчику создавать и модифицировать меню IDE Delphi, управлять атрибутами пунктов меню и отслеживать событие OnClick пункта меню. В данном модуле определен также класс TIAddInNotifier, кото рый позволяет создаваемым инструментам дополнениям получать уведомления о возникновении в среде разработки определенных событий. В этом модуле имеется класс TIToolServices, который обеспечивает взаимодействие между различными элементами IDE Delphi (такими как редактор, библиотека компонентов, редактор кода, конструктор форм и файловая система). |
Основные сервисы ToolsAPI
Файл ToolsAPI.pas содержит целый ряд сервисов, которые помогут получать информацию из IDE. Самый "главный" интерфейс – IBorlandIDEServices. Это из него мы получим все необходимые нам интерфейсы-сервисы. Все интерфейсы, содержащие в себе слово "Services" являются производными от IBorlandIDEServices. Указатель на IBorlandIDEServices можно получить двумя способами.
Через экспортную функцию регистрации эксперта путем присвоения указателя глобальной переменной BorlandIDEServices. (в случае с библиотекой).
Через уже определенную глобальную переменную BorlandIDEServices (используя пакеты).
Например, нам необходимо получить указатель на INTAServices. Для этого достаточно написать следующую функцию:
function NTAServices: INTAServices;
begin
Result := (BorlandIDEServices as INTAServices);
end;
Аналогичные функции можно создать для всех остальных сервисов.
Типы экспертов
Экспертом мы называем набор функций, расширяющий возможности IDE и выполненный в виде загружаемого модуля. В "новом" стиле нет понятия "Expert" (пережиток "старого стиля"), но есть понятие "Wizard". На самом деле это одно и тоже. Существуют два типа исполнения экспертов:
В виде пакетов (расширение файла .BPL)
В виде библиотек (расширение файла .DLL)
У каждого из этих двух типов есть преимущества и недостатки.
Пакеты
Эксперты, выполненные в виде пакетов (BPL), регистрируются как обычные пакеты с компонентами прямо из среды разработки. Для этого достаточно зайти в меню Component \ Install Packages, нажать кнопку Add и выбрать BPL с экспертом.
Рис. 1
Чтобы временно отключить эксперта, достаточно снять галочку напротив пакета в списке этого окна. Для того, чтобы удалить его из системы, следует выбрать пакет в списке и нажать кнопку Remove.
Библиотеки
У этого типа экспертов более сложная регистрация. Для регистрации нам необходимо запустить редактор реестра вашей операционной системы (regedit.exe) и открыть ключ
HKEY_CURRENT_USER\Software\Borland\Delphi(или C++Builder)\X.X\Experts,
где X.X версия Delphi\C++Builder.
Пример показан на рис. 2.
Рис. 2
Имя значения может иметь произвольное название, значение должно иметь тип REG_SZ (String) и содержать путь к библиотеке с экспертом. Добавляя или удаляя значения в этой ветке, мы подключаем или отключаем расширения.
Примечание. Для удобства можно использовать ExpertManager из набора Gexperts (http://www.gexperts.org).
Пример создания эксперта
Рассмотрим создание эксперта в виде пакета.
Создаем новый пакет и добавляем в него модуль (Unit), в котором будет храниться класс эксперта.
Базовым для класса эксперта является интерфейс IOTAWizard. Все его методы нуждаются в реализации, даже если не будут использоваться.
Методы IOTAWizard:
function GetIDString: string; – этот метод должен возвращать уникальную строку для идентификации эксперта внутри IDE. Например: "MY.FIRST.EXPERT". Если IDE обнаружит два расширения с одинаковыми идентификаторами, то среда разработки выдаст сообщение об ошибке, и загрузится только первый эксперт. Должен переопределяться во всех экспертах.
function GetName: string; – должен вернуть имя эксперта. Например: "MyFirstExpert". При использовании эксперта формы или проекта это имя будет подписью под соответствующей пиктограммой в диалоговом окне New Items. Должен переопределяться во всех экспертах.
function GetState: TWizardState; – определяет, доступен ли в данный момент эксперт, т. е. может ли пользователь выбрать соответствующий пункт меню. Имеет два состояния: wsEnabled (доступен), wsChecked (отмечен). Пример: Result:=[esEnabled];
procedure Execute; – метод выполнится, когда эксперт запустится.
Все объекты, которые будут взаимодействовать с ToolsAPI, порождаются от TNotifierObject. Его реализацию можно посмотреть в том же ToolsAPI.pas.
Также при создании класса будем использовать интерфейс IOTAMenuWizard, обладающий методом function GetMenuText: string. Данная функция возвращает строку, которая будет внесена в подменю Help ИСР Delphi. Метод вызывается при каждом выводе меню.
Переопределение методов - задача довольно простая; она предполагает написание всего нескольких строк кода. Например, реализация метода GetMenuText:
function TFirstExpert.GetMenuText: string;
begin
Result := 'Execute MyFirstExpert';
end;
После определения всех методов нужно зарегистрировать пакет с помощью процедуры Register, которая объявляется вне класса в interface и реализуется в implementation.
Для регистрации пакетов используется процедура RegisterPackageWizard(TFirstExpert.Create), где TFirstExpert – имя класса эксперта.
В результате код модуля может быть следующим:
unit FirstExpClass;
interface
uses
ToolsApi, Dialogs, SysUtils;
type
TFirstExpert = class(TNotifierObject, IOTAWizard, IOTAMenuWizard)
public
function GetIDString: string;
function GetName: string;
function GetState: TWizardState;
procedure Execute;
function GetMenuText: string;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterPackageWizard(TFirstExpert.Create);
end;
{ TFirstExpert }
procedure TFirstExpert.Execute;
begin
ShowMessage(‘Эксперт запущен’);
end;
function TFirstExpert.GetIDString: string;
begin
Result := 'MY.FIRST.EXPERT';
end;
function TFirstExpert.GetMenuText: string;
begin
Result := 'Execute MyFirstExpert';
end;
function TFirstExpert.GetName: string;
begin
Result := 'MyFirstExpert';
end;
function TFirstExpert.GetState: TWizardState;
begin
Result := [wsEnabled];
end;
end.
Далее остается только скомпилировать
пакет, нажав на
,
и установить его в ИСР. Для установки
достаточно зайти в меню Component \ Install
Packages, нажать Add и выбрать BPL с экспертом.
(Можно сразу нажать
).
Некоторые полезные эксперты
В Internet есть предостаточно мест, где можно найти эксперты для Delphi. Одно из таких мест - польский сервер “Delphi Super Page” (http://delphi.icm.edu.pl/). Там вы найдете множество различных экспертов и полезных компонентов. Например, набор экспертов myexp100, предоставляющий возможность ускорить разработку приложений на Delphi. Его можно загрузить по адресу: http://delphi.icm.edu.pl/ftp/d40free/myexp100.zip. Рассмотрим вкратце эти маленькие “добавки”. Набор содержит эксперт - редактор префиксов для имен компонентов. После того, как он будет установлен в инспекторе объектов, напротив свойства Name появится кнопка с многоточием. Это говорит о том, что можно воспользоваться редактором для изменения свойства Name. С его помощью можно указывать префикс для данного класса компонента. Строго говоря, использование префиксов в названиях компонентов - это правило хорошего тона. В меню Tools теперь появляется новое подменю Prefix list editor, с помощью которого можно изменять и добавлять такие префиксы. Как известно, некоторые компоненты являются контейнерами для других (например, TPanel, TGroupBox, TScrollBox и т. п.). Установленный набор позволит управлять выравниванием дочерних компонентов. Для этого достаточно щелкнуть правой кнопкой мыши и выбрать в контекстном меню пункт Align controls. В Delphi есть мастер создания элементов управления, работающих с данными. Однако в рассматриваемом наборе имеется эксперт, благодаря которому можно создавать компоненты для работы с данными более совершенным способом. С помощью эксперта, вызываемого командой Tools / Shortcut list editor, можно определить свой набор клавиатурных эквивалентов для главного меню Delphi. Кроме всего прочего, после установки набора вы обнаружите, что палитра компонентов Delphi стала многострочной.
Лекция 14 тема: Использование и создание dll.
Литература: 1. Вальвачев А.Н., Сурков К.А., Сурков Д.А., Четырько Ю.М. Программирование на языке Delphi. Глава 5. Динамически загружаемые библиотеки. - http://www.rsdn.ru/ARTICLE/DELPHI/DELPHI_7_05.XML#ECB
2. Кривошеев С.Е. Разработка DLL в среде Borland Delphi. http://www.delphihelp.boom.ru
Если вы создаете не одну программу, а несколько, и в каждой из них пользуетесь общим набором подпрограмм, то код этих подпрограмм включается в каждую вашу программу. В результате достаточно большие общие части кода начинают дублироваться во всех ваших программах, неоправданно «раздувая» их размеры. Поддержка программ затрудняется, ведь если вы исправили ошибку в некоторой подпрограмме, то вам придется перекомпилировать и переслать потребителю целиком все программы, которые ее используют. Решение проблемы состоит в переходе к модульной организации выполняемых файлов, которая реализуется с помощью динамически загружаемых библиотек.
Определение 1. Динамически загружаемая библиотека (от англ. dynamically loadable library) - это библиотека подпрограмм, которая загружается в оперативную память и подключается к использующей программе во время ее работы (а не во время компиляции и сборки).
Файлы динамически загружаемых библиотек в среде Windows обычно имеют расширение .dll (от англ. Dynamic-Link Library). Dll не является запускаемым файлом.
Несколько разных программ могут использовать в работе общую динамически загружаемую библиотеку. При этом операционная система в действительности загружает в оперативную память лишь одну копию библиотеки и обеспечивает совместный доступ к ней со стороны всех программ. Кроме того, такие библиотеки могут динамически загружаться и выгружаться из оперативной памяти по ходу работы программы, освобождая ресурсы системы для других задач.
Одно из важнейших назначений динамически загружаемых библиотек - это взаимодействие подпрограмм, написанных на разных языках программирования. Например, вы можете свободно использовать в среде Delphi динамически загружаемые библиотеки, разработанные в других системах программирования с помощью языков C и C++. Справедливо и обратное утверждение - динамически загружаемые библиотеки, созданные в среде Delphi, можно подключать к программам на других языках программирования.
Обобщив выше изложенное, можно указать следующие области применения библиотек:
Отдельные библиотеки, содержащие полезные для программистов дополнительные функции. Например, функции для работы со строками, или же сложные библиотеки для преобразования изображений.
Хранилища ресурсов. В DLL можно хранить не только программы и функции, но и всевозможные ресурсы - иконки, рисунки, строковые массивы, меню, и т.д.
Библиотеки поддержки. В качестве примера можно привести библиотеки таких известных пакетов, как: DirectX, ICQAPI (API для ICQ), OpenGL и т.д.
Части программы. Например, в DLL можно хранить окна программы (формы), и т.п.
Плагины (Plugins). Плагины - дополнения к программе, расширяющие ее возможности.
Разделяемый ресурс. DLL (Dynamic Link Library) может быть использована сразу несколькими программами или процессами (т.н. sharing - разделяемый ресурс).
Существует два типа динамических библиотек - исполняемые и библиотеки ресурсов. Однако это не означает, что в одном файле не может находиться и код некоторой функции и какие-либо ресурсы. Просто иногда бывает удобно разнести реализацию исполняемых процедур и используемые приложением ресурсы в разные файлы.
Итак, процедуры и функции, содержащиеся в динамической библиотеке, можно разделить на два типа: те, которые могут быть вызваны из других приложений и те, которые используются только внутри самого файла библиотеки. Рассмотрим следующий пример:
Screen.Cursors[myCursor] := LoadCursor(HInstance, MYCURSOR');
LoadCursor - функция Windows API, которая вызывается приложением из динамической библиотеки User 32.dll. Кстати, примером хранимых в динамической библиотеке ресурсов могут являться такие стандартные диалоги Windows, как диалог открытия файла, диалог печати или настройки принтера. Эти диалоги находятся в файле Comctl32.dll. Однако многие прикладные разработчики используют функции вызова форм этих диалогов, совершенно не задумываясь, где хранится их описание.
Разработка библиотеки Структура библиотеки
По структуре исходный текст библиотеки похож на исходный текст программы, за исключением того, что текст библиотеки начинается с ключевого слова library, например:
library SortLib;
uses SysUtils, Classes, Forms, Windows;
procedure HelloWorld(AForm : TForm);
begin
MessageBox(AForm.Handle, 'Hello world!',
'DLL Message Box', MB_OK or MB_ICONEXCLAMATION);
end;
procedure BubleSort(var Arr: array of Integer);
var I, J, T: Integer;
begin
for I := Low(Arr) to High(Arr) - 1 do
for J := I + 1 to High(Arr) do
if Arr[I] > Arr[J] then
begin
T := Arr[I];
Arr[I] := Arr[J];
Arr[J] := T;
end;
end;
exports
BubleSort,
HelloWorld;
begin
end.
Первое, на что следует обратить внимание, это ключевое слово library, находящееся вверху страницы. Library определяет этот модуль как модуль библиотеки DLL. Далее идет название библиотеки. В нашем примере мы имеем дело с динамической библиотекой, содержащей две процедуры: HelloWorld и BubleSort. Причем данные процедуры по структуре ничем не отличается от тех, которые вы помещаете в модули своих приложений. Процедуры и функции - это главное, что должно быть в динамически загружаемой библиотеке, поскольку лишь они могут быть экспортированы.
Если в теле библиотеки объявлены некоторые процедуры, то это еще не значит, что они автоматически станут доступны для вызова извне. Для того чтобы это разрешить, нужно поместить имена процедур в специальную секцию exports.
exports
BubleSort,
HelloWorld;
Перечисленные в секции exports процедуры и функции отделяются запятой, а в конце всей секции ставится точка с запятой. Секций exports может быть несколько, и они могут располагаться в программе произвольным образом.
В конце модуля можно увидеть ключевые слова begin и end. Внутри данного блока вы можете поместить код, который должен выполняться в процессе загрузки библиотеки. Достаточно часто этот блок остается пустым.
Описание и реализация процедур и функций, вызываемых в пределах текущей DLL, ничем не отличаются от их аналогов в обычных проектах-приложениях. Их специфика заключается лишь в том, что вызывающая программа не будет иметь к ним доступа. Она просто не будет ничего знать об их существования, так же, как одни классы ничего не знают о тех методах, которые описаны в секции private других классов.
Экспорт подпрограмм
Каждая экспортируемая подпрограмма в компилированном файле библиотеки представлена уникальным символьным именем. Эти имена собраны в таблицу и используются при поиске подпрограмм - с их помощью выполняется динамическая привязка записанных в программе команд вызова к адресам соответствующих процедур и функций в библиотеке. В качестве экспортного имени может выступать любая последовательность символов, причем между заглавными и строчными буквами делается различие.
По умолчанию экспортное имя подпрограммы совпадает с ее идентификатором в исходном тексте библиотеки (с учетом заглавных и строчных букв). Например, если секция exports имеет следующий вид,
exports
BubleSort;
то экспортное имя процедуры будет BubleSort. При желании экспортное имя можно сделать отличным от программного имени, дополнив описание директивой name, например:
exports
BubleSort name 'BubleSortIntegers';
В итоге, экспортное имя процедуры BubleSort будет BubleSortIntegers.
Экспортные имена подпрограмм должны быть уникальны в пределах библиотеки, поэтому их нужно всегда указывать явно для перегруженных (overload) процедур и функций. Например, если имеются две перегруженные процедуры с общим именем QuickSort,
procedure QuickSort(var Arr: array of Integer); overload; // для целых чисел
procedure QuickSort(var Arr: array of Real); overload; // для вещественных
то при экспорте этим двум процедурам необходимо явно указать отличные друг от друга экспортные имена:
exports
QuickSort(var Arr: array of Integer) name 'QuickSortIntegers';
QuickSort(var Arr: array of Real) name 'QuickSortReals';
Полные списки параметров нужны для того, чтобы компилятор мог разобраться, о какой процедуре идет речь в каждом случае.
Соглашения о вызове подпрограмм
В различных языках программирования используются различные правила вызова подпрограмм, и для совместимости с ними в языке Delphi существуют директивы register, stdcall, pascal и cdecl. Применение этих директив становится особенно актуальным при разработке динамически загружаемых библиотек, которые используются в программах, написанных на других языках программирования.
Чтобы разобраться с применением директив, обратимся к механизму вызова подпрограмм. Он основан на использовании стека.
Примечание. Стек - это область памяти, в которую данные помещаются в прямом порядке, а извлекаются в обратном, по аналогии с детской пирамидкой. Очередность работы с элементами в стеке обозначается термином LIFO (от англ. Last In, First Out - последним вошел, первым вышел).
Существует еще обычная очередность работы с элементами, обозначаемая термином FIFO (от англ. First In, First Out - первым вошел, первым вышел).
Для каждой программы на время работы создается свой стек. Через него передаются параметры подпрограмм и в нем же сохраняются адреса возврата из этих подпрограмм. Именно благодаря стеку подпрограммы могут вызывать друг друга, или даже рекурсивно сами себя.
Вызов подпрограммы состоит из «заталкивания» в стек всех аргументов и адреса следующей команды (для возврата к ней), а затем передачи управления на начало подпрограммы. По окончании работы подпрограммы из стека извлекается адрес возврата с передачей управления на этот адрес; одновременно с этим из стека выталкиваются аргументы. Происходит так называемая очистка стека. Это общая схема работы и у нее бывают разные реализации. В частности, аргументы могут помещаться в стек либо в прямом порядке (слева направо, как они перечислены в описании подпрограммы), либо в обратном порядке (справа налево), либо вообще, не через стек, а через свободные регистры процессора для повышения скорости работы. Кроме того, очистку стека может выполнять либо вызываемая подпрограмма, либо вызывающая программа. Выбор конкретного соглашения о вызове обеспечивают директивы register, pascal, cdecl и stdcall. Их смысл поясняет таблица 1.
Таблица 1. Соглашения о вызове подпрограмм.
Директива |
Порядок занесения аргументов в стек |
Кто отвечает за очистку стека |
Передача аргументов через регистры |
register |
Слева направо |
Подпрограмма |
Да |
pascal |
Слева направо |
Подпрограмма |
Нет |
cdecl |
Справа налево |
Вызывающая программа |
Нет |
stdcall |
Справа налево |
Подпрограмма |
Нет |
Примечание. Директива register не означает, что все аргументы обязательно передаются через регистры процессора. Если число аргументов больше числа свободных регистров, то часть аргументов передается через стек.
Для процедур и функций динамически загружаемых библиотек следует выбирать соглашение о вызове stdcall:
procedure BubleSort(var Arr: array of Integer); stdcall;
procedure QuickSort(var Arr: array of Integer); stdcall;
Именно соглашение stdcall, изначально предназначенное для вызова подпрограмм операционной системы, лучше всего подходит для взаимодействия программ и библиотек, написанных на разных языках программирования. Все программы так или иначе используют функции операционной системы, следовательно они обязательно поддерживают соглашение stdcall.
Использование библиотеки в программе
Для того чтобы в прикладной программе воспользоваться процедурами и функциями библиотеки, необходимо выполнить так называемый импорт. Импорт обеспечивает загрузку библиотеки в оперативную память и привязку записанных в программе команд вызова к адресам соответствующих процедур и функций библиотеки. Существуют два способа импорта, отличающихся по удобству и гибкости программирования:
статический импорт (обеспечивается директивой компилятора external);
динамический импорт (обеспечивается функциями LoadLibrary и GetProcAddress).
Статический импорт является более удобным, а динамический - более гибким.
Статический импорт
Импорт из DLL может проводиться по имени процедуры (функции), порядковому номеру или с присвоением другого имени.
При статическом импорте все действия по загрузке и подключению библиотеки выполняются автоматически операционной системой во время запуска главной программы. Чтобы задействовать статический импорт, достаточно просто объявить в программе процедуры и функции библиотеки как внешние. Это делается с помощью директивы external, например:
// вызов по имени
procedure BubleSortIntegers (var Arr: array of Integer); stdcall;
external 'SortLib.dll';
procedure QuickSort(var Arr: array of Integer); stdcall;
external 'SortLib.dll';
procedure HelloWorld(AForm : TForm); stdcall;
external 'SortLib.dll';
После ключевого слова external записывается имя двоичного файла библиотеки в виде константной строки или константного строкового выражения.
Если вы хотите изменить имя импортируемой функции (процедуры), то можно использовать директиву name, которая служит для явного указания экспортного имени процедуры в библиотеке. С ее помощью объявления процедур можно переписать по-другому:
// присвоением другого имени внешней процедуре
procedure QuickSort(var Arr: array of Integer); stdcall;
external 'SortLib.dll' name 'QuickSortIntegers';
Импорт по порядковому номеру требует от вас указание этого самого номера:
procedure SayHelloWorld(AForm : TForm);
external 'myfirstdll.dll' index 15;
В этом случае имя, которое вы даете процедуре при импорте не обязательно должно совпадать с тем, которое было указано для нее в самой DLL. Т.е. приведенная выше запись означает, что вы импортируете из динамической библиотеки myfirstdll.dll процедуру, которая в ней экспортировалась пятнадцатой, и при этом в рамках вашего приложения этой процедуре дается имя SayHelloWorld.
Поместив в программу приведенные выше объявления, можно вызывать процедуры BubleSort, QuickSort и HelloWorld, как будто они являются частью самой программы.
…
Var Arr: array [0..9] of Integer;
I: Integer;
begin
Randomize;
// Заполнение массива случайными числами
for I := Low(Arr) to High(Arr) do Arr[I] := Random(100);
// сортировка
BubleSort(Arr);
// вызов процедуры HelloWorld
HelloWorld(self);
…
end.
Примечание. Компиляция программы не требует наличия компилированной библиотеки, а это значит, что их разработка может осуществляться совершенно независимо, причем разными людьми. Нужно лишь договориться о типах и списках параметров, передаваемых в процедуры и функции, а также выбрать единое соглашение о вызове.
Динамический импорт
Модуль импорта
При разработке динамически загружаемых библиотек нужно всегда думать об их удобном использовании. Если в библиотеке не две процедуры, а сотня, и нужны они не в одной программе, а в нескольких, то в этом случае намного удобнее вынести external-объявления процедур в отдельный модуль, подключаемый ко всем программам в секции uses. Такой модуль условно называют модулем импорта. Кроме объявлений внешних подпрограмм он обычно содержит определения типов данных и констант, которыми эти подпрограммы оперируют.
Модуль импорта для библиотеки SortLib будет выглядеть так:
unit SortLib;
interface
procedure BubleSort(var Arr: array of Integer); stdcall;
procedure QuickSort(var Arr: array of Integer); stdcall;
procedure HelloWorld(AForm : TForm); stdcall;
implementation
const DllName = 'SortLib.dll';
procedure BubleSort(var Arr: array of Integer); external
DllName name 'BubleSortIntegers';
procedure QuickSort(var Arr: array of Integer); external
DllName name 'QuickSortIntegers';
procedure HelloWorld(AForm : TForm); external DllName;
end.
Выполняемый файл библиотеки должен всегда сопровождаться модулем импорта, чтобы потребитель мог разобраться с параметрами подпрограмм и правильно воспользоваться библиотекой.
Динамический импорт
Действия по загрузке и подключению библиотеки (выполняемые при статическом импорте автоматически) можно проделать самостоятельно, обратившись к стандартным функциям операционной системы. Таким образом, импорт можно произвести динамически во время работы программы (а не во время ее запуска).
Для динамического импорта необходимо загрузить библиотеку в оперативную память вызовом функции LoadLibrary, а затем извлечь из нее адреса подпрограмм с помощью функции GetProcAddress. Полученные адреса нужно сохранить в процедурных переменных соответствующего типа. После этого вызов подпрограмм библиотеки может выполняться путем обращения к процедурным переменным. Для завершения работы с библиотекой необходимо вызвать функцию FreeLibrary.
Описание функций:
LoadLibrary(LibFileName: PChar): HModule - загружает в оперативную память библиотеку, которая хранится на диске в файле с именем LibFileName. При успешном выполнении функция возвращает числовой описатель библиотеки, который должен использоваться в дальнейшем для управления библиотекой. Если при загрузке библиотеки призошла какая-нибудь ошибка, то возвращается нулевое значение. Если аргумент LibFileName содержит имя файла без маршрута, то этот файл ищется в следущих каталогах: в каталоге, из которого была запущена главная программа, в текущем каталоге, в системном каталоге операционной системы Windows (его точный маршрут можно узнать вызовом функции GetSystemDirectory), в каталоге, по которому установлена операционная система (его точный маршрут можно узнать вызовом функции GetWindowsDirectory), а также в каталогах, перечисленных в переменной окружения PATH.
FreeLibrary(LibModule: HModule): Bool - выгружает библиотеку, заданную описателем LibModule, из оперативной памяти и освобождает занимаемые библиотекой ресурсы системы.
GetProcAddress(Module: HModule; ProcName: PChar): Pointer - возвращает адрес подпрограммы с именем ProcName в библиотеке с описателем Module. Если подпрограмма с именем ProcName в библиотеке не существует, то функция возвращает значение nil (пустой указатель).
Приведенная ниже программа TestDynamicImport использует технику динамического импорта:
program TestDynamicImport;
uses Windows;
type
TBubleSortProc = procedure (var Arr: array of Integer); stdcall;
TQuickSortProc = procedure (var Arr: array of Integer); stdcall;
var
BubleSort: TBubleSortProc; // указатель на функцию BubleSort
QuickSort: TQuickSortProc; // указатель на функцию QuickSort
LibHandle: HModule; // описатель библиотеки
Arr: array [0..9] of Integer;
I: Integer;
begin
LibHandle := LoadLibrary('SortLib.dll');
if LibHandle <> 0 then
begin
@BubleSort := GetProcAddress(LibHandle, 'BubleSortIntegers');
@QuickSort := GetProcAddress(LibHandle, 'QuickSortIntegers');
if (@BubleSort <> nil) and (@QuickSort <> nil) then
begin
Randomize;
for I := Low(Arr) to High(Arr) do Arr[I] := Random(100);
BubleSort(Arr);
…
end
else Writeln('Ошибка отсутствия процедуры в библиотеке.');
FreeLibrary(LibHandle);
end
else Writeln('Ошибка загрузки библиотеки.');
…
end.
В программе определены два процедурных типа данных, которые по списку параметров и правилу вызова (stdcall) соответствуют подпрограммам сортировки BubleSort и QuickSort в библиотеке:
type
TBubleSortProc = procedure (var Arr: array of Integer); stdcall;
TQuickSortProc = procedure (var Arr: array of Integer); stdcall;
Эти типы данных нужны для объявления процедурных переменных, в которых сохраняются адреса подпрограмм:
Var BubleSort: TBubleSortProc;
QuickSort: TQuickSortProc;
В секции var объявлена также переменная для хранения целочисленного описателя библиотеки, возвращаемого функцией LoadLibrary: LibHandle: HModule;
Программа начинает свою работу с того, что вызывает функцию LoadLibrary, в которую передает имя файла DLL-библиотеки. Функция возвращает описатель библиотеки, который сохраняется в переменной LibHandle: LibHandle := LoadLibrary('SortLib.dll');
Если значение описателя отлично от нуля, значит, библиотека была найдена на диске и успешно загружена в оперативную память. Убедившись в этом, программа обращается к функции GetProcAddress за адресами подпрограмм. Полученные адреса сохраняются в соответствующих процедурных переменных:
@BubleSort := GetProcAddress(LibHandle, 'BubleSortIntegers');
@QuickSort := GetProcAddress(LibHandle, 'QuickSortIntegers');
Использование символа @ перед именем каждой переменной говорит о том, что выполняется не вызов подпрограммы, а работа с ее адресом. Если этот адрес отличен от значения nil, значит подпрограмма с указанным именем была найдена в библиотеке и ее можно вызвать путем обращения к процедурной переменной:
if (@BubleSort <> nil) and (@QuickSort <> nil) then
begin
...
BubleSort(Arr);
...
end
По окончании сортировки программа выгружает библиотеку вызовом функции FreeLibrary.
Динамический импорт в сравнении со статическим требует значительно больше усилий на программирование, но он имеет ряд преимуществ:
Более эффективное использование ресурсов оперативной памяти по той причине, что библиотеку можно загружать и выгружать по мере надобности;
Динамический импорт помогает в тех случаях, когда некоторые процедуры и функции могут отсутствовать в библиотеке. При статическом импорте такие ситуации обрабатывает операционная система, которая выдает сообщение об ошибке и прекращает работу программы. Однако при динамическом импорте программа сама решает, что ей делать, поэтому она может отключить часть своих возможностей и работать дальше.
Динамический импорт отлично подходит для работы с библиотеками драйверов устройств. Он, например, используется самой средой Delphi для работы с драйверами баз данных.
Глобальные переменные и константы
Глобальные переменные и константы, объявленные в библиотеке, не могут быть экспортированы, поэтому если необходимо обеспечить к ним доступ из использующей программы, это нужно делать с помощью функций, возвращающих значение.
Несмотря на то, что библиотека может одновременно подключаться к нескольким программам, ее глобальные переменные не являются общими и не могут быть использованы для обмена данными между программами. На каждое подключение библиотеки к программе, операционная система создает новое множество глобальных переменных, поэтому библиотеке кажется, что она работает лишь с одной программой. В результате программисты избавлены от необходимости согласовывать работу нескольких программ с одной библиотекой.
Инициализация и завершение работы библиотеки
Инициализация библиотеки происходит при ее подключении к программе и состоит в выполнении секций initialization во всех составляющих библиотеку модулях, а также в ее главном программном блоке. Завершение работы библиотеки происходит при отключении библиотеки от программы; в этот момент в каждом модуле выполняется секция finalization. Нужно использовать эти секции тогда, когда библиотека запрашивает и освобождает какие-то системные ресурсы, например файлы или соединения с базой данных. Запрос ресурса выполняется в секции initialization, а его освобождение - в секции finalization.
Существует еще один способ инициализации и завершения библиотеки, основанный на использовании предопределенной переменной DllProc. Переменная DllProc хранит адрес процедуры, которая автоматически вызывается при отключении библиотеки от программы, а также при создании и уничтожении параллельных потоков в программах, использующих DLL-библиотеку.
Пример использования переменной DllProc:
library MyLib;
var SaveDllProc: TDLLProc;
procedure LibExit(Reason: Integer);
begin
if Reason = DLL_PROCESS_DETACH then
begin
// завершение библиотеки
end;
SaveDllProc(Reason); // вызов предыдущей процедуры
end;
begin
... // инициализация библиотеки
SaveDllProc := DllProc; // сохранение предыдущей процедуры
DllProc := @LibExit; // установка процедуры LibExit
end.
Процедура LibExit получает один целочисленный аргумент, который уточняет причину вызова. Возможные значения аргумента:
DLL_PROCESS_DETACH — отключение программы;
DLL_PROCESS_ATTACH — подключение программы;
DLL_THREAD_ATTACH — создание параллельного потока;
DLL_THREAD_DETACH — завершение параллельного потока.
Обратите внимание, что установка значения переменной DllProc выполняется в главном программном блоке, причем предыдущее значение сохраняется для вызова "по цепочке".
Рекомендуется прибегать к переменной DllProc лишь в том случае, если библиотека должна реагировать на создание и уничтожение параллельных потоков. Во всех остальных случаях лучше выполнять инициализацию и завершение с помощью секций initialization и finalization.
Исключительные ситуации и ошибки выполнения подпрограмм
Для поддержки исключительных ситуаций среда Delphi использует средства операционной системы Window. Поэтому, если в библиотеке возникает исключительная ситуация, которая никак не обрабатывается, то она передается вызывающей программе. Программа может обработать эту исключительную ситуацию самым обычным способом — с помощью операторов try … except ... end. Такие правила действуют для программ и DLL-библиотек, созданных в среде Delphi. Если же программа написана на другом языке программирования, то она должна обрабатывать исключение в библиотеке, написанной на языке Delphi как исключение операционной системы с кодом $0EEDFACE. Адрес инструкции, вызвавшей исключение, содержится в первом элементе, а объект, описывающий исключение, - во втором элементе массива ExceptionInformation, который является частью системной записи об исключительной ситуации.
Если библиотека не подключает модуль SysUtils, то обработка исключительных ситуаций недоступна. В этом случае при возникновении в библиотеке любой ошибки происходит завершение вызывающей программы, причем программа просто удаляется из памяти и код ее завершения не выполняется. Это может стать причиной побочных ошибок, поэтому если вы решите не подключать к библиотеке модуль SysUtils, позаботьтесь о том, чтобы исключения "не выскальзывали" из подпрограмм библиотеки.
Использование стандартных системных переменных IsLibrary и CmdLine
В Delphi существует стандартный модуль System, неявно подключаемый к каждой программе или библиотеке. В этом модуле содержатся предопределенные системные подпрограммы и переменные. Среди них имеется переменная IsLibrary с типом Boolean, значение которой равно True для кода вызываемого из библиотеки и False в случае выполнения процедуры или функции из вызывающего приложения. Проверив значение переменной IsLibrary, подпрограмма может определить, является ли она частью библиотеки.
В модуле System объявлена также переменная CmdLine: PChar, содержащая командную строку, которой была запущена программа. Библиотеки не могут запускаться самостоятельно, поэтому для них переменная CmdLine всегда содержит значение nil.
Лекция 15 тема: Разработка собственных компонентов.
Литература: 1. Создание собственных компонент: http://citforum.ru/programming/32less/index.shtml
2. Тейксейра Стив, Пачеко Ксавье. Borland Delphi 6. Руководство разработчика. : Пер. с англ. — М. : Издательский дом “Вильямс”, 2002.
3. Учебник по Delphi. Главы 7, 8. / wm-help.net - Электронная библиотека: http://wm-help.net/books-online/book/56472/56472-93.html
Delphi является открытой средой и позволяет не только использовать объекты из Библиотеки Визуальных Компонент (VCL) в своей программе, но и создавать новые объекты. Причем, ничего другого, кроме Delphi, для этого не требуется. Создание нового объекта в Delphi не является очень сложной задачей, хотя для этого и требуется знание Windows API, объектно-ориентированного программирования и иерархии классов в VCL.
Может возникнуть вопрос: если в Delphi уже есть своя библиотека, то зачем еще создавать какие-то объекты? Для разработки нового компонента существует несколько важных причин:
Если необходимо разработать новый элемент пользовательского интерфейса и в дальнейшем применять его в разных приложениях.
Упрощение кода приложения, путем введения новых.
Если среди существующих компонентов Delphi и элементов ActiveX нет такого, который полностью удовлетворял бы всем требованиям.
Если существуют потенциальные пользователи создаваемого компонента и его можно распространить среди других программистов либо за деньги, либо ради собственного удовольствия.
Если хочется глубже разобраться в Delphi, библиотеке VCL и функциях интерфейса API Win32.
Новые компоненты, во-первых, позволяют расширить область применения Delphi: например, с помощью библиотек объектов третьих фирм разрабатывать приложения для работы в Internet. Во-вторых, позволяют дополнить или настроить для себя имеющиеся в VCL объекты (например, переопределить значения свойств, устанавливаемые по умолчанию).
Примечание. Посмотреть исходный код библиотеки VCL можно в двух версиях Delphi: в Professional и Enterprise.
Основные шаги при создании нового компонента:
определить, какие действия должен выполнять компонент;
разработать краткий алгоритм, по которому будет работать компонент;
разбить всю конструкцию компонента на независимые части;
предоставьте возможность дальнейшей разработки компонента (возможно, в будущем вы захотите создать на его основе компонент-потомок);
написать код компонента (этот пункт разбивается на такие этапы):
выбор предка для компонента;
создание заготовки (модуля) компонента;
создание свойств, событий и методов компонента;
отладка и тестирование;
регистрация компонента в среде Delphi;
создание справки для компонента.
Выбор предка компонента
Прежде, чем приступить к написанию кода, нужно определиться, хотя бы приблизительно, что за компонент вы собираетесь делать. Далее, исходя из его предполагаемых свойств, определите класс-предок. В VCL имеется несколько базовых классов, рекомендуемых для наследования. Эти классы перечислены в таблице 1.
Таблица 1. Базовые классы VCL.
Класс |
Возможности класса |
TObject |
Классы, предком которых является данный класс, не являются компонентами. Класс TObject применяется при создании объектов, которые, обычно, являются предками для других компонентов. TObject можно использовать в качестве предка, если с этим компонентом не нужно работать во время дизайна. Это может быть, например, класс, содержащий значения переменных среды (environment) или класс для работы с INI файлами. |
TComponent |
Применяется для создания невизуальных компонентов. Данный класс обладает встроенной возможностью сохранять / считывать себя в потоке во время дизайна. |
TGraphicControl |
Применяется для создания визуальных неоконных компонентов, т. е. компонентов без дескриптора окна (handle). Потомки данного класса размещаются в клиентской области своих родительских компонентов рисуют прямо на своей поверхности и требуют мало ресурсов Windows. |
TWinControl |
Применяется для создания компонентов, имеющих дескриптор окна (handle), который используют при доступе к возможностям Windows через API. Данные компоненты являются компонентами оконного типа и могут содержать в себе другие компоненты. |
TCustomControl |
Этот класс является потомком TWinControl и дополняет его областью вывода (канвой). В данный класс добавлен метод Paint для лучшего контроля за прорисовкой компонента. Рекомендуется использовать настоящий класс для создания пользовательских оконных компонентов. |
TCustomClassName |
Библиотека визуальных компонентов содержит несколько классов, у которых не все свойства объявлены как published, т. е. доступные из других модулей, но на основе данных классов можно создавать классы-потомки, в которых и объявлять данные свойства. Таким образом, разработчик может создать несколько идентичных классов на основе одного класса ClassName и в каждом из этих классов определять необходимые свойства из набора предопределенных свойств. |
TComponentName |
Класс вроде TEdit или TButton. Позволяет создавать компоненты-потомки, предками которых являются обычные компоненты или классы VCL Delphi. Таким образом, если перед разработчиком стоит задача расширить возможности какого-либо компонента Delphi, можно использовать данный класс с целью доопределения его свойств и методов или переопределения значения свойств, принимаемых по умолчанию. |
Обратите внимание на то, что для правильного выбора класса-предка, вам нужно очень хорошо ориентироваться в возможностях уже существующих в Delphi классов.
Создание заготовки для нового компонента
После выбора класса-предка для компонента можно приступать к созданию модуля компонента. Создание модуля (заготовки) для нового компонента можно выполнить путем вызова окна Delphi, которое называется экспертом компонентов (Component Expert). Данное окно можно вызвать путем выбора в главном меню Delphi пункта Component / New Component (Компонент / Новый компонент) или File / New / Other и в появившемся окне выбрать Component. При этом появляется окно, изображенное на рисунке 1.
Рис. 1. Эксперт для создания нового компонента.
В диалоге в поле Ancestor type нужно указать имя класса-предка для нового компонента. Это поле ввода содержит в выпадающем списке все зарегистрированные классы библиотеки VCL. Предположим, что мы будем создавать компонент, предком которого является кнопка TButton. Для этого выберем в выпадающем списке класс TButton. Поле Class Name предназначено для ввода имени нового класса. Пусть в нашем случае это будет новый класс TMyButton. Поле Palette Page показывает, на какой вкладке палитры компонентов будет расположен новый компонент после его регистрации. Оставим в этом поле значение, предлагаемое Delphi по умолчанию Samples. Два следующих поля Unit file name и Search path заполняются средой Delphi самостоятельно, но разработчик может их изменить. В поле Unit file name указывается имя модуля регистрации (полный путь к файлу с расширением pas, в котором содержится процедура регистрации). В поле Search path указываются пути поиска модуля регистрации.
После заполнения полей данного окна нажимаем кнопку ОК, и Delphi автоматически создаст заготовку модуля нового компонента:
unit MyButton;
interface
uses
SysUtils, Classes, Controls, StdCtrls;
type
TMyButton = class(TButton)
private
{ Private declarations }
protected
{ Protected declarations }
public
{ Public declarations }
published
{ Published declarations }
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TMyButton]);
end;
end.
Модуль содержит декларацию нового класса и процедуру его регистрации в Палитре Компонент. В процедуре RegisterComponents первый параметр - имя страницы (можно указать свое имя - появится новая страница); второй параметр - множество объектов для регистрации.
Создание свойств, событий и методов компонента Соглашения по наименованиям
Если рассмотреть исходные тексты VCL, то могли увидеть, что они следуют нескольким простым соглашениям при определении новых классов. Delphi этого не требует, имена методов, свойств и т.п. могут быть любыми, компилятору это безразлично. Но если следовать этим соглашениям, то разработка новых компонентов и чтение исходных текстов станет существенно проще.
Итак:
Все декларации типов начинаются на букву T. Delphi не требует этого, но это делает очевидным, что "TEdit", например, есть определение типа, а не переменная или поле класса.
Имена свойствам нужно давать легко читаемые и информативные. Нужно помнить, что пользователь будет их видеть в Инспекторе Объектов. И имя вроде "TextOrientation" много удобнее, нежели "TxtOr". То же самое относится к методам. Методы, доступные пользователю, должны иметь удобные названия.
При создании свойств типа Event, имя такого свойства должно начинаться с "On" (например, OnClick, OnCreate и т.д.).
Имя метода для чтения свойства должен начинаться со слова "Get". Например, метод GetStyle должен выполнять чтение для свойства Style.
Имя метода для записи свойства должен начинаться со слова "Set". Например, метод SetStyle должен выполнять запись в свойство Style.
Внутреннее поле для хранения данных свойства должно носить имя, начинающееся с буквы "F". Например, свойство Handle могло бы храниться в поле FHandle.
Конечно же, есть исключения из правил. Иногда бывает удобнее их нарушить, например, класс TTable имеет свойства типа Event, которые называются BeforePost, AfterPost и т.п.
Создание свойств компонента
Для добавления новых свойств в компонент достаточно задать поля и свойства, определив при этом их тип и доступ (чтение, запись).
Например, создадим новое свойство - счетчик нажатий на кнопку. Для этого в секции private объявим поле FClickCount : Longint (внутреннее поле для сохранения значения), а в секции published объявим свойство: property ClickCount : Longint read FClickCount write FClickCount. Задание этого свойства в разделе published будет гарантировать доступ к нему в окне инспектора объектов и не потребует написания дополнительных методов для доступа к нему.
Примечание. 1.За ключевым словом write указывается идентификатор поля или метода на запись свойства; метод должен быть процедурой, получающей параметр одного с полем типа. За ключевым словом read указываем идентификатор поля или метода на чтение объекта; метод должен быть функцией, возвращающей значение одного со свойством типа. Одна из этих директив может быть опущена, и тогда значение в поле нельзя будет читать или писать извне (свойство уже не попадёт в Object Inspector).
2. Указание свойства в секции public позволит обращаться к свойству в методах, но в Инспекторе объектов свойство отображаться не будет.
Теперь исходный текст выглядит так:
unit MyButton;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls;
type
TMyButton = class(TButton)
private
FClickCount : Longint;
protected
{ Protected declarations }
public
{ Public declarations }
published
property ClickCount : Longint read FClickCount write
FClickCount;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TMyButton]);
end;
end.
Создание перечисляемых свойств компонента
К свойствам перечисляемого типа относятся такие свойства компонента, которые при их редактировании в окне инспектора объектов вызывают выпадающий список, содержащий возможные значения данного свойства. К числу подобных свойств относятся Align, BorderStyle, Color и др. Для того чтобы самостоятельно добавить в новый компонент перечисляемое свойство, необходимо сначала определить новый перечисляемый тип, например:
TMyEnumType = (eFirst, eSecond, eThird);
После этого нужно определить поле компонента, которое будет хранить значение данного перечисляемого типа, затем определить свойство. Пример добавления перечисляемого типа в новый компонент TMyButton приведен ниже.
type
TMyEnumType = (eFirst, eSecond, eThird);
type
TMyButton = class(TButton)
private
FMyEnum: TMyEnumType;
Protected
{ Protected declarations }
Public
{ Public declarations }
Published
property MyEnumProp: TMyEnumType read FMyEnum write FMyEnum;
end;
Таким образом, в окне инспектора объектов, при изменении свойства MyEnumProp, будет выдан выпадающий список, содержащий три пункта: eFirst, eSecond и eThird (рис. 2).
Рис. 2. Перечисляемое свойство MyEnumProp в новом компоненте TMyButton.
Создание свойств-множеств в компоненте
Тип множества часто фигурировал в Object Pascal, и некоторые свойства компонентов Delphi имеют данный тип. Когда вы используете свойство типа set, вы должны учитывать, что каждый элемент множества будет являться отдельным свойством, имеющим логический тип в инспекторе объектов.
Для создания свойства-множества сначала зададим нужный тип:
TMySetTypeFirst = (poFirst, poSecond, poThird);
TMySetType = set of TMySetTypeFirst;
Первая строка задает перечисляемый тип TMySetTypeFirst, который определяет диапазон множества, а вторая строка – само множество TMySetType.
Пример добавления свойства-множества в компонент TMyButton:
type
TMyEnumType = (eFirst, eSecond, eThird);
TMySetTypeFirst = (poFirst, poSecond, poThird);
TMySetType = set of TMySetTypeFirst;
type
TMyButton = class(TButton)
private
FMyEnum: TMyEnumType;
FMyOptions: TMySetType;
protected
{ Protected declarations }
public
{ Public declarations }
published
property MyEnumProp: TMyEnumType read FMyEnum write FMyEnum;
property MyOptions: TMySetType read FMyOptions write
FMyOptions;
end;
В результате, в окне инспектора объектов свойство-множество будет отображаться, как представлено на рисунке 3.
Рис. 3. Свойство-множество MyOptions в новом компоненте TMyButton.
Создание свойства-объекта в компоненте
Каждый компонент может содержать в себе свойство-объект. В качестве свойства-объекта может выступать любой компонент или объект Delphi. Кроме того, свойствами-объектами нового компонента могут быть новые компоненты или объекты, которые вы создали самостоятельно. Важным условием является тот факт, что свойства-объекты должны быть потомками класса TPersistent. Это необходимо для того, чтобы свойства объекта-свойства отображались в окне инспектора объектов.
Для начала создадим произвольный новый объект, являющийся прямым потомком класса TPersistent:
type
TMyObject = class (TPersistent}
private
FPropertyl: Real;
FProperty2: Char;
protected
{ Protected declarations }
public
procedure Assign (Source: TPersistent);
published
property Propertyl: Real read FPropertyl write FPropertyl;
property Property2: Char read FProperty2 write FProperty2;
end;
В качестве предка нового класса может выступать не только класс TPersistent, но любой его потомок. В вышеприведенном листинге создается новый класс TMyObject, в котором присутствует два простых свойства – Propertyl и Property2. Кроме того, в новый объект включена процедура Assign. Данная процедура необходима для обеспечения правильного доступа к свойству нашего будущего компонента TMyButton. Ниже приведен листинг, в котором в компонент TMyButton добавляется новое свойство-объект.
type
TMyButton = class (TButton)
private
FMyObject: TMyObject;
procedure SetMyObject (Value: TMyObject);
protected
{ Protected declarations }
public
constructor Create (AOwner: TComponent); override;
destructor Destroy; override;
published
property MyObject: TMyObject read FMyObject write SetMyObject;
end;
Также в секцию public нового компонента добавили конструктор и деструктор объекта.
Теперь осталось дописать конструктор и деструктор для компонента TMyButton, в которых необходимо соответственно создать и уничтожить, кроме компонента TMyButton, еще и объект TMyObject. А также нужно написать код метода SetMyObject, который будет помещать новое значение в свойства объекта TMyObject. Ну и, конечно, написать код метода Assign для объекта TMyObject. Далее приведена полная версия кода.
type
TMyObject = class (TPersistent)
private
FPropertyl: Real;
FProperty2: Char;
protected
{ Protected declarations }
public
procedure Assign (Source: TPersistent);
published
property Propertyl: Real read FPropertyl write FPropertyl;
property Property2: Char read FProperty2 write FProperty2;
end;
type
TMyButton = class (TButton)
private
FMyObject: TMyObject;
procedure SetMyObject (Value: TMyObject);
protected
{ Protected declarations }
public
constructor Create (AOwner: TComponent); override;
destructor Destroy; override;
published
property MyObject: TMyObject read FMyObject write SetMyObject;
end;
procedure Register;
implementation
procedure TMyButton.SetMyObject (Value: TMyObject);
begin
if Assigned (Value) then FMyObject.Assign (Value);
end;
constructor TMyButton.Create (AOwner; TComponent);
begin
inherited Create (AOwner);
FMyObject:=TMyObject.Create;
end;
destructor TMybutton.Destroy;
begin
FMyObject.Free;
inherited Destroy;
end;
procedure TMyObject.Assign (Source: TPersistent);
begin
if Source is TMyObject then
begin
FPropertyl:=TMyObject (Source).Property1;
FProperty2:=TMyObject (Source).Property2;
inherited Assign (Source);
end;
end;
Обратите внимание на реализацию метода TMyObject.Assign. Здесь сначала выполняется проверка на то, что передается правильный экземпляр объекта TMyObject. Если он правильный, то значения свойств (Source) Переносятся в поля FPropertyl и FProperty2 объекта TMyObject. Результатом исполнения вышеприведенного кода будет новый компонент TMyButton, содержащий в себе свойство-объект TMyObject. В окне инспектора объектов это будет выглядеть, как представлено на рисунке 4.
Рис. 4. Свойство-объект MyObject в новом компоненте TMyButton.
Примечание. При объявлении конструктора использовалась следующая строка:
constructor Create (AOwner: TComponent); override;
Слово "override" указывает на то, что стандартный конструктор для компонента-предка будет перекрыт.
Строка inherited Destroy; в реализации деструктора означает, что будут выполнены стандартные методы деструктора компонента-предка.
Создание свойства-массива в компоненте
Свойства компонента могут быть практически любого типа, которые поддерживает язык Object Pascal. Некоторые свойства могут быть массивами. Яркими примерами свойств такого типа являются ТМеmо, Lines, TDBGrid. Columns и др. Подобные свойства требуют собственных редакторов. Мы пока остановимся на создании простого свойства, которое представляет из себя массив. Создадим новый компонент Tweek, содержащий два свойства: Month и Number. Свойство Month будет представлять собой массив, возвращающий название месяца по переданному целому числу от 1 до 12. Свойство Number – тоже массив, который возвращает число, соответствующее названию месяца.
Код компонента TWeek:
unit Week;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs;
type
TWeek = class(TComponent)
private
function GetMonthName (const AIndex: Integer): String;
function GetMonthNumber (const AMonthName: String): Integer;
protected
{ Protected declarations }
public
property MonthName[const AIndex: Integer]: String
read GetMonthName; default;
property MonthNumber[const AMonthName: String]: Integer
read GetMonthNumber;
published
{ Published declarations }
end;
procedure Register;
implementation
const
MonthNames: array[1..12] of String[8]= ('Январь', 'Февраль',
'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август',
'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь');
function TWeek.GetMonthName(const AIndex: Integer): String;
begin
if (AIndex<=0) or (AIndex>12) then
raise Exception.Create('Номер месяца должен быть числом от 1 до
12')
else Result:= MonthNames[AIndex];
end;
function TWeek.GetMonthNumber(const AMonthName: String): Integer;
var i:integer;
begin
Result:=0;
for i:=l to 12 do
if AnsiUppercase(AMonthName)=AnsiUpperCase(MonthNames[i]) then
Result:=i;
end;
procedure Register;
begin
RegisterComponents('Samples', [TWeek]);
end;
end.
Рассмотрим вышеприведенный код более подробно. Как вы можете видеть, свойства типа Array объявляются вместе с индексами. Для свойства MontnName мы определили индекс AIndex, а для свойства MonthNumber - индекс AMonthName. Для доступа к свойствам такого типа необходимо использовать методы. Внутренних полей здесь нет. Если свойство типа Array многомерно, то свойство-массив объявляется сразу с несколькими индексами. При вызове методов доступа к ним нужно передавать параметры в том же порядке, в каком вы их указали в свойстве.
Функция GetMonthName возвращает строку, содержащую имя месяца, соответствующего целому числу от 1 до 12, переданному в качестве параметра данной функции. В случае передачи функции числа, не принадлежащему данному диапазону, будет сгенерировано исключение командой raise.
Функция GetMonthNumber возвращает число от 1 до 12, которое соответствует названию месяца, переданного в качестве параметра в данную функцию. В случае если ни один месяц не соответствует названию, определенному массивом MonthNames, результатом выполнения данной функции будет ноль.
Таким образом, если поместить на форму экземпляр компонента TWeek с именем Week1, при выполнении строки ShowMessage (Weekl.MonthName[5]); будет выдано окно с сообщением Май.
Команды Default и NoDefault
Многим свойствам можно присвоить конкретное значение, которое будет установлено по умолчанию. Для этого достаточно присвоить это значение полю компонента, например:
FMyProperty := 10;
В результате чего, при каждом добавлении компонента на форму свойство MyProperty будет принимать значение 10.
Команды Default и NoDefault применяются для ускорения процесса загрузки формы при работе приложения. Например:
property MyCount: Integer read FMyCount write FmyCount Default 0;
Данный код не присваивает значение 0 свойству MyCount. При выполнении вышеприведенного кода команда Default 0 означает следующее: если при сохранении формы, содержащей компонент, значение свойства MyCount не будет равно нулю, то новое значение сохранится в файле формы, иначе значение данного свойства не будет сохранено.
Примечание. Рекомендуется использовать команду Default во всех случаях, когда это возможно, если вы хотите создать быстро работающее приложение.
Команда NoDefault предназначена для нейтрализации команды Default. Команда применяется для отмены команды Default компонентов-предков. Пример использования команды NoDefault:
TSecondComponent = class (TMyButton)
published
property MyCount NoDefault 0;
Переопределение значений свойств компонента-предка
Для того чтобы переопределить начальное значение свойства при создании объекта, нужно переписать конструктор Create, в котором и присвоить этому свойству нужное значение (не забыв перед этим вызвать конструктор предка).
Изменим в компоненте MyButton значение по умолчанию свойства ShowHint на True:
constructor TMyButton.Create(AOwner : TComponent);
begin
inherited Create(AOwner); // вызов конструктора предка
ShowHint:=True; // изменение значения свойства по-умолчанию
FClickCount:=0; //очистка значения поля для подсчета нажатий на
// кнопку
end;
Создание событий компонента
Определение. Событие - это любое действие, произошедшее благодаря операционной системе, действиям пользователя, работе программы.
Событие можно "перехватить" и обработать с помощью программы-обработчика события. Связь между событием и программой-обработчиком называется свойством-событием. Таким образом, когда происходит какое-либо событие компонента, он может обработать данное событие. Для этого сначала происходит проверка наличия кода обработки события. Если подобный код есть - он выполняется.
Рассмотрим в качестве примера такое часто возникающее событие, как нажатие левой кнопки мыши OnClick. Данное событие, как и многие другие, имеет так называемые методы диспетчеризации событий (event-dispatching methods). Эти методы нужны как раз для того, чтобы определять, создан ли код обработки произошедшего события для данного компонента. Эти методы объявляются как защищенные (protected). Таким образом, для свойства OnClick определен метод диспетчеризации события Click:
TControl = class (TComponent)
private
FOnClick: TNotifyEvent;
protected
procedure Click; dynamic;
property OnClick: TNotifyEvent read FOnClick write FOnClick;
end;
implementation
procedure TControl.Click;
begin
if Assigned (FOnClick) then FOnClick (Self);
end;
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
Canvas.TextOut(X, Y, '('+IntToStr(X)+', '+IntToStr(Y)+')');
end;
Рис. 5. Результат обработки события OnMouseDown.
Пример создания нового события компонента
Попробуем теперь создать собственное событие. Для этого нужно сначала убедиться, что такого события нет в VCL Delphi. Предположим, возникла необходимость создания события, которое возникает каждые 30 секунд. Естественно, для этого случая можно воспользоваться компонентом Timer, который расположен на вкладке System палитры компонентов Delphi. Но, предположим, что компонент должен иметь такое событие для удобства работы с ним. Код для создания события представлен ниже:
unit Halfmin;
interface
uses
Windows, Messages, SysUtils, Classes,Graphics, Controls, Forms, Dialogs, ExtCtrls;
type
TTimeEvent = procedure (Sender: TObj'ect; TheTime: TDateTime) of object;
THalfMinute = class (TComponent)
private
FTimer: TTimer;
FOnHalfMinute: TTimeEvent;
FOldSecond, FSecond: Word;
procedure FTimerTimer (Sender: TObject);
protected
procedure DoHalfMinute (TheTime: TDateTime); dynamic;
public
constructor Create (AOwner: TComponent); override;
destructor Destroy; override;
published
property OnHalfMinute: TTimeEvent read FOnHalfMinute write
FOnHalfMinute;
end;
implementation
constructor THalfMinute.Create (AOwner: TComponent);
begin
inherited Create (AOwner);
if not (csDesigning in ComponentState) then begin
FTimer:=TTimer.Create(self);
FTimer.Enabled:=True;
FTimer.Interval:=500;
FTimer.OnTimer:=FTimerTimer;
end;
end;
destructor THalfMinute.Destroy;
begin
FTimer.Free;
inherited Destroy;
end;
procedure THalfMinute.FTimerTimer (Sender: TObject);
var
DT: TDateTime;
Temp: Word;
begin
DT:=Now;
FOldSecond:=FSecond;
DecodeTime (DT,Temp,Temp,FSecond,Temp);
if FSecond <> FOldSecond then
if ((FSecond=30) or (FSecond=0)) then DoHalfMinute(DT);
end;
procedure THalfMinute.DoHalfMinute(TheTime: TDateTime);
begin
if Assigned (FOnHalfMinute) then FOnHalfMinute (Self, TheTime);
end;
end.
Для проверки работоспособности вышеприведенного кода вы можете добавить еще одну процедуру для регистрации нового компонента с именем THalfMinute, предварительно добавив в interface-часть программы строку: procedure Register;
Ниже представлен код для регистрации компонента:
procedure Register;
begin
RegisterComponents('Samples', [THalfMinute]);
end;
Для просмотра работоспособности нового компонента после его регистрации создадим новую форму и разместим на ней новый компонент. Добавим на форму компонент TEdit. Затем добавим обработчик события OnHalfMinute для формы:
procedure TForml.HalfMinute1HalfMinute(Sender: TObject; TheTime: TDateTime);
begin
Edit1.Text: = ('Время '+TimeToStr(TheTime));
Edit1.Refresh;
end;
В результате работы данной программы в компоненте Editl будет выводиться текущее время каждые 30 секунд.
Создание методов компонента
Добавление методов в новый компонент - операция несложная. Однако нужно обратить внимание на некоторые особенности, которые в дальнейшем облегчат взаимодействие пользователя с вашим компонентом.
Во-первых, необходимо, чтобы методы не были взаимозависимыми, т. е. каждый метод должен быть самостоятельным и законченным. Во-вторых, метод не должен блокировать компонент. И, в-третьих, метод должен иметь имя, соответствующее выполняемым действиям.
Методы объявляются в секциях private, public и protected. При создании нового метода важно учитывать, как он будет использоваться в дальнейшем, сможет ли данный компонент быть предком для других компонентов, и, в зависимости от этого, разместить методы в нужных секциях. В таблице 2 приведены рекомендации по выбору секций для методов компонента.
Таблица 2. Размещение методов компонента в различных секциях.
Секция |
Размещаемые методы |
Private |
В данной секции лучше всего размещать те методы, которые не могут изменяться в компонентах-потомках. Эти методы не доступны вне данного компонента |
Protected |
В этой секции размещают методы, которые будут доступны для изменения в компонентах-потомках |
Public |
Данная секция предназначена для размещения методов, которые доступны любому пользователю компонента. Доступ полный во время работы приложения, но не во время разработки, т. е. данные методы недоступны в окне инспектора объектов |
Published |
В этой секции размещаются свойства компонента, которые доступны во время разработки приложения в окне инспектора объектов |
Например, добавим в компонент TMyButton процедуру Click в которой будем подсчитывать количество нажатий на кнопку:
unit New_btn;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type
TMyButton = class(TButton)
private
FClickCount : Longint;
protected
{ Protected declarations }
public
constructor Create(AOwner : TComponent); override;
published
procedure Click; override;
property ClickCount : Longint read FClickCount write FClickCount;
end;
procedure Register;
implementation
constructor TMyButton.Create(AOwner : TComponent);
begin
inherited Create(AOwner);
ShowHint:=True;
FClickCount:=0;
end;
procedure TMyButton.Click;
begin
Inc(FClickCount);
inherited Click;
end;
procedure Register;
begin
RegisterComponents('Samples', [TMyButton]);
end;
end.
Регистрация компонента в среде Delphi
Регистрация компонента необходима для размещения компонента в палитре компонентов.
Перед регистрацией модуль компонента нужно сохранить.
При использовании эксперта компонентов для создания нового компонента Delphi самостоятельно создает процедуру регистрации компонента в модуле-заготовке. Создателю компонента в данном случае ничего не нужно более делать, кроме выполнения следующих шагов:
1. Выбрать пункт меню Component/Install Component. Появится диалог, как на рис. 6.
Рис. 6. Диалог установки нового компонента.
2. Далее нужно в поле Unit file name указать модуль, содержащий процедуру регистрации и в поле Package file name указать имя пакета, в который устанавливается компонент (выбираем пакет dclusr.dpk в котором содержатся пользовательские компоненты). Нажать "OK".
После успешной перекомпиляции новый объект появится в палитре.
Если же вы создаете компонент без использования эксперта компонентов, вам придется самостоятельно дописывать процедуру регистрации компонента. В разделе interface модуля компонента нужно дописать строку:
procedure Register;
А в разделе implementation добавить процедуру регистрации, например:
procedure Register;
begin
RegisterComponent ('Samples', [TMyComponent]);
end;
В результате, компонент с именем TMyComponent будет размещен на вкладке Samples палитры компонентов.
Создание пиктограммы для компонента
Delphi для новых компонентов
использует стандартный значок
.
Его можно поменять на любой другой. Для
создания собственного значка разумно
воспользоваться стандартной программой
Image Editor, которая входит в комплект
поставки Delphi. Можно использовать и любой
другой редактор растровых изображений.
Запустите Image Editor и создайте файл ресурсов, в котором будет лежать пиктограмма для представления данного объекта в Палитре Компонент (File/New.../Component Resource File(.dcr)). Файл ресурсов должен называться точно так же, как модуль регистрации компонента и иметь расширение .DCR (т.е., если объект регистрируется в модуле NEW_BTN.PAS, то тогда имя файла ресурсов будет NEW_BTN.DCR). В файле ресурсов должен находиться ресурс типа BITMAP - картинка размером 24x24 точки (можно меньше), название картинки должно совпадать с именем класса (в нашем случае TMYCOMPONENT): в контекстном меню выбираем New/Bitmap, и в появившемся диалоге:
Указываем размер 24x24, выбираем количество используемых цветов, нажимаем ОК и рисуем нужную пиктограмму. Созданную картинку называем командой Rename контекстного меню по имени компонента. Затем поместите файл ресурсов в ту папку, в которой находится файл с модулем компонента. Перекомпилируйте модуль, и компонент будет изображаться в палитре компонентов вашим рисунком.
Переустановка компонента
Очень часто бывает необходимо
переустановить ваш компонент. Если вы
попробуете сделать это путем выбора
Component->Install Component, то Дельфи вас честно
предупредит о том, что пакет уже содержит
модуль с таким именем. Перед вами
открывается окно с содержимым пакета.
В нем вы должны найти имя вашего компонента
и удалить его (либо нажать кнопочку
Remove
).
Затем проделайте стандартную процедуру
по установке компонента (или нажмите
кнопочку Add
).
Он будет включен уже с последними
изменениями (например, с новой
пиктограммой).
Далее нужно скомпилировать пакет –
нажать кнопку
.
Использование созданного компонента
После инсталляции компонента его можно использовать в своих приложениях.
type
TForm1 = class(TForm)
MyButton1: TMyButton; // на форму добавлен компонент типа
// TMyButton
…
procedure TForm1.MyButton1Click(Sender: TObject);
begin
{ в метке выводится значение свойства ClickCount компонента, в котором хранится количество нажатий на кнопку}
Label1.Caption:=inttostr(MyButton1.ClickCount);
end;
Лекция 16 тема: Инструментальные средства и методы расширения функциональности среды разработки.
Литература: 1. Создание собственных компонент: http://citforum.ru/programming/32less/index.shtml
2. Тейксейра Стив, Пачеко Ксавье. Borland Delphi 6. Руководство разработчика. : Пер. с англ. — М. : Издательский дом “Вильямс”, 2002.
3. Учебник по Delphi. Главы 7, 8. / wm-help.net - Электронная библиотека: http://wm-help.net/books-online/book/56472/56472-93.html
Открытость Delphi проявляется наиболее ярко в том, что наряду с расширяемостью Библиотеки Визуальных Компонент можно изменять саму среду программирования. Delphi предоставляет соответствующее API с тем, чтобы программисты могли расширять функциональность среды разработки. С помощью этого API можно создать свои собственные Эксперты (Experts), свою Систему Контроля Версий (Version Control system), Редакторы Компонент (Component Editors) и Редакторы Свойств (Property Editors).
В данной лекции мы рассмотрим Редакторы Свойств и Редакторы Компонент, необходимость в которых иногда возникает при написании новых объектов.
Редакторы свойств
Во время дизайна для настройки внешнего вида и поведения объекта используется Инспектор Объектов. Например, можно изменить цвет фона у объекта TLabel на форме. Для этого в Инспекторе Объектов выберем свойство Color. Справа от свойства есть маленькая стрелка, она означает, что мы можем выбрать цвет из списка (рис.1).
Рис. 1. Выбор цвета из списка.
Можно подумать, что этот список цветов является некоей функцией, жестко заданной разработчиками среды программирования Delphi. В действительности, для свойства Color используется соответствующий Редактор Свойств. Вы можете добавить свой собственный Редактор Свойств в среду разработки.
Стандартные Редакторы Свойств
Все свойства, даже простейшие, вроде Left или Caption, имеют свои редакторы. Причем, компоненты сами по себе даже не знают, что за редакторы используются для их свойств. Это означает, что Вы можете свой Редактор Свойств связать с уже существующими свойствами. Например, можно написать Редактор Свойств, который ограничивает свойство, имеющее целый тип (Integer), некоторым максимальным значением и затем связать этот редактор со свойством Width для всех существующих компонент.
Delphi определяет несколько редакторов свойств в модуле DesignEditors.pas, причем все они происходят от базового класса TPropertyEditor. Создаваемый редактор свойства также должен происходить от него или его потомков:
TPropertyEditor
TOrdinalProperty
TIntegerProperty
TColorProperty
TModalResultProperty
TTabOrderProperty
TCharProperty
TEnumProperty
TSetProperty
TShortCutProperty
TFloatProperty
TStringProperty
TComponentNameProperty
TFontNameProperty
TCaptionProperty
TSetElementProperty
TClassProperty
TFontProperty
TMethodProperty
TComponentProperty
Названия классов в большинстве своем очевидны. Класс TFloatProperty связан со свойствами, которые имеют тип Float, класс TSetProperty связан со свойствами, которые имеют тип Set. Некоторые редакторы имеют специальное назначение. Так, например, TTabOrderProperty нужен для того, чтобы предотвратить изменение свойства TabOrder (тип Integer) при выборе на форме нескольких компонент одновременно.
Класс TPropertyEditor
Прежде, чем писать свой собственный Редактор Свойств, нужно разобраться в базовом классе TPropertyEditor:
TPropertyEditor = class
private
FDesigner: TFormDesigner;
FPropList: PInstPropList;
FPropCount: Integer;
constructor Create(ADesigner: TFormDesigner; APropCount: Integer);
function GetPrivateDirectory: string;
procedure SetPropEntry(Index: Integer; AInstance: TComponent;
APropInfo: PPropInfo);
protected
function GetPropInfo: PPropInfo;
function GetFloatValue: Extended;
function GetFloatValueAt(Index: Integer): Extended;
function GetMethodValue: TMethod;
function GetMethodValueAt(Index: Integer): TMethod;
function GetOrdValue: Longint;
function GetOrdValueAt(Index: Integer): Longint;
function GetStrValue: string;
function GetStrValueAt(Index: Integer): string;
procedure Modified;
procedure SetFloatValue(Value: Extended);
procedure SetMethodValue(const Value: TMethod);
procedure SetOrdValue(Value: Longint);
procedure SetStrValue(const Value: string);
public
destructor Destroy; override;
procedure Activate; virtual;
function AllEqual: Boolean; virtual;
procedure Edit; virtual;
function GetAttributes: TPropertyAttributes; virtual;
function GetComponent(Index: Integer): TComponent;
function GetEditLimit: Integer; virtual;
function GetName: string; virtual;
procedure GetProperties(Proc: TGetPropEditProc);virtual;
function GetPropType: PTypeInfo;
function GetValue: string; virtual;
procedure GetValues(Proc: TGetStrProc); virtual;
procedure Initialize; virtual;
procedure SetValue(const Value: string); virtual;
property Designer: TFormDesigner read FDesigner;
property PrivateDirectory: string read GetPrivateDirectory;
property PropCount: Integer read FPropCount;
property Value: string read GetValue write SetValue;
end;
Методы, приведенные ниже, можно переопределять (override) для изменения поведения Редактора свойств. ( SetXxxValue используется для представления одного из методов SetFloatValue, SetMethodValue, SetOrdValue или SetStrValue. GetXxxValue обозначает GetFloatValue, GetMethodValue, GetOrdValue или GetStrValue)
Activate - вызывается, когда свойство выбирают в инспекторе объектов. Может быть полезно, позволяя некоторым атрибутам свойства определяться в каждый момент выбора этого свойства.
AllEqual - вызывается всякий раз, когда на форме выбирается более чем один объект. Если этот метод возвращает True, то вызывается GetValue, иначе в Инспекторе Объектов показывается пустая строка. AllEqual вызывается при условии, что GetAttributes возвращает paMultiSelect.
Edit - вызывается при нажатии кнопки «...» или по двойному щелчку мыши на свойстве. Этот метод может, к примеру, показать какое-нибудь диалоговое окно для редактирования свойства (пример - свойство Font).
GetAttributes - возвращает необходимую Инспектору Объектов информацию для того, чтобы тот смог отобразить свойство в подходящей манере. GetAttributes возвращает множество (set) значений типа TPropertyAttributes:
paValueList: Редактор Свойств может возвращать список значений для этого свойства. Если этот атрибут установлен, то нужно определить GetValues. В Инспекторе объектов справа от свойства появится кнопка для выпадающего списка.
paSortList: Инспектор объектов будет сортировать список, полученный от GetValues.
paSubProperties: свойство имеет подсвойства, которые будут показываться ниже в виде иерархии (outline). Если GetProperties будет генерировать объекты-свойства, то этот атрибут должен быть установлен.
paDialog: показывает, что метод Edit будет вызывать диалог. Если данный атрибут установлен, то появится кнопка «...» справа от свойства в Инспекторе Объектов.
paMultiSelect: позволяет свойству оставаться в Инспекторе Объектов, когда на форме выбрано сразу несколько объектов. Некоторые свойства не годятся для множественного выбора, например, Name.
paAutoUpdate: Если этот атрибут установлен, то метод SetValue будет вызываться при каждом изменении, произведенном в редакторе, а не после завершения редактирования (пример - свойство Caption).
paReadOnly: значение менять нельзя.
GetComponent - возвращает компонент под номером Index в случае множественного выбора объектов (multiselect). GetAttributes должен возвращать paMultiSelect.
GetEditLimit - возвращает число символов, которые пользователь может ввести при редактировании свойства. По умолчанию 255 символов.
GetName - возвращает имя свойства. По умолчанию это имя получается из информации о типе, все подчеркивания замещаются пробелами. Данный метод Вам нужно переопределять только в том случае, если имя свойства отличается от того, которое нужно отображать в Инспекторе Объектов.
GetProperties - должен быть переопределен для вызова PropertyProc для каждого подсвойства (или вложенного свойства) редактируемого свойства и передачи нового TPropertyEdtior для каждого подсвойства. По умолчанию, PropertyProc не вызывается и подсвойства не ожидаются. TClassProperty будет передавать новый редактор свойств для каждого свойства, объявленного published в классе. TSetProperty передает новый редактор для каждого элемента множества.
GetPropType - возвращает указатель на информацию о типе редактируемого свойства.
GetValue - возвращает значение свойства в виде строки. По умолчанию возвращает '(unknown)'. Этот метод нужно переопределять с тем, чтобы возвращать правильное значение.
GetValues - вызывается, если GetAttributes возвращает paValueList. Должно вызвать Proc для каждого значения, которое приемлемо для данного свойства.
Initialize - вызывается при создании Редактора свойств.
SetValue(Value) - вызывается для того, чтобы установить значение свойства. Редактор свойств должен уметь разобрать строку (Value) и вызвать метод SetXxxValue. Если строка имеет некорректный формат или неверное значение, то редактор Свойств должен сгенерировать исключительную ситуацию (exception), описывающую данную проблему. SetValue может вообще проигнорировать все изменения и оставить всю обработку изменений методу Edit (как в свойстве Picture).
Свойства и методы полезные при создании нового класса Редактора свойств:
PrivateDirectory (свойство) Это директория, в которой находится .EXE, либо рабочая директория, указанная в DELPHI.INI. Если редактор должен сохранить какую-то информацию (установки), то лучше в этой директории.
Value (свойство) Текущее значение свойства, то же самое возвращает GetValue.
Modified (метод) Вызывается для того, чтобы показать, что значение свойства изменилось. Методы SetXxxValue вызывают Modified автоматически.
GetXxxValue (метод) Возвращает значение первого из редактируемых свойств.
SetXxxValue (метод) Устанавливает значения свойства для всех выбранных объектов.
Создание Редактора Свойств
Редактировать свойства в окне Инспектора Объектов можно двумя способами. Один заключается в предоставлении пользователю возможности редактирования свойства как строки текста. Другой требует создания специального диалогового окна, в котором и выполняется редактирование свойства. В некоторых случаях потребуется использовать оба способа для редактирования одного свойства.
Вот основные этапы создания редактора свойств.
1. Создание объекта класса, производного от класса редактора свойств.
2. Редактирование свойства как текста.
3. Редактирование свойства в диалоговом окне (необязательный этап).
4. Определение атрибутов редактора свойств.
5. Регистрация редактора свойств.
При создании нового Редактора Свойств не нужно всегда переписывать его заново от базового класса TPropertyEditor. Достаточно выбрать в качестве предка уже существующий для данного свойства редактор и переопределить некоторые его методы.
Рассмотрим простейший пример нового Редактора Свойств. У всех видимых объектов есть свойство Hint - подсказка, появляющаяся во время выполнения программы, если задержать на некоторое время мышь на объекте. Это свойство имеет тип String и во время дизайна для его редактирования используется Редактор типа TStringProperty. Обычно, подсказка бывает однострочной, но в некоторых случаях ее нужно сделать многострочной. В принципе, здесь проблемы нет, достаточно во время выполнения программы присвоить свойству Hint нужное значение, например:
Button1.Hint:='Line1'#13#10'Line2';
Теперь подсказка будет состоять из двух строк. Но это достаточно неудобно, более удобно было бы формировать многострочную подсказку во время дизайна, однако редактор TStringProperty такой возможности не дает.
Создадим новый редактор, который мог бы это сделать. Для этого достаточно будет выбрать в качестве предка редактор TStringProperty и переписать некоторые методы. Во-первых, нужно переопределить метод Edit, в котором будет вызываться диалог для ввода строк подсказки. Во-вторых, нужно переопределить функцию GetAttributes, которая возвращает набор параметров, описывающих данное свойство. В частности, должен быть установлен атрибут paDialog, при этом в Инспекторе Объектов у свойства появится кнопка «…» для вызова диалога. И вообще-то нужно изменить метод GetValue, который используется для отображения значения свойства в Инспекторе Объектов.
Назовем новый Редактор Свойств THintProperty.
Объявление нового класса:
THintProperty = class(TStringProperty)
public
function GetAttributes: TPropertyAttributes; override;
function GetValue : String; override;
procedure Edit; override;
end;
Рассмотрим по порядку методы нового класса.
Функция GetAttributes добавляет к унаследованному множеству атрибуты paDialog (появляется кнопка «…») и paReadOnly (свойство нельзя редактировать непосредственно в Инспекторе Объектов, а только в диалоге, вызываемом через кнопку «…»):
function THintProperty.GetAttributes: TPropertyAttributes;
begin
Result := inherited GetAttributes + [paDialog, paReadOnly];
end;
Функция GetValue заменяет "неправильные" символы #10 и #13 (перевод каретки и переход на новую строку) на символ ">":
function THintProperty.GetValue : string;
var
i : Byte;
begin
result:=inherited GetValue;
for i:=1 to Byte(result[0]) do
if result[i]<#32 then result[i]:='>';
end;
Процедура Edit вызывает диалог для ввода строк подсказки. Диалог можно было бы нарисовать свой собственный, однако можно воспользоваться уже готовым. Несколько разных диалогов лежит в директории X:\DELPHI\SOURCE\LIB. Воспользуемся модулем STREDIT.PAS, в котором есть необходимый диалог редактирования строк:
procedure THintProperty.Edit;
var
HintEditDlg : TStrEditDlg;
s : string;
begin
HintEditDlg:=TStrEditDlg.Create(Application);
with HintEditDlg do
try
Memo.MaxLength := 254;
s:=GetStrValue+#0;
Memo.Lines.SetText(@s[1]);
UpdateStatus(nil);
ActiveControl := Memo;
if ShowModal = mrOk then begin
s:=StrPas(Memo.Lines.GetText);
if s[0]>#2 then Dec(Byte(s[0]),2);
SetStrValue(s);
end;
finally
Free;
end;
end;
Строка if s[0]>#2 then Dec(Byte(s[0]),2) нужна, так как Memo.Lines.GetText возвращает все строки с символами #13#10.
Регистрация Редактора Свойств
Новый Редактор Свойств готов, осталось только его зарегистрировать в среде Delphi. Для этого в интерфейсной части модуля с нашим редактором требуется поместить объявление процедуры Register, а в части implementation написать следующее:
procedure Register;
begin
RegisterPropertyEditor(TypeInfo(String), TControl, 'Hint', THintProperty);
end;
Как уже сообщалось выше, один и тот же редактор свойств можно "привязать" к свойствам, в зависимости от их названия или типа объекта. Это определяется параметрами (второй и третий), которые передаются во время регистрации в процедуре RegisterPropertyEditor. Возможны четыре варианта:
Класс компоненты |
Имя свойства |
Для каких свойств |
Nil |
'' |
совпадает тип свойства |
Nil |
'Name' |
Тип свойства + Имя свойства |
TClass |
'' |
Тип свойства + класс компоненты |
TClass |
'Name' |
Тип свойства + Имя свойства+ класс компоненты |
Если вы зарегистрировали Редактор и указали как класс компоненты, так и имя свойства, то данный редактор "привязывается" ко всем свойствам, которые:
имеют тип, указанный в первом параметре процедуры;
принадлежат компоненте, которая относится к классу (или его потомкам), указанному во втором параметре;
имеют имя, совпадающее с указанным в третьем параметре;
Если вместо типа класса в процедуре регистрации стоит Nil, а вместо имени свойства - пустая строка, то данный редактор "привязывается" ко всем свойствам, которые имеют тип, указанный в первом параметре, независимо от их имени или принадлежности к объекту какого-либо класса.
Если указан только класс, то редактор относится ко всем свойствам указанного типа для объектов указанного класса.
Если указано только имя, то редактор относится к свойствам указанного типа, которые имеют указанное имя.
В нашем случае Редактор Свойств зарегистрирован для всех свойств, которые имеют тип String, относятся к компоненте класса TControl или наследника от него и имеют имя 'Hint'.
Установка Редактора свойств
После того, как модуль с новым редактором свойств подготовлен, его нужно подключить к среде Delphi. Установка Редактора Свойств абсолютно аналогична установке новых объектов в палитру компонент и происходит следующим образом:
1. Выбрать пункт меню Component/Install Component.
2. Далее нужно в поле Unit file name указать модуль, содержащий процедуру регистрации и в поле Package file name указать имя пакета, в который устанавливается редактор свойств.
3. Нажать OK.
После успешной перекомпиляции библиотеки проверьте, как действует новый редактор свойств. Для этого создайте новый проект, положите на форму какой-либо видимый объект, например TButton, установите ShowHint для него в True, вызовите редактор подсказки (кнопка «…» в свойстве Hint), редактор выглядит примерно так:
В диалоге нажмите "OK" и запустите программу.
Примечание. Иногда в создании редактора свойства для специфического типа данных нет никакой необходимости. Например, типу диапазонов автоматически назначается необходимый редактор (скажем, набор значений 1..10 будет передан компоненту TIntegerProperty), для перечислимых типов автоматически используются раскрывающиеся списки и т.д. Предпочтительней применять определения типов, а не пользовательские редакторы свойств, так как они отлично поддерживаются и языком программирования во время компиляции, и используемыми по умолчанию стандартными редакторами свойств.
Редактор Компонент
Редактор Компонент во многом похож на Редактор свойств, отличия в том, что его используют для изменений скорее всего объекта, нежели отдельного свойства.
Класс TComponentEditor в модуле DSGNINTF.PAS выглядит следующим образом:
TComponentEditor = class
private
FComponent: TComponent;
FDesigner: TFormDesigner;
public
constructor Create(AComponent: TComponent;
ADesigner: TFormDesigner); virtual;
procedure Edit; virtual;
procedure ExecuteVerb(Index: Integer); virtual;
function GetVerb(Index: Integer): string; virtual;
function GetVerbCount: Integer; virtual;
procedure Copy; virtual;
property Component: TComponent read FComponent;
property Designer: TFormDesigner read FDesigner;
end;
Редактор Компонент создается для каждого выбранного объекта на форме, основываясь на классе объекта. При двойном щелчке на объекте вызывается метод Edit Редактора Компонент. При вызове контекстного меню (popup menu) правой кнопкой мыши, при построении этого меню вызываются методы GetVerbCount и GetVerb. Если в этом меню выбирается пункт, то вызывается метод ExecuteVerb. Copy вызывается при копировании компонента в Clipboard.
Редактор Компонент по умолчанию (TDefaultEditor) при двойном щелчке на объекте создает (или переходит на) в Редакторе Исходного Текста заготовку для событий OnCreate, OnChanged или OnClick (какое первым попадется).
При создании Редактора Компонент нужно переопределить либо метод Edit, либо три следующих метода: GetVerb, GetVerbCount и ExecuteVerb. Можно переопределять все четыре метода.
Если Редактор Компонент был вызван и изменил компонент, то всегда обязательно нужно вызывать метод Designer.Modified, чтобы Дизайнер об этом узнал.
Методы и свойства TComponentEditor:
Create(AComponent, ADesigner) - конструктор Редактора Компонент. AComponent - редактируемый компонент. ADesigner - интерфейс к Дизайнеру среды Delphi.
Edit - вызывается при двойном щелчке мышью на компоненте. Редактор Компонент может вызвать какой-нибудь диалог или эксперт.
ExecuteVerb(Index) - выполняется, когда был выбран пункт номер Index из контекстного меню. Считается, что Редактор Компонент знает, как проинтерпретировать это значение.
GetVerb(Index) - Редактор Компонент должен вернуть в этом методе строку, которая будет показана в виде пункта контекстного меню. Можно использовать обычные для пунктов меню символы, например &.
GetVerbCount - возвращает число, которое определяет на какие значения будут отвечать методы GetVerb и ExecuteVerb. Например, если это число равно 3, то в меню будет добавлено три пункта со значениями Index от 0 до 2.
Copy - вызывается, когда компонент нужно скопировать в Clipboard. На самом деле, образы полей компонента уже находятся в Clipboard. Просто предоставляется возможность скопировать различные типы форматов, которые игнорируются Дизайнером, но которые могут быть распознаны другими приложениями.
Пример Редактора Компонент
В качестве примера создадим Редактор Компонент для класса TButton. Этот Редактор будет показывать сообщение и изменять свойство Caption у объекта TButton. В данном примере это будет срабатывать и при двойном щелчке мыши, и через контекстное меню.
Объявление нового класса Редактора Компонент:
TButtonEditor = class(TComponentEditor)
private
procedure HiThere;
public
procedure Edit; override;
procedure ExecuteVerb(Index: Integer); override;
function GetVerb(Index: Integer): string; override;
function GetVerbCount: Integer; override;
end;
Процедура HiThere будет показывать сообщение и изменять свойство Caption:
procedure TButtonEditor.HiThere;
begin
MessageDlg('Hi! It replaces Default Component Editor.',
mtInformation, [mbOK], 0);
(Component as TButton).Caption:='Hi!';
Designer.Modified;
end;
Процедуры Edit и ExecuteVerb только вызывают HiThere:
procedure TButtonEditor.Edit;
begin
HiThere;
end;
procedure TButtonEditor.ExecuteVerb(Index: Integer);
begin
if Index = 0 then HiThere;
end;
Процедуры GetVerb и GetVerbCount определяют вид контекстного меню:
function TButtonEditor.GetVerb(Index: Integer): string;
begin
result:='&Get message ...'
end;
function TButtonEditor.GetVerbCount: Integer;
begin
result:=1;
end;
Здесь в контекстное меню добавляется один пункт "Get message :".
Редактор Компонент готов. Теперь необходимо зарегистрировать новый Редактор Компонент, это делается аналогично регистрации Редактора Свойств:
procedure Register;
begin
RegisterComponentEditor(TButton, TButtonEditor);
end;
После подключения нового Редактора Компонент в среду Delphi (пункт меню Component/Install Component), создайте новый проект, положите на форму объект TButton и щелкните дважды на нем - появится диалог:
После того, как Вы нажмете "OK", текст на кнопке изменится.
Созданный Редактор Компонент заместит Редактор по умолчанию для всех объектов класса TButton и его наследников, например, TBitBtn.
Лекция 17
ТЕМА: Отладка программ. Инструменты. Методика отладки.
Литература: 1. Культин Н. Б. Основы программирования в Delphi 7. – СПб.: БХВ-Петербург, 2010.
2. http://www.helloworld.ru/texts/comp/lang/delphi/delphi4/DebugerDelphi.htm
Конечная цель программиста заключается в написании правильно работающей программы, но, к сожалению, в 99 случаях из 100 первая попытка использования программы приводит к появлению предупреждения диалогового окна с кодом ошибки, неверного результата или в худшем случае к зависанию компьютера. Приблизительно так начинается отладка программы.
Определение 1. Отладка программы — это специальный этап в разработке программы, состоящий в выявлении и устранении программных ошибок, факт существования которых уже установлен.
Программные ошибки, как правило, делятся на три вида:
Синтаксическая ошибка. Неправильное употребление синтаксических конструкций, например употребление оператора цикла For без Tо.
Ошибка времени выполнения.
Алгоритмическая ошибка. Нарушение логики программы, приводящее к неверному результату. Это наиболее трудный для "отлова" тип ошибки. Такие ошибки, как правило, кроются в алгоритмах и требуют тщательного анализа и всестороннего тестирования.
Синтаксические ошибки, их также называют ошибками времени компиляции (Compile-time error), наиболее легко устранимы. Их обнаруживает компилятор, а программисту остается только внести изменения в текст программы и выполнить повторную компиляцию.
Ошибки времени выполнения, в Delphi они называются исключениями (exception), тоже, как правило, легко устранимы. Они обычно проявляются уже при первых запусках программы и во время тестирования. При возникновении ошибки в программе, запущенной из Delphi, среда разработки прерывает работу программы, о чем свидетельствует заключенное в скобки слово Stopped в заголовке главного окна Delphi, и на экране появляется диалоговое окно, которое содержит сообщение об ошибке и информацию о типе (классе) ошибки. Помимо этого, IDE размещает окно редактора поверх остальных и выделяет строку, вызвавшую исключительную ситуацию. На рис. 1 приведен пример сообщения об ошибке, возникающей при попытке деления на ноль.
Рис. 1. Сообщение об ошибке при запуске программы из Delphi.
После возникновения ошибки программист может либо прервать выполнение программы (для этого надо из меню Run выбрать команду Program Reset), либо продолжить ее выполнение, например, по шагам (для этого из меню Run надо выбрать команду Step), наблюдая результат выполнения каждой инструкции.
Если программа запушена из Windows, то при возникновении ошибки на экране также появляется сообщение об ошибке, но тип ошибки (исключения) в сообщении не указывается (рис. 2). После щелчка на кнопке ОК программа, в которой проявилась ошибка, продолжает (если сможет) работу.
Р
ис.
2. Сообщение об ошибке при запуске
программы из Windows.
С алгоритмическими ошибками дело обстоит иначе. Компиляция программы, в которой есть алгоритмическая ошибка, завершается успешно. При пробных запусках программа ведет себя нормально, однако при анализе результата выясняется, что он неверный. Для того чтобы устранить алгоритмическую ошибку, приходится анализировать алгоритм, вручную "прокручивать" его выполнение.
Отладка заключается в выполнении (пошаговом) программы и анализе её поведения, состояния вычислительных ресурсов и их изменений. В процессе отладки используются заранее определённые входные данные. В некоторых случаях производится принудительное изменение состояния вычислительных ресурсов и поведения программы. Процедура отладки может осуществляться с применением:
механизма исключений. Операционная система вносит ряд ограничений на допустимые действия, которые может выполнять программное обеспечение. Программа принудительно завершается или выдает некоторое сообщение при возникновении недопустимых событий (исключений);
журналирования (вывода) текущего состояния программного обеспечения. Обычно эта процедура производится с помощью расположенных в критических точках программы операторов вывода. Программа может самостоятельно завершиться при возникновении некоторых событий;
отладчиков специальных системных программ, которые позволяют пошагового выполнить программу и понаблюдать за изменением состояния вычислительных ресурсов.
Описанные выше способы отладки расположены в порядке увеличения функционала, доступного разработчику программного обеспечения. В случае отладки с применением механизма исключений возможно лишь удостовериться, что программа не выполняет никаких недопустимых действий. Проверить каким образом изменяются вычислительные ресурсы в процессе выполнения программы, и вмешаться в этот процесс невозможно. Во втором случае программное обеспечение самостоятельно сообщает, какие изменения им производятся, но вмешаться в сам процесс выполнения программы невозможно. При этом если в процессе отладки возникает необходимость получить дополнительные сведения об изменениях вычислительных ресурсов, то это становится возможным только после изменения исходного кода программы, её перекомпоновки и повторного запуска программы на выполнение. С применением отладчика доступен самый полный функционал – на любом этапе выполнения программы возможно посмотреть состояние всех вычислительных ресурсов и, при необходимости, изменить их или даже изменить саму исполняющуюся программу.
Трассировки с отслеживанием промежуточных значений переменных, пожалуй, самый распространенный способ отладки. Не нужно только забывать, что это только один из способов, и применять всегда и везде только его - часто невыгодно. Сложности возникают, когда приходится отслеживать слишком большие структуры данных или огромное их число. Еще проблематичнее трассировать проект, где выполнение каждой подпрограммы приводит к вызову пары десятков других. Но для небольших программ трассировки вполне достаточно.
Следует отметить, что с применением указанных выше процедур процесс отладки программного обеспечения может осуществляться либо поэтапно (т.е. каждая отдельная функционально завершенная его часть, например, процедура, функция, модуль и т.п., проверяется отдельно), либо единовременно. Очевидно, что в степень сложность отладки в первом случае может быть значительно меньше, чем во втором.
Нынешние среды разработки часто предлагают нам реагировать на возникающую проблему в диалоговом режиме. При этом можно:
просмотреть текущие значения переменных, состояние памяти, участок алгоритма, где произошел сбой;
прервать выполнение программы;
внести в программу изменения и повторно запустить ее (в компиляторных средах для этого потребуется перекомпилировать код, в интерпретаторных выполнение можно продолжить прямо с измененного оператора).
Работа с отладчиком в Delphi
Интегрированная среда разработки Delphi предоставляет программисту мощное средство поиска и устранения ошибок в программе отладчик. Отладчик позволяет выполнять трассировку программы, наблюдать значения переменных, контролировать выводимые программой данные.
Настройка ide для отладки
Для работы со встроенным отладчиком Delphi предлагает целую серию установок, большинство из которых вам лучше не трогать, а оставить, как есть (по умолчанию). Однако если вы все-таки решили изменить установки, выберите команду Tools/ Environment Options… и в появившемся диалоговом окне Environment Options щелкните на вкладке Preferences (рис 3).
Рис. 3. Использование вкладки Preferences для настройки интегрированного отладчика Delphi.
На этой странице имеет смысл изменять установку следующих опций из группы Compiling and running:
Hide Designers on Run - когда эта опция включена, окно Object Inspector и формы, использующиеся при разработке приложения, перед запуском программы на выполнение закрываются. Отключение опции позволяет запускать программу быстрее, но эффект перекрывается используемыми незакрытыми ресурсами приложения. Впрочем, будет ли выбрана эта опция, зависит от пользователя.
Minimize on Run - опция сворачивает окно IDE при запуске приложения. Подобно опции Hide Designers on Run, ее установка зависит исключительно от личных предпочтений программиста.
На странице Display диалогового окна Editor Properties есть еще одна установка - опция Visible Gutter. Она включает или отключает отображение серой вертикальной полосы, расположенной слева от окна редактирования (рис. 4), на которой мнемоническими значками отображается отладочная информация. Ширина этой полосы задается в поле Gutter Width. Окно Editor Properties вызывается командой меню Tools/ Editor Properties…
Р
ис.
4. Окно редактора с отладочными
значками.
В окне Debagger Options можно включать и отключать встроенный отладчик, устанавливая или убирая галочку около Integrated Debugging (рис 5). Окно Debagger Options вызывается командой меню Tools/ Debagger Options… Если вы отключите отладчик, отладочные команды в меню Run станут недоступными.
Рис. 5. Окно Debagger Options.
Включение в код отладочной информации
Перед началом отладки следует убедиться, что в приложение включена отладочная информация Delphi.
Для компиляции проекта с отладочной информацией следует выполнить команду Project/Options и в диалоговом окне Project Options выбрать вкладку Compiler (рис. 6).
Рис. 6. Вкладка Compiler диалогового окна Project Options
Включение отладочной информации регулируется следующими установками из группы Debugging:
Debug Information - опция контролирует включение отладочной информации. При отключении этой опции вы не сможете трассировать код или ставить точки прерывания в любом модуле. Опция эквивалентна директивам компилятора $D и $DEBUGINFO
Local Symbols - опция контролирует включение информации о локальных переменных, декларированных, например, внутри функций, процедур и раздела implementation. Вряд ли у вас возникнет необходимость в отключении этой опции, тем более что она игнорируется при выключенной предыдущей опции. Эквивалентные директивы компилятора— $L и $LOCALSYMBOLS.
Reference info - эту опцию нельзя целиком отнести к разряду отладочных, так как ее действие направлено на браузер объектов, а не на встроенный отладчик. Если опция включена, браузер объектов сможет выводить информацию для объектов, определенных в модулях. Опция игнорируется при выключенных предыдущих двух опциях. Эквивалентные директивы компилятора - $Y и $REFERENCEINFO.
Обычно опции Debug Information и Local Symbols включаются для пошаговой трассировки приложения. Однако можно отключить отладочную информацию для некоторых модулей (просто используя соответствующую директиву в начале модуля).
unit MyUnit;
{$D-}
interface
Использование директивы $D- автоматически отключает опции Local Symbols и Symbol Info, так что вам не надо отключать их отдельно.
Параметр Optimization группы Code generation влияет непосредственно на оптимизацию кода: при включенном параметре код будет сгенерирован максимально оптимальным способом с учетом как его размера, так и скорости исполнения. Это может привести к потере возможности доступа (даже на чтение) к некоторым локальным переменным, ибо из-за оптимизации кода они уже могут быть удалены из памяти в тот момент, когда программа остановилась в точке останова.
Также влияют на отладку параметры группы Runtime errors.
Параметр Range checking
Это один из наиболее востребованных параметров при отладке приложения. Он отвечает за проверку границ при доступе к массиву данных. В самом простом случае вам будет сгенерировано исключение при выполнении вот такого кода:
const
A: array [0..1] of Char = ('A', 'B');
procedure TForm1.FormCreate(Sender: TObject);
var I: Integer;
begin
for I := 0 to 100 do
Caption := Caption + A[I];
end;
Здесь мы просто пытаемся обратится к элементу массива, и в принципе, при отключенной опции «Range checking», если мы не выйдем за границу выделенной памяти, данный код нам грозит только тем, что в заголовке формы появится некая непонятная строка.
Что неприятно, но некритично для выполнения программы.
Гораздо хуже, если вы ошиблись с границами блока при попытке записи в него – в этом случае может произойти разрушение памяти приложения. Рассмотрим такой пример, оптимизацию отключим:
type
TMyEnum1 = (en1, en2, en3, en4, en5);
TMyEnum2 = en1..en3;
procedure TForm1.FormCreate(Sender: TObject);
var
I: TMyEnum1;
HazardVariable: Integer;
Buff: array [TMyEnum2] of Integer;
begin
HazardVariable := 100;
for I := Low(I) to High(I) do
Buff[I] := Integer(I);
ShowMessage(IntToStr(HazardVariable));
end;
После выполнения данного кода число HazardVariable будет равно не 100, а 4. При написании кода мы ошиблись при выборе типа итератора и вместо TMyEnum2 написали TMyEnum1, произошел выход за диапазон границ массива и затерлись данные на стеке, изменив значения локальных переменных хранящихся на нём же.
С
включенной оптимизацией ситуация будет
еще хуже. Мы получим следующую ошибку:
По данному описанию мы даже не сможем предположить, где именно произошло исключение, и почему оно произошло, так как адреса, упомянутые в тексте ошибки, не принадлежат памяти приложения, и если такая ошибка возникнет у клиента – исправить ее по данному описанию мы не сможем.
Поэтому отладка приложения всегда должна происходить с включенной настройкой Range checking.
Так же данный параметр контролирует выход за границы допустимых значений при изменении значения переменных. Например, будет поднято исключение при попытке присвоения отрицательного значения беззнаковым типам наподобие Cardinal/DWORD, или при попытке присвоить значение большее, чем может содержать переменная данного типа, например, при присвоении 500 переменной типа Byte и т. п…
Параметр «I/O cheking»
Отвечает за проверку результатов ввода/вывода при работе с файлами в стиле Pascal.
Если вы работаете с Append/Assign/Rewrite и т. п., то включайте данный параметр при отладке приложения.
Параметр «Overflow cheking»
Контролирует результаты арифметических действий и поднимает исключение в тех случаях, когда результат выходит за диапазон переменной.
Чтобы было проще понять различия между данным параметром и Range checking, рассмотрим следующий код:
procedure TForm1.FormCreate(Sender: TObject);
var
C: Cardinal;
B: Byte;
I: Integer;
begin
I := -1;
B := I;
C := I;
ShowMessage(IntToStr(C - B));
end;
Данный код не поднимет исключения при включенном параметре Overflow cheking. Хоть здесь и присваиваются переменным недопустимые значения, но не производится математических операций над ними. Однако исключение будет поднято при включенном параметре «Range checking».
А теперь рассмотрим второй вариант кода:
procedure TForm1.FormCreate(Sender: TObject);
var
C: Cardinal;
B: Byte;
begin
B := 255;
Inc(B);
C := 0;
C := C - 1;
ShowMessage(IntToStr(C - B));
end;
Здесь уже не будет реакции от параметра Range checking, но произойдет поднятие исключения EIntegerOverflow, за который отвечает Overflow cheking, на строчках Inc(B) и C := C - 1 из-за того, что результат арифметической операции не может быть сохранен в соответствующей переменной.
Таким образом, при работе с переменными оба параметра взаимодополняют друг друга. Overflow cheking не настолько критичен, как Range checking, но всё же желательно держать его включенным при отладке приложения.
Примечание. Если вы реализуете криптографические алгоритмы, то в них, как правило, операция переполнения является штатной. В таких ситуациях выносите код в отдельный модуль и в начале модуля прописывайте директиву {$OVERFLOWCHECKS OFF} для отключения проверки переполнений в текущем модуле.
Чтобы установленные на вкладке опции вступили в силу, нужно заново выполнить сборку проекта.
Трассировка программы
Во время работы программы ее инструкции выполняются одна за другой со скоростью работы процессора компьютера. При этом программист не может определить, какая инструкция выполняется в данный момент, и, следовательно, определить, соответствует ли реальный порядок выполнения инструкций разработанному им алгоритму.
В случае неправильной работы программы необходимо видеть реальный порядок выполнения инструкций. Это можно сделать, выполнив трассировку программы.
Определение 2. Трассировка это процесс выполнения программы по шагам (step-by-step), инструкция за инструкцией. Во время трассировки программист дает команду: выполнить очередную инструкцию программы.
После успешной компиляции модуля на полосе отладочной информации каждая строка кода, внесшая свой вклад в модуль, будет отмечена маленьким, синим кружком. Если же строка не помечена, значит, здесь поработал оптимизатор. Поскольку для таких строк выполняемый код не сгенерирован эти строки не будут помечены точкой выполнения.
Интегрированная среда Delphi предоставляет пользователю несколько команд пошаговой отладки доступных в меню Run:
Run (F9) - выбор этой команды запускает приложение на выполнение в обычном режиме. Можно использовать ее как для запуска приложения, так и для продолжения его работы после какого-либо прерывания выполнения (например, по точке останова). Если включена опция Break on Exception, можно использовать команду для продолжения работы после получения сообщения об исключительной ситуации.
Step Over (F8) - когда точка выполнения находится на строке содержащей вызов процедуры или функции, используйте эту команду для выполнения строки, включая вызовы в один шаг, без прохождения отдельных строк вызываемых функций. Точка выполнения перемещается при выполнении на следующую строку.
Trace Into (F7) - в отличие от предыдущей команды, эта опция отработает пошаговую отладку вызываемых процедур и функций. Другими словами, если, например, в строке вызывается некая процедура, то при выполнении этой команды точка выполнения перейдет на первую строку процедуры. Однако если в строке нет таких вызовов, значит, последние две команды идентичны. Примечание. Будьте осторожны при пошаговой трассировке обработчика события OnPaint. Поскольку при пошаговой отладке окно редактора размещается поверх других окон, требуется перерисовка окна приложения, для чего вызывается обработчик события OnPaint... Вы попадаете в замкнутый круг, точнее— в бесконечный цикл вызовов одного и того же обработчика. Тем не менее, стоит лишь проследить, чтобы окна приложения и редактора не перекрывались, и проблема разрешится сама собой.
Trace to Next Source Line (Shift+F7) - иногда ваш код вызывает другой код косвенно, например, при вызове функции, которая запускает обработчик события, или при вызове функции Windows API, которая, в свою очередь, запускает функцию косвенного вызова. Поскольку такие вызовы косвенные, отладчик не видит вызова и не отслеживает пошагового выполнения таких вызовов. Однако использование описываемой команды приводит к отслеживанию таких вызовов и останову отладчика на первой строке вызываемой таким образом функции или процедуры.
Run to Cursor (F4) - зачастую вам вовсе не хочется в поисках ошибки, местоположение которой с какой-то точностью вам известно, пошагово добираться до нужного места через сотни, а то и тысячи строк кода. В таком случае просто поместите курсор на нужную вам строку программы в окне редактирования и используйте команду Run to Cursor. Эти действия эквивалентны временному помещению точки останова в необходимую вам строку программы, и после выполнения предшествующего строке кода работа программы приостанавливается. Если вы пытаетесь выполнить программу до позиции курсора, который находится в строке, не содержащей отладочной информации, вы получите сообщение об ошибке, показанное на рис. 7.
Рис. 7. Это сообщение о том, что вы пытаетесь остановить выполнение программа на строке, не содержащей отладочной информации.
Show Execution Point - эта команда заставляет среду разработки открыть окно редактора и показать выполняемую в настоящее время строку программы. Она полезна в случаях, когда вы, например, закрыли или свернули окно редактора во время отладки (обычно при нормальном состоянии окна отладчик делает это автоматически).
Program Pause - выбор этой команды немедленно останавливает выполнение программы. Она особенно полезна при зацикливании программы.
Program Reset (Ctrl+F2) - если вы достаточно "наотлаживались" и хотите завершить работу своей программы или запустить ее заново, используйте эту команду. Она немедленно прекратит выполнение программы и вернет вас в среду разработчика.
Для того чтобы начать трассировку, необходимо из меню Run выбрать команду Step over или Trace into. В результате в окне редактора кода будет выделена первая инструкция программы. Слева от нее будет располагаться зеленая стрелка. Для того чтобы выполнить выделенную инструкцию, необходимо из меню Run выбрать команду Step over (нажать клавишу F8) или Trace into (нажать клавишу F7). После выполнения инструкции будет выделена следующая. Таким образом, выбирая нужную команду из меню Run, можно выполнить трассировку программы. В любой момент времени можно завершить трассировку и продолжить выполнение программы в реальном темпе. Для этого надо из меню Run выбрать команду Run.
Во время трассировки можно наблюдать не только порядок выполнения инструкций программы, но и значения переменных.
Точки останова программы
При отладке широко используется метод, который называют методом точек останова. Суть метода заключается в том, что программист помечает некоторые инструкции программы (ставит точки останова), при достижении которых программа приостанавливает свою работу, и программист может начать трассировку или проконтролировать значения переменных.
Добавление точки останова
Для того чтобы поставить в программу точку останова (breakpoint), нужно из меню Run выбрать команду Add Breakpoint (Добавить точку останова), затем из меню следующего уровня команду Source Breakpoint. В результате открывается диалоговое окно Add Source Breakpoint (рис. 8), в котором выводится информация о добавляемой точке останова. Поле Filename содержит имя файла программы, куда добавляется точка останова, поле Line number номер строки программы, в которую добавляется точка останова. После щелчка на кнопке ОК точка останова добавляется в программу, в Редакторе кода строка, в которой находится точка останова, помечается слева красной точкой и выделяется цветом (рис. 9).
Рис. 8 Диалоговое окно Add Source Breakpoint.
Т
очку
останова можно добавить, щелкнув мышью
на синей точке, помечающей ту инструкцию
программы, перед которой надо поместить
точку останова (если в программе нет
ошибок, то компилятор помечает выполняемые
инструкции программы синими точками).
Рис. 9 Окно редактора кода после добавления точки останова.
Для точки останова можно задать логическое условие, при выполнении которого (равенстве TRUE) программа приостановит свою работу в данной точке (например, если значение переменной равно определенной величине). Условие (логическое выражение) вводится в поле Condition диалогового окна Add Source Breakpoint. Если условие будет равно FALSE, то в данной точке программа останавливаться не будет.
Кроме условия для точки останова можно задать количество пропусков данной точки. Если во время добавления в программу точки останова в поле Pass count (Число пропусков) диалогового окна Add Source Breakpoint записать отличное от нуля число, то программа приостановит свою работу в этой точке только после того, как инструкция, находящаяся в строке, помеченной точкой останова, будет выполнена указанное число раз. При каждом проходе через точку останова отладчик уменьшает значение счетчика на единицу и по достижении нулевого значения программа приостанавливается. Такой метод полезен при работе с циклами, особенно если вы знаете, что ошибка происходит после определенного количества циклов.
Если параметр Pass count используется совместно с указанием условия в параметре Condition, то он указывает, сколько таких условий нужно пропустить, прежде чем точка останова будет активирована, причем подсчёт количества срабатываний ведется от самого первого, с учетом значения параметра Condition.
Изменение характеристик точки останова
Программист может изменить характеристики точки останова. Для этого надо из меню View выбрать команду Debug Windows, затем из меню следующего уровня команду Breakpoints. В открывшемся диалоговом окне Breakpoint List (рис. 10) нужно щелкнуть правой кнопкой мыши в строке, содержащей информацию о нужной точке останова, и в появившемся контекстном меню выбрать команду Properties. В результате открывается диалоговое окно Source Breakpoint Properties, в котором можно изменить характеристики точки останова, например, изменить условие (содержимое поля Condition) остановки программы в данной точке. Используя это же контекстное меню, можно быстро перейти к инструкции, в которой находится точка останова. Для этого надо выбрать команду Edit Source.
Рис. 10. Окно Breakpoint List.
Р
ис.
11. Отображение неактивной точки
останова.
Удаление точки останова
Для того чтобы удалить точку останова, нужно в диалоговом окне Breakpoint List щелкнуть правой кнопкой мыши в строке, содержащей информацию о точке, которую надо удалить, и в появившемся контекстном меню выбрать команду Delete. Чтобы отключить точку останова нужно выбрать в контекстном меню команду Enabled (после этого исчезнет галочка), повторный выбор команды Enabled сделает точку останова опять активной (около Enabled появится галочка). Неактивная точка останова помечается серой точкой и выделяется зеленой полосой (рис 11). Можно также в окне Редактора кода щелкнуть мышью на красной точке, помечающей строку, в которой находится точка останова.
Примечание. После щелчка правой кнопкой мыши в окне Breakpoint List при невыбранной точке останова выводится контекстное меню, в котором команда Add служит для добавления новой точки, Delete All удаляет все точки останова, а команды Disable All и Enable All отключают или включают все точки останова в списке.
Наблюдение значений переменных
Для того чтобы во время выполнения программы по шагам иметь возможность контролировать значение переменной, нужно добавить имя этой переменной в список наблюдаемых элементов (Watch List). Для этого надо из меню Run выбрать команду Add Watch (Добавить наблюдаемый элемент). После этого появится диалоговое окно Watch Properties (рис. 12).
В поле Expression появившегося диалогового окна Watch Properties нужно ввести имя переменной. Можно просматривать значения не только переменных, но и выражений типа х*(y+z). Единственное ограничение - выражение не может содержать вызовов функций. Допускается также просмотр значений записей, массивов и других структурированных элементов.
Р
ис.
12. Добавление имени переменной в список
Watch List.
Поле Repeat Count используется в том случае, если нужно просмотреть часть большого массива. Предположим, что вам надо знать значения элементов 826-833 следующего массива
Var
BigArray: array[1..1000] of Integer;
Тогда в поле Expression указываем значение BigArray [826] и устанавливаем параметр Repeat Count равным 8. При этом вам будут показаны значения восьми элементов массива— от 826 по 833.
Использование поля Digits позволяет определить количество значащих цифр при выводе числа с плавающей точкой.
Отключенная опция Enabled предотвращает вывод значения переменной, однако, среда будет хранить все параметры, заданные для ее просмотра. Так можно временно убрать с экрана информацию о переменной, которая в настоящий момент вам не нужна, и быстро восстановить ее на экране при необходимости без повторного ввода характеристик просмотра.
В диалоговом окне имеется также набор переключателей для выбора способа представления переменной. Значение Default позволяет среде разобраться с типом переменной самостоятельно. Как правило, такое решение оптимально, однако в любой момент вы можете представить переменную как переменную того типа, который вас интересует. Опция Memory dump представляет информацию как набор байтов, что бывает полезно, когда необходимо увидеть внутреннее представление информации в переменной.
Рис. 13. Результат добавления имени переменной в список Watch List.
После нажатия на кнопку ОК в окне Watch Properties в список Watch List, содержимое которого отражается в диалоговом окне Watch List (рис. 13), будет добавлен новый элемент. Так как переменные программы существуют (и, следовательно, доступны) только во время выполнения программы, то после имени переменной выводится сообщение: process not accessible (процесс недоступен). Также может быть выведено одно из следующих сообщений:
Variable 'X' inaccessible here due to optimization (Переменная 'X' недоступна из-за оптимизации). В этой точке значение переменной просмотреть невозможно (иногда это можно сделать в другом месте программы), так как для нее не выделена память из-за оптимизации программы компилятором.
Symbol was eliminated by linker (Переменная удалена компоновщиком). Переменная удалена из кода программы компоновщиком, так как на нее нет ни одной ссылки в тексте программы.
Отладчик также выводит сообщение об ошибке, если имя переменной написано неправильно, например вы запросили значение Foo [5], в то время как переменная Foo массивом не является.
В качестве примера на рис. 14 приведен вид окна редактора кода и окна Watch List во время пошагового выполнения программы.
Р
ис.
14. Контроль значений переменных во время
пошагового выполнения программы.
В окне Редактора кода стрелкой помечена инструкция, которая будет выполнена на следующем шаге выполнения программы (при нажатии клавиши F8 или при выборе команды Step Over из меню Run), в диалоговом окне Watch List выведены значения переменных.
Существует еще один способ, позволяющий проверить значение переменной, не добавляя ее имя в список Watch List. Заключается он в следующем. После того как программа достигнет точки останова, в результате чего откроется окно редактора кода, нужно установить курсор мыши на имени переменной, значение которой надо проверить. В окне редактора кода появится окно подсказки, в котором будет выведено значение переменной (рис. 15).
Рис. 15. Контроль значения переменной без добавления имени в список Watch List.
Диалоговое окно Evaluate/Modify
Для просмотра значения переменной можно использовать диалоговое окно Evaluate/Modify, позволяющее работать только с одной переменной (в нем можно не только просмотреть, но и изменить ее содержимое).
Для вывода диалогового окна Evaluate/Modify нужно выбрать команду Run/Evaluate/Modify (рис 16). Другой способ вызова диалогового окна - установить курсор в окне редактирования на необходимой переменной, щелкнуть правой кнопкой мыши и в контекстном меню выбрать команду Debug\Evaluate/Modify.
Далее в поле Expression нужно ввести выражение и щелкнуть на кнопке Evaluate для того, чтобы увидеть результат в поле Result. Если выражение состоит только из имени простой переменной (не массива или структуры!), можете ввести новое значение для переменной в поле New Value и, щелкнув на кнопке Modify, присвоить переменной новое значение. Это позволяет корректировать значения переменных в процессе отладки, не останавливаясь и не перекомпилируя всю программу.
Р
ис
16. Использование диалогового окна
Evaluate/Modify для проверки изменения отдельной
переменной.
Диалоговое окно Evaluate/Modify - немодальное, т. е. можно не закрывать его, продолжая отладку. Однако в отличие от окна Watch List диалоговое окно Evaluate/Modify не отслеживает изменения значений переменных и для получения информации о текущем состоянии переменной необходимо пользоваться кнопкой Evaluate.
Так же, как и окно Watch List, диалоговое окно Evaluate/Modify может выводить сообщения об ошибках, если отладчик не в состоянии вывести информацию о переменных. И точно так же не выводится информация о выражениях, содержащих вызов функций.
Отладка dll
До версии Delphi 4 для отладки библиотек динамической компоновки требовался внешний отладчик (Turbo Debugger for Windows). Delphi 4 внесла возможность отладки DLL в список своих возможностей. Windows не может загрузить DLL без предварительной загрузки использующего ее ЕХЕ, поэтому придется набросать простенькую программу, использующую нужную DLL. Затем в главном меню нужно выбрать команду Run/Parameters для вывода диалогового окна Run Parameters. Если текущий проект - DLL (DPR-файл начинается ключевым словом library, а не program), то поле Host Application будет доступно, и в нем надо либо ввести имя использующей DLL программы, либо выбрать его с помощью кнопки Browse. После выбора приложения запуск и отладка DLL становятся обычной рутинной работой со всеми возможностями, используемыми при отладке программ, - установкой точек останова, просмотром значений переменных и т.д.
Точно так же можно отлаживать и свои компоненты ActiveX, и объекты автоматизации OLE.
Окно cpu (дизассемблер)
Окно CPU предоставляет возможность увидеть работу приложения на уровне языка ассемблера. Эффективное его использование предполагает знание ассемблера Intel x86 и архитектуры процессора.
Окно CPU требуется крайне редко, в безвыходных положениях, когда обычная трассировка кода не позволяет найти, понять и исправить ошибки. Только в таких случаях окно CPU и проход по ассемблерным инструкциям может приподнять завесу над причиной возникновения ошибок.
Для отображения данного окна нужно выбрать пункт меню View/Debug Windows/CPU.
Окно CPU содержит следующие панели:
Code pane - панель кода представляет дизассемблированный код в окрестности текущей точки выполнения (если вы не отлаживаете приложение, окно будет полупустым). Кроме того, панель показывает исходный текст строк, соответствующих выполняемому коду. В окне редактирования точка выполнения индицируется маленьким зеленым значком. При пошаговом проходе значок точки выполнения синхронно перемещается по окну CPU и окну редактирования.
Register pane в панели регистров отображается содержимое 16 регистров процессора. Значения регистров, изменившиеся в результате выполнения последней операции, выделены красным цветом.
Flags pane - панель флагов показывает состояние 14 флагов процессора. Установленный флаг представляется значением 1, сброшенный флаг значением 0. В зависимости от процессора некоторые флаги могут быть недоступными.
Stack pane - панель стека показывает содержимое стека приложения. Вы можете изменять представление содержимого стека с помощью контекстного меню.
Data pane - по умолчанию в панели данных выводится содержимое глобального сегмента данных приложения. Ее вид можно изменить так же, как и вид панели стека.
Каждая из панелей в окне CPU имеет собственное контекстное меню.
Окно состояния подзадач.
Окна Thread Status, Modules и Call Stack предоставляют дополнительную информацию, которая может быть полезна при отладке приложения.
В окне Thread Status перечислены все активные подзадачи текущего процесса. Для просмотра состояния подзадач нужно выбрать команду View/Debug Windows/Threads, и на экране появится окно Thread Status (рис. 17).
Р
ис.
17. Использование окна Thread Status для
просмотра атрибутов подзадач в приложении.
В четырех колонках окна представлена следующая информация:
Thread ID - уникальный идентификатор подзадачи, присвоенный ей операционной системой.
State - состояние подзадачи, обычно - Running или Stopped. Если приложение запущено, но ожидает ввода от пользователя, состояние выводится как Runnable.
Status - статус подзадачи может иметь одно из четырех значений. Breakpoint означает, что поток остановлен в точке останова. Stepped - подзадача находится в режиме пошагового выполнения. Faulted - остановка подзадачи из-за исключительной ситуации и Unknown - статус неизвестен.
Location - в этой колонке выводится строка исходного кода, соответствующего текущей точке выполнения подзадачи. Если отладчик не в состоянии определить строку исходного текста, выводится 32-битовый адрес точки выполнения.
Если в приложении разработано несколько подзадач, и нужно отладить одну из подзадач, можно сделать ее основной с помощью окна Thread Status. Для этого нужно выбрать подзадачу, которая должна быть текущей, щелкнуть на ней правой кнопкой мыши и выбрать из контекстного меню команду Make Current. При этом фокус выполнения будет передан выбранной подзадаче, и можно будет отлаживать ее как основную задачу.
В контекстном меню окна Thread Status содержатся две команды - View Source и Go to Source. Они могут пригодиться для того, чтобы проследить за точкой выполнения другой подзадачи без передачи ей фокуса.
Окно Modules
В
окне Modules отображаются все модули
(ЕХЕ-файл вашего приложения и все
используемые динамические библиотеки),
которые располагаются в адресном
пространстве приложения. В него входят
непосредственно подключенные DLL и
библиотеки, подключенные через другие
библиотеки, а также библиотеки, загруженные
операционной системой. Чтобы увидеть
это окно, нужно выбрать команду View/
Debug Windows
/Modules (рис. 18). В окне информация выводится
в трех столбцах Name (имя модуля),
Address (адрес начала кода модуля) и
Path (полный путь каталога, из которого
был загружен модуль). Информация о
каталоге может быть важна, если возможна
загрузка модуля не из того каталога, из
которого ожидалась, например, из более
старой версии. Информация об адресе
обычно используется при отладке в окне
CPU.
Рис. 18. Использование окна Modules для вывода списка модулей, используемых приложением.
Окно Call Stack
В этом окне представлен список всех функций и процедур, вызванных к моменту достижения точки выполнения и работа которых приостановлена. Для открытия этого окна используется команда View/ Debug Windows /Call Stack (рис. 19).
Рис. 19. Использование окна Call Stack для определения всех вызванных функций и процедур.
В верхней строке окна выводится имя текущей процедуры. В следующей строке указывается имя процедуры, вызвавшей данную процедуру (Ouch) и т.д. Это окно может быть очень полезным, когда надо определить, каким путем была достигнута точка останова.
Имея на руках данный список, мы можем быстро переключаться между вызовами двойным кликом (или через контекстное меню View Source). В большинстве случаев данное окно позволяет достаточно быстро локализовать место возникновения ошибки. Например, вот так будет выглядеть стек вызовов при возникновении ошибки EAbstractError:
В данном случае достаточно найти самый первый сверху вызов, код которого расположен не в системных модулях Delphi, чтобы с большой долей вероятности сказать, что ошибка именно в нём. Таким вызовом является Unit1.TForm1.Button1Click() – это обработчик кнопки Button1.
Трассировка исходного кода vcl
В поставку Delphi в версиях Professional, Enterprise и Architect входят исходные тексты VCL (Visual Component Library). В поставку модули компонентов VCL входят скомпилированными без отладочной информации, что означает, что при отладке вы не сможете пройти код пошагово. Нет особой необходимости трассировать код VCL, но если вы хотите убедиться, что ошибка не в VCL, или посмотреть, как работает функция, придется перекомпилировать модули, которые нужно трассировать, с отладочной информацией.
Примечание. Некоторые стандартные модули VCL требуют компиляции с отключенной опцией Overflow Checking для корректной работы. Поэтому при перекомпиляции убедитесь, что эта опция компилятора отключена.
.
