Инструменты, алгоритмы и структуры данных

Инструментарий

Показывать лекцию целиком

4.1. Текстовые редакторы на этапах проектирования и программирования

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

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

  • Хороший общецелевой редактор предлагает много усовершенствованных свойств, например, способ выполнения сложных изменений или командный язык для обработки документов специальным образом. Но такие редакторы не "заточены" под программирование и не имеют некоторых характерных свойств, важных для программ. В частности, они не знают, что программы обрабатываются компилятором и выполняются под управлением отладчика.
  • Специализированный редактор программ может получать преимущества, зная специфику языка, например, он может выполнять синтаксический анализ текста непосредственно в момент ввода этого текста. Он может иметь кнопку запуска компиляции и иметь другие способы прямого взаимодействия с различными средствами разработки. Но в отношении свойств, не связанных со спецификой языка, он может быть менее продвинутым, чем общецелевой редактор текста.
  • Технические рассмотрения не являются строго определяющими. Текстовые редакторы, такие как Vi или Emacs, завоевали много сторонников, и люди не хотят отказываться от привычных средств работы с текстами только потому, что данные тексты являются программами.

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

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

    (рис 4.1) Текстовый облик (вверху) и построение диаграмм в EiffelStudio Как пример, EiffelStudio предлагает встроенный редактор для текстов классов. Работа стала бы проще, если бы можно было предположить, что это единственный способ для пользователей ввести текст класса, – было бы легче сохранять историю изменений, облегчался бы процесс перекомпиляции, описанный ниже. Но необходимо учитывать, что пользователи могут использовать другие текстовые редакторы. В этом случае компилятор должен анализировать файлы и их метки времени (время последней модификации), чтобы знать, что следует перекомпилировать.

    Помимо чисто текстуальных средств часто может быть удобным применять графику для представления программных текстов, такую как диаграммы, используемые в этом курсе для описания архитектуры ПО в виде множества классов со связями, которые отражают отношения наследования и "клиент-поставщик". Задействованная нотация называется BON-нотацией (Business Object Notation). Другой, более сложной нотацией, также широко используемой, является UML (Unified Modeling Language) – унифицированный язык моделирования. Графический инструментарий, поддерживая эти нотации, часто позволяет ввести диаграмму в интерактивном режиме, затем автоматически сгенерировать программный текст или его шаблон, отражающий структуру, которая задана диаграммой. Такие инструменты часто относят к CASE- средствам (Computer-Aided Software Engineering) – термин, который в буквальном смысле относится ко всем рассматриваемым в этой лекции средствам, но обычно применяется в более ограниченном смысле. Хорошо известным инструментом, поддерживающим UML, является система Rose от Rational Software. EiffelStudio включает инструмент Diagram Tool, позволяющий отображать диаграммы классов и кластеров:

    Такой инструментарий должен удовлетворять требованию "обращения цикла" – гарантировать, что преобразование графики в текст и обратно, выполняемое в любом порядке, возвращает оригинал. Для многих людей графические облики дают более точное понимание общей структуры. Но когда дело доходит до точной семантики, нет ничего лучшего, чем текст. Обращение цикла гарантирует согласованность этих двух точек зрения. Инструментарий позволяет вам свободно переходить от графического образа к тексту и обратно, изменяя по желанию либо картинку, либо текст, сохраняя согласованность представлений. Этот принцип соблюдается при работе с инструментом Diagram Tool.

    4.2. Управление конфигурацией

    Программная система имеет тенденцию изменяться с течением времени. Сложное ПО состоит из многих частей и разрабатывается многими людьми.

    Если рассматривать эти три характеристики совместно, то можно увидеть серьезные проблемы:

    (рис 4.2) Три измерения управления конфигурацией ПО

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

    Разнообразие в управлении конфигурацией

    Источником, который управлению конфигурацией и дал это имя, является задача по комбинированию или конфигурированию отдельных частей ПО в единую систему. Для простоты рассмотрим два измерения – "Части" и "Изменения". В случае программ частями являются модули, такие как методы, классы и кластеры. Каждый модуль проходит через последовательные версии, в соответствии со своим собственным расписанием и ограничениями; по аналогии с музыкальным произведением, это скорее контрапункт, чем гармония, скорее фуга Баха, чем военный марш. Но системе в целом нужно развиваться в своем ритме, имея собственную историю. Приходится отдельные куски склеивать вместе в их текущем состоянии и выпускать очередной релиз системы. На жаргоне скомпонованная система называется "билд", или сборка (build), и это источник, угрожающий бедствиями.

    Так просто использовать версию 3.1 модуля А и версию 2.5 модуля В, в то время как версия модуля А прошла сертификацию на совместную работу с версией 2.4 модуля В. Многие катастрофические ошибки в работе ПО возникали по вине этой, казалось бы, тривиальной ошибки управления сборкой. Ошибки перестают быть тривиальными с ростом размера системы и времени ее существования.

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

  • Когда модуль был последний раз модифицирован?
  • Кто модифицировал модуль в период между сентябрем и декабрем прошлого года?
  • Когда была выпущена версия n?
  • Что изменилось в версии n +1 в сравнении с версией n?
  • Что послужило причиной данного изменения?
  • Эта ошибка все еще существует в текущей версии?
  • Если нет, то когда она была исправлена?
  • Можем ли мы вернуться к версии модуля М от 15 марта этого года?
  • Данный аспект управления конфигурацией называют контролем версий.

    Большинство широко распространенных средств управления конфигурацией занимаются именно этими двумя вопросами – автоматической сборкой и контролем версий. Мы рассмотрим оба эти аспекта, а в следующем разделе обсудим проект глобального хранилища (репозитория), расширяющий управление конфигурацией до поддержки общей инфраструктуры проекта.

    Инструменты сборки: от Make до автоматического анализа зависимостей

    Источником вдохновения для построения инструментария сборки послужила команда Unix – Make, разработанная Стюартом Фельдманом в 1977 году.

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

    target: source1, source2 …
        command1
    …
            
    (рис 4.3) Стюарт Фельдман (2006)

    Это описание говорит, что цель target включает источники source, которые обычно задаются файлами. Всякий раз, когда один из источников изменяется, цель должна быть перестроена, для получения цели из источников нужно выполнить команду или группу команд command, указанную в файле сборки.

    Выполнение

    make target
            

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

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

    Типичный файл сборки для построения программы, написанной на С, имеет вид:

    program: main.o module1.o module2.o
        cc main.o module1.o module2.o
    %.c: %.o
        cc $<
            

    Для программ на С принято сохранять их в файлах с расширением .c, например name.c. Компилятор – команда cc – генерирует по исходному коду объектный код, записывая его в файл с расширением .o (name.o). Команда cc выполняет двойную работу: будучи линкером, она применяется к одному или нескольким объектным файлам, создавая новый модуль с удаленными перекрестными ссылками. В приведенном файле сборки указано, что наша программа program должна быть собрана из трех объектных модулей (main – это главный программный модуль); для генерирования программы нужно применить к ним компилятор cc. Для описания того, как из исходного модуля получать объектный, можно было бы использовать три зависимости в форме

    main.o: main.c
        cc main.c
            

    Аналогично можно было бы записать зависимости для module1 и module2. В файле сборки все три зависимости объединены в одно общее правило, применимое к любому -файлу; символ % является держателем места – вместо него может быть подставлено любое имя. Команда в строчке $< означает, что она должна быть применена к результату, полученному в предыдущей строке файла сборки (конечно, нотация не слишком прозрачная, но это простительно, особенно если учесть, что с 1977 года Make и его потомки помогли миллионам программистам правильно конфигурировать свои программы).

    Команда make program будет делать то, что и ожидалось: вначале скомпилируются три исходных модуля и будут созданы соответствующие -файлы; затем они будут скомпонованы (снова той же командой cc), создав в результате program. Заметьте, Make автоматически определяет порядок этих операций, анализируя зависимости файла сборки.

    Эти концепции применимы не только к программированию. Например, файл сборки можно было бы применить для генерации документации, где зависимости включали бы исходные файлы для документов в разных форматах (Microsoft Word, Open Office, TeX, Frame Maker), а команды позволяли бы преобразовывать эти документы в единый формат HTML или PDF.

    Без всякой посторонней помощи Make устанавливает дисциплину управления сборкой. Он является примером успешного решения инженерных задач. Его главным ограничением является необходимость явного задания зависимостей. При изменении модулей могут меняться и зависимости, поэтому постоянно нужно быть уверенным, что файл сборки был корректно обновлен. Будучи программным продуктом, он должен проектироваться и сопровождаться так же, как и остальные компоненты ПО. Возможны некоторые его улучшения, подобные рассмотренным параметризованным правилам, но процесс формирования файла остается утомительным.

    Более современный подход состоит в том, чтобы поставлять документируемую программу, содержащую всю необходимую информацию для автоматического построения зависимостей. Это то, что делает, в частности, EiffelStudio, – здесь нет аналога файла сборки, поскольку компилятор сам может определить, когда метод или класс был изменен, какие классы от него зависят и, следовательно, должны быть перекомпилированы.

    Контроль версий

    Инструменты, отвечающие за контроль версий, помогают сохранить трассировку успешных версий отдельного модуля. В нашей трехмерной картинке контроль версий соответствует горизонтальной плоскости, а в частном случае одного разработчика – горизонтальной оси координат.

    (рис 4.4) Три измерения управления конфигурацией ПО

    Части (модули в случае программ) подвергаются последовательным изменениям. Мы не можем позволить разработчикам редактировать их по своему усмотрению, а затем перекомпилировать всю систему. Это привело бы к хаосу. Вместо этого каждое изменение должно быть записано – кто, когда, что и почему. Необходимо уметь сравнивать последовательные версии и уметь откатываться к предыдущей версии.

    Существует достаточно много средств контроля версий, коммерческих и свободно распространяемых. Некоторые из них – достаточно сложные интегрированные системы, хотя наиболее успешные являются простыми средствами, сфокусированными на основных проблемах, которые позволяют интегрировать их без особых усилий в процесс разработки ПО. Наибольшую известность получила линейка инструментов, название первого из которых было четырехбуквенным акронимом, а остальные стали трехбуквенными. Марк Рочкинд из Bell Labs разработал в 1972 году инструмент SCCS (Source Code Control System). Он был усовершенствован Уолтером Тичи в 1982 году под названием RCS (R – от Revision).

    (рис 4.5) Уолтер Тичи (2006)

    Позже появился CVS (1986, C – от Concurrent, основанный на RCS), последняя версия – это SVN, новая реализация CVS.

    Установка такой системы включает:

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

  • Обновить (Update) – создать локальную копию части, хранящейся в репозитории. По умолчанию выдается последняя версия части, но можно получить и любую предыдущую версию;
  • Зафиксировать (Commit) – ввести часть в репозиторий, возможно, новую, но чаще модифицированную версию существующей части.
  • Фиксация создает новую версию, снабжая ее идентификатором версии. Общепринято идентификаторы версий задавать в виде последовательности чисел, разделяемых точкой.

    (рис 4.6) Входной и выходной контроль

    Так, на момент написания этого текста версия EiffelStudio имела номер 6.3, где 6 – это главный номер, который изменяется только для версий, вносящих принципиальные изменения, 3 – это младший номер, изменяющийся при каждом новом выпуске системы. Промежуточные версии, которые вносят, например, заплатки (patch), устраняющие ошибки, будут нумероваться как 6.3.m или даже 6.3.m.n.

    Концептуально репозиторий сохраняет все версии. Кажется, что эта цель недостижима, поскольку требует чрезмерно большой памяти, Реалистичной эту технологию делает введение понятия "diff" – различие, название, пришедшее от команды Unix, которая для двух переданных ей файлов показывает их различие – строки добавлены, строки удалены, строки изменились. Если вы когда-либо, будучи на странице History в Википедии, выбирали "Compare selected versions" ("Сравнить выбранные версии"), то могли видеть подобную картину сличения различий:

    Представление "diffs" двух версий файла, скажем, $$file_n$$ и $$file_{n-1}$$ предназначено для человеческого восприятия, но diff-алгоритм может также вырабатывать специальную форму d, которая позволяет сопутствующему алгоритму реконструировать $$file_n$$, зная d и $$file_{n-1}$$, или получить $$file_{n-1}$$ по $$file_n$$ и d.

    Сопутствующий алгоритм прямолинеен: d описывает последовательность строк, добавляемых, удаляемых, изменяемых, поэтому достаточно применять эти операции к файлу в заданном порядке. Алгоритм diff не столь прост.

    Как следствие такого подхода, в репозитории системы контроля версий нужно хранить только оригинальную версию каждого файла и последовательность его изменений. Когда приходит запрос на очередную версию, она реконструируется. Чаще в репозитории хранится последняя версия, поскольку в этом случае для наиболее часто возникающего запроса нет необходимости в реконструкции версии. В любом случае, поскольку "diffs", как правило, много меньше полной версии, такая технология позволяет сохранять полную историю каждого файла, иногда продолжающуюся в течение десятилетий и помнящую тысячи исправлений.

    Как показано на примере Википедии, контроль версий полезен не только для программных модулей. Например, в нашей группе в ETH многие преподаватели работают над слайдами больших курсов, – все слайды хранятся в репозитории.

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

    Почувствуй методологию

    Используй контроль версий

    Сохраняйте весь код и все документы программного проекта под управлением системы контроля версий. При фиксации версии всегда записывайте причину и природу изменений.

    Вторая часть совета связана с возможностью при фиксации новой версии ввести сообщение, которое будет храниться вместе с изменениями. Можно этого не делать, но так поступать не следует. Ситуация аналогична заданию заголовочного комментария для метода. Систематическое применение этого правила приводит к тому, что со временем репозиторий превращается в обогащенную базу знаний, хранящую эволюцию ПО.

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

    Почувствуй методологию

    Чаще фиксируйте изменения

    Каждое важное изменение следует фиксировать. Это позволяет минимизировать конфликты и облегчает их разрешение.

    Дополняющая часть совета включает "ветвление".

    Почувствуй методологию

    Ветвление

    Не создавайте новую ветвь контроля версий, если только не собираетесь на старой основе разработать новый, независимо сопровождаемый продукт.

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

    Соблазн ветвления возникает достаточно часто, когда несколько разработчиков, работая над одним и тем же кодом, развивают его в разных направлениях, – понятно их желание работать независимо, оставляя проблемы воссоединения на будущее. Мудрость, накопленная сообществом разработчиков за многие годы, говорит, что подобная независимость – плохая идея. Небольшие хлопоты по разрешению небольших ежедневных конфликтов предпочтительнее "большого взрыва", когда собираются все разработчики, предлагающие результаты работы за несколько месяцев упорного труда, и у каждого из них все работает хорошо, но отказывается работать в совместном варианте.

    Единственный случай, когда допускается ветвление, – это создание новой независимой линейки программного продукта.

    Например, EiffelStudio имеет исследовательскую версию EVE (Eiffel Verification Environment), которая является ветвью версии 6.2. В данном случае обе ветви все еще остаются синхронизированными, подвергаясь регулярным реконструкциям, поскольку изменения имеют тенденцию воздействовать на разные части системы.

    Управление конфигурацией, включающее управление сборкой, контроль версий, так же как и более продвинутые приложения, – это те "лучшие практики", отработанные современной инженерией программ, и их следует применять в каждом проекте – большом или малом.

    4.3. Репозиторий проекта "в целом"

    Управление сборкой, контроль версий поднимают специфические проблемы управления проектом. Дополняя эти частные решения, часто интегрируя их, в последние годы появились платформы "общего репозитория проекта", предоставляя общее хранилище проекту в целом. Одной из наиболее известных таких систем является SourceForge. Другим примером является разработанная в ETH система Origo.

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

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

    4.4. Просмотр и документирование

    Большие программные системы включают многие компоненты, связанные различными отношениями, такими как клиентские отношения и отношения наследования в ОО-мире. Методы в этом мире подвергаются многим перевоплощениям в своем долгом путешествии через поколения наследования – классы могут переопределять их, переименовывать, отменять их определение.

    Средства просмотра помогают программистам разобраться в этом лабиринте. Они помогают получить ответ на типичные вопросы:

  • Кто является родителями класса С? Его наследники, предки, потомки? Его клиенты, поставщики?
  • Какой из предков впервые определил метод f?
  • У какого предка я могу найти версию f, применяемую в классе С?
  • Связанной задачей является задача генерирования документации по тексту ПО. Возможно, вам потребуются версии класса на разных уровнях абстракции (контрактный облик или интерфейс класса), в разных форматах (HTML, PostScript). Насколько возможно, этот процесс должен быть автоматизирован и должен извлекать информацию из самого текста ПО, а не из различных документов, его сопровождающих. Если пользоваться внешней информацией, то всегда есть риск, что она устарела, не поспевая за изменениями ПО. Так как код может не содержать всю требуемую информацию, некоторые языки программирования предоставляют специальные конструкции для этих целей. Так, языки Java и C# позволяют задавать документируемые комментарии, обрабатываемые специальным инструментарием; в языке Eiffel для этих целей введено специальное предложение note, которое может и должно появляться в классах Eiffel, позволяя описать специальные свойства класса.

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

    4.5. Метрики

    Информатика не относится к естественным наукам: объекты ее изучения – это творения, созданные не природой, а человеком. Эти творения большие и сложные, так что к ним не всегда применимы эмпирические, количественные методы анализа, которые используют ученые для объектов реального мира. Специальные метрические инструменты применяются для программ.

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

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

    4.6. Интегрированная среда разработки

    Прогресс, достигнутый в разработке индивидуальных инструментов программной инженерии – компиляторов, интерпретаторов, редакторов, компоновщиков, средств управления конфигурацией, – привел к объединению всех этих инструментов в интегрированную среду разработки – IDE (Integrated Development Environment). Интеграция означает, что для разработчика среда представляется единым инструментом, обычно интерактивным и графическим (поддерживается также возможность работы с командной строкой). Пользователи IDE могут выполнять в ней все задачи, связанные с разработкой ПО, или, по крайней мере, большинство таких задач. Вместо того чтобы готовить текст в редакторе, сохранять его в файле, потом переходить к новому инструменту – компилятору, затем к отладчику, – все это теперь можно выполнять в одном месте – в среде разработки.

    Типичный графический интерфейс пользователя – GUI – все еще предоставляет разные подокна – редактора, компилятора, отладчика и так далее, но теперь все они связаны друг с другом. Их можно комбинировать для исследования различных свойств класса: видеть текст в окне редактора, структуру в окне документации, можно запустить на выполнение один из методов в окне отладчика. Можно обмениваться информацией между окнами, используя механизм перетаскивания – "drug and drop".

    Одной из наиболее известных IDE является среда Eclipse, относящаяся к системам с открытым кодом и изначально ориентированная на Java. Из коммерческих сред следует отметить среду Microsoft Visual Studio.

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

    4.7. IDE: EiffelStudio

    В заключение обсуждения рассмотрим среду разработки EiffelStudio, как конкретный пример IDE, а также потому, что она поддерживает программистские концепции, изучаемые в этом курсе.

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

    Реализация EiffelStudio использует свою собственную технологию; на момент написания среда включала примерно 2 миллиона строк Eiffel-кода (примерно 6000 классов) плюс поддержка С кода для исполняемой среды (runtime).

    Общая структура

    На следующем рисунке показаны главные компоненты EiffelStudio:

    (рис 4.7) Главные компоненты EiffelStudio

    Центром среды – ее машиной – является EiffelStudio. Она обеспечивает пользователя ключевыми механизмами: просмотром и документацией, компиляцией, отладкой, взаимодействия между текстуальным и графическим (Diagram Tool) способом задания структуры, метрическим инструментарием.

    Слева показаны несколько библиотек повторно используемых компонентов, ориентированных на разные области применения. Основными являются библиотека EiffelBase, покрывающая общие структуры данных и алгоритмы, и библиотека EiffelVision, которая предлагает графику, переносимую на разные платформы, такие как WIndows и Linux. Компоновщик EiffelBuild, показанный в верхней части рисунка, позволяет строить графический интерфейс пользователя. Он генерирует код, вызывающий методы библиотеки EiffelVision, хотя можно непосредственно вызывать нужные методы, создавая интерфейс программным путем.

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

    Рисунок показывает два механизма компиляции. Компилятор может сгенерировать C-код, используемый здесь в качестве переносимого ассемблерного языка, который на целевой машине компилируется в машинный код. Для платформы .Net компилятор создает байт-код для этой платформы, известный как промежуточный язык CIL (Common Intermediate Language), который затем JIT-компилятором транслируется в окончательный код. Для платформы .Net используется ее собственная исполняемая среда, но в схеме, базируемой на C, результат компиляции компонуется с собственной исполняемой средой EiffelStudio.

    Созданная в результате работы выполняемая система (справа) может создавать сохраняемые структуры объектов, благодаря механизму сериализации, и обмениваться объектами с базами данных – реляционными и объектно-ориентированными.

    Просмотр и документация

    Возможности просмотра и документирования в EiffelStudio разработаны с особой тщательностью. Работа пользователя в студии характеризуется метафорой "выбрать и опустить" (pick and drop) – выбираем "камешек", представляющий программный элемент, и опускаем его в "лунку" для выполнения подходящей операции над этим элементом. Более детально:

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

    (рис 4.8) Реализаторы метода

    В качестве примера облика на снимке экрана, приведенном ниже, показаны "реализаторы" метода item из класса LINKED_LIST – все классы, в которых метод получал новую реализацию. Для получения приведенного результата, следуя технологии "выбрать и опустить", в верхнем окне было выбрано имя метода, затем оно было перетащено и опущено в нижнее окно, где был выбран облик "implementers", отображающий классы – реализаторы метода. Любой класс, метод или кластер, появляющийся на экране, может стать целью для технологии "выбрать и опустить".

    Отображать такую информацию, как и любой другой облик, можно в разных форматах – HTML, PDF, RTF (Microsoft Word) и других форматах (достаточно просто добавить произвольный формат, написав для него соответствующий "фильтр"). При командной работе предоставляемые возможности облегчают работу, позволяя обмениваться информацией, начиная от наиболее детализированной формы – исходного кода, и заканчивая абстрактными представлениями – обликами и диаграммами.

    Технология тающего льда

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

    Известно, что разработчик большой системы не компилирует ее каждый раз "с нуля". Никто не проводит месяцы, создавая код без его компиляции. Нормальной практикой является перекомпиляция системы, в которую внесены относительно небольшие изменения. Система может быть большой или малой, изменения могут большими или незначительными. Для небольших систем любой современный механизм компиляции будет достаточно быстрым. Критическим является случай малых изменений большой системы. Примером может считаться сама EiffelStudio с ее миллионами строк кода. Очередное изменение системы, ее расширение, можно реализовать в течение нескольких минут, понятно, что результаты хочется получить непосредственно, запустив систему и выполняя некоторый тест.

    Это естественное желание разработчика задает ограничение на проектирование окружения.

    Принцип тающего льда

    Время перезапуска системы после внесения в нее изменений должно зависеть от размера изменений, но не от размеров самой системы.

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

    (рис 4.9) Тающий лед

    Таяние – это процесс внесения изменений в систему, замораживание – превращение системы в глыбу льда (помещение ее в холодильник) – это перекомпиляция системы.

    Когда вы вносите изменения, "растаявшие" части по умолчанию не перекомпилируются, они интерпретируются – для них пишется байт-код. Но это относится только к измененным кусочкам кода, типично очень небольшому фрагменту системы. Как много можно изменить за несколько минут или за полчаса работы? Оставшаяся часть системы остается скомпилированной, поэтому общая производительность не пострадает из-за внесения интерпретируемого фрагмента. Общий механизм требует, чтобы компиляция и интерпретация сочетались друг с другом. Если скомпилированная подпрограмма вызывает подпрограмму, подвергшуюся модификации, то вызывается версия, использующая байт-код. Компилируемый код должен содержать переключатель, позволяющий вызывать правильную версию.

    Вся EiffelStudio является открытым кодом, так что при желании понять детали этого тонкого устройства можно, просто проанализировав код.

    Механизм таяния автоматический. Изменения могут быть сделаны либо путем редактирования текста в EiffelStudio, либо внешним инструментарием. При нажатии кнопки компиляции Compile механизм анализа зависимостей EiffelStudio находит минимальное множество элементов (это могут быть отдельные методы, а не обязательно классы), которые должны быть перекомпилированы. Сюда входят не только те элементы, которые вы явно изменили, но и другие, зависящие от них методы. Скрытие информации позволяет делать этот процесс более эффективным, поскольку, если метод изменился, но эти изменения не затронули его интерфейс, то клиентов класса перекомпилировать не нужно. Анализ не требует вмешательства пользователя, в частности, файла сборки или его эквивалента: как отмечалось ранее, семантической информации, присутствующей в тексте, вполне достаточно. Вычисление зависимостей в EiffelStudio выполняется автоматически. Это одно из преимуществ инструментов, встроенных в IDE и специально предназначенных для статически типизированного ОО-языка.

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

    Как таяние, так и заморозка являются автоматически выполняемыми операциями в режимах компиляции, предполагающих выполнение в EiffelStudio. Третий режим компиляции – финализация – позволяет сгенерировать код, полностью независимый от EiffelStudio. Финализация также выполняет интенсивную оптимизацию – удаление мертвых участков кода, статическое связывание (некий эквивалент динамического связывания, о котором пойдет речь в лекции, посвященной наследованию).

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

    Финализация, благодаря прогрессу в технике компиляции и успехам современной аппаратуры, выполняется за разумное время. Если несколько лет назад на полную компиляцию EiffelStudio уходило несколько часов, то теперь это время сокращено до 15 минут на обычном персональном компьютере.

    В дополнение к трем рассмотренным режимам компиляции – таяние, заморозка и финализация – введен еще один режим – предкомпиляции, обрабатывающий библиотеки, такие как EiffelBase и EiffelVision, так что они могут быть присоединены без компиляции. Базисные библиотеки обычно используются в предкомпилированной версии или выполняют предкомпиляцию в момент инсталляции этих библиотек.

    4.8. Ключевые концепции, введенные в этой лекции

  • Инструментарий ПО для программной инженерии играет ту же роль, что и средства CAD для проектировщиков и инженеров, работающих в архитектуре, электронике, машиностроении.
  • Для реализации программ, написанных на языке высокого уровня, применяются два подхода: компиляция, преобразующая программу в исполняемый код, и интерпретация, которая предлагает машину, непосредственно выполняющую исходную программу.
  • Некоторые реализации комбинируют компиляцию и интерпретацию. Например, компиляция может производить не машинный код, а код на промежуточном языке, который затем будет интерпретироваться. Примером смешанной стратегии компиляции и интерпретации является технология тающего льда, которая поддерживает возрастающую разработку путем интерпретации частей программы, подвергшихся изменению, оставляя остальную часть программы скомпилированной.
  • Компиляторы выполняют несколько задач: лексический и синтаксический анализ, семантический анализ, генерацию кода и его оптимизацию. Традиционно эти задачи решались на нескольких последовательных "проходах" компилятора. Теперь компиляторы строят и декорируют базисные структуры данных – АСТ и таблицу идентификаторов.
  • Компоновщик или линкер связывает скомпилированные модули, разрешая ссылки.
  • Загрузчик загружает программу в память, готовя ее к выполнению.
  • Исполняемая среда (runtime) обеспечивает поддержку в период выполнения программы.
  • Редакторы и CASE-инструментарий помогает строить программу из текстового и графического ввода. При работе с графикой и текстом должно поддерживаться "циклическое обращение".
  • Инструментарий компоновки, такой как Make, собирает систему из ее компонентов на основе описания зависимостей.
  • Инструментарий контроля версий сохраняет последовательные версии частей программы, храня различия (diffs) между версиями.
  • Средства просмотра и документирования облегчают анализ и понимание больших программных систем.
  • Интегрированная среда разработки – IDE – поддерживает основные задачи разработки ПО, предоставляя взаимосвязанную коллекцию инструментов с согласованным интерфейсом.
  • Новый словарь

    Branching Ветвление Browsing Просмотр
    Bytecode Байт-код CASE Средства автоматизации программиста
    Commit Фиксация (транзакций) Debugger Отладчик
    Configuration management Управление конфигурацией Diff Инструментарий, определяющий различия
    DSL Проблемно-ориентированный язык IDE Интегрированная среда разработки
    Jitter JIT-компилятор Melting Ice Технология "тающий лед"
    Jitting Компиляция байт-кода Pass (of a compiler) Проход компилятора
    Metric tool Метрический инструментарий Runtime Исполняемая среда
    Round-trip engineering Циклическое обращение. Ограничение при взаимных преобразованиях текста и графики Update Обновление
    Version control Контроль версий Virtual machine Виртуальная машина

    4.9. Упражнения

    4.9.1. Словарь

    Дайте точные определения терминам словаря.

    4.9.2. Карта концепций

    Добавьте новые термины в карту концепций, построенную в предыдущих лекциях.

    4.9.3. Интерпретатор и компилятор

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

    Вернуться к учебному плану