3.2. Конструирование программ
Одной из основных задач проектирования является декомпозиция системы, т. е. разделение системы в целом на совокупность взаимосвязанных элементов. При декомпозиции последовательно меняется уровень детализации системы.
По направлению процесса декомпозиции принято выделять три основных метода проектирования.
При нисходящем проектировании (проектировании «сверху вниз») проектирование начинается с верхнего уровня. Система иерархически разбивается на подсистемы и т.д. вплоть до компонентов нижнего уровня. Это метод общего назначения, с его помощью можно проектировать любую систему.
При восходящем проектировании (проектировании «снизу вверх») сразу выделяются необходимые компоненты нижнего уровня реализации, на основе которых строятся подсистемы уровня выше и т.д. до верхнего уровня. Этот метод используют для относительно небольших систем, как правило, инструментального назначения. В таких системах обычно четко прослеживается большое количество инструментальных компонентов нижнего уровня, а на верхнем уровне, практически, реализуется только интерфейс к ним.
При проектировании методом расширения ядра (проектировании «от центра») выделяется базовый процесс или объект (ядро), на котором основана вся система. Проектирование ведется одновременно «вниз» (для реализации ядра на низком уровне) и «вверх» (для использования ядра на верхнем уровне). Примером может служить реализация реляционной системы управления базами данных (СУБД). В ней четко прослеживается базовое понятие записи. Проектирование «вниз» нацелено на реализацию понятия записи, типов полей, операций над полями и т.д. Проектирование «вверх» предназначено для реализации собственно СУБД, т. е. таблиц, запросов и т.д.
Наиболее часто используется смешанный подход к проектированию, при котором основное проектирование ведется сверху вниз, однако используются элементы проектирования от центра и снизу вверх.
Проектирование средних и тем более крупных систем есть столь сложный и длительный процесс, что его стремятся также вести параллельно несколькими группами проектировщиков. При этом декомпозиция системы органично приводит к декомпозиции самого процесса проектирования.
Объектно-ориентированное программирование
В настоящее время наиболее привлекательные для программистов языки базируются на так называемой объектной модели, которая имеет четыре главных элемента: абстрагирование, инкапсуляция, модульность, иерархия.
Абстрагирование — выделения абстракций (abstraction), под которыми понимаются существенные характеристики объекта, которые отличают его от всех других объектов и четко определяют его концептуальные границы для наблюдателя.
Инкапсуляция (encapsulation) — разделение элементов абстракции, которые образуют ее структуру и поведение.
Модульность (modularity) — разделение системы на модули (module), под которыми понимаются единицы кода, служащие блоками физической структуры системы.
Иерархия (hierarchy) — подчинение или упорядочение абстракций. Две типичных иерархии в сложной системе — иерархия наследования «общее/частное», присущая, обычно, типам (классам) и иерархия агрегирования «целое/часть», присущая, обычно, элементам (объектам). Иерархия присуща также модулям и другим частям системы.
Наиболее явно эти черты присутствуют в языках, реализующих концепции объектно-ориентированного программирования. Под последним понимается методология реализации, при которой программа организуется как совокупность сотрудничающих объектов, каждый из которых является экземпляром какого-либо класса, а классы образуют иерархию наследования. При этом классы обычно статичны, а объекты очень динамичны, что поощряется динамическим связыванием и полиморфизмом.
Для ООП характерны понятия наследования и полиморфизма.
Наследование (inheritance) — отношение между классами, при котором класс использует структуру или поведение другого (одиночное наследование) или других (множественное наследование). Наследование вводит иерархию «общее/частное».
Полиморфизм (polymorphism) — положение теории типов, согласно которому имена (например, переменных) могут обозначать объекты разных (но имеющих общего родителя) классов. Следовательно, любой объект (метод), обозначаемым полиморфным именем, может по-своему реагировать на некий общий набор операций (аргументов).
Модульное программирование
Понятие модуля является в значительной степени аксиоматичным и трудно поддается формальному определению, общему для всех языков. Обычно под модулем понимают компонент программной системы, оформляемый, как правило, в виде отдельного файла с целью раздельной компиляции. Модуль в программных проектах также является единицей описания и администрирования и, как правило, кодируется на этапе программирования одним программистом. Модульное программирование является воплощением в процессе разработки программ обоих общих методов борьбы со сложностью и обеспечение независимости компонент системы, и использование иерархических структур. Для воплощения первого метода формулируются определенные требования, которым должен удовлетворять программный модуль, т.е. выявляются основные характеристики «хорошего» программного модуля. Для воплощения второго метода используют древовидные модульные структуры программ (включая деревья со сросшимися ветвями).
Оценка программного модуля осуществляется на основании следующих конструктивных характеристик: размер модуля, прочность модуля, сцепление с другими модулями, рутинность модуля.
Размер модуля измеряется числом содержащихся в нем операторов или строк. Модуль не должен быть слишком маленьким или слишком большим. Маленькие модули приводят к громоздкой модульной структуре программы и могут не окупать накладных расходов, связанных с их оформлением. Большие модули неудобны для изучения и изменений, они могут существенно увеличить суммарное время повторных трансляций программы при отладке программы. Обычно рекомендуются программные модули размером от нескольких десятков до нескольких сотен операторов.
Прочность модуля это мера его внутренних связей. Чем выше прочность модуля, тем больше связей он может спрятать от внешней по отношению к нему части программы и, следовательно, тем больший вклад в упрощение программы он может внести. Самой слабой степенью прочности обладает модуль, прочный по совпадению. Это такой модуль, между элементами которого нет осмысленных связей. Такой модуль может быть выделен, например, при обнаружении в разных местах программы повторения одной и той же последовательности операторов, которая и оформляется в отдельный модуль. Функционально прочный модуль это модуль, выполняющий (реализующий) одну какую-либо определенную функцию. Информационно прочный модуль это модуль, выполняющий (реализующий) несколько операций (функций) над одной и той же структурой данных (информационным объектом), которая считается неизвестной вне этого модуля. Для каждой из этих операций в таком модуле имеется свой вход со своей формой обращения к нему.
Сцепление модуля это мера его зависимости по данным от других модулей. Характеризуется способом передачи данных. Чем слабее сцепление модуля с другими модулями, тем сильнее его независимость от других модулей. Для оценки степени сцепления используется упорядоченный набор из шести видов сцепления модулей. Худшим видом сцепления модулей является сцепление по содержимому. Таким является сцепление двух модулей, когда один из них имеет прямые ссылки на содержимое другого модуля (например, на константу, содержащуюся в другом модуле). Не рекомендуется использовать также сцепление по общей области это такое сцепление модулей, когда несколько модулей используют одну и ту же область памяти. Единственным видом сцепления модулей, который рекомендуется для использования современной технологией программирования, является параметрическое сцепление это случай, когда данные передаются модулю либо при обращении к нему как значения его параметров, либо как результат его обращения к другому модулю для вычисления некоторой функции. Такой вид сцепления модулей реализуется на языках программирования при использовании обращений к процедурам (функциям).
Рутинность модуля это его независимость от предыстории обращений к нему. Модуль будем называть рутинным, если результат (эффект) обращения к нему зависит только от значений его параметров (и не зависит от предыстории обращений к нему). Модуль будем называть зависящим от предыстории, если результат (эффект) обращения к нему зависит от внутреннего состояния этого модуля, изменяемого в результате предыдущих обращений к нему.
Насколько возможно, все модули:
- должны быть связаны друг с другом иерархическим образом, причем структура связей, по возможности, должна представлять собой дерево;
- должны иметь небольшие размеры (не более 100 операторов языка программирования высокого уровня);
- должны быть функционально простыми (чем меньше число получаемых модулем управляющих признаков, которые им анализируются, тем он более функционально прост);
- должны иметь четко выраженное функциональное назначение;
- по возможности не должны использовать общие блоки данных (глобальные переменные), т. е. должны получать входные данные в форме параметров вызова и возвращать выходные данные вызывающему модулю в явном виде.
Тема 5. Способы конструирования программ
Обзор современных методов программирования. Структурное, процедурное, модульное и объектно-ориентированное программирование. Технологии нисходящего и восходящего проектирования программ.
Сущность структурного программирования: разбиение на подзадачи, нисходящее проектирование, стандартные структуры управления. Достоинства и недостатки. Виды стандартных управляющих структур. Базовые управляющие структуры: следование, развилка, цикл с предусловием. Дополнительные управляющие структуры: обход, выбор варианта, цикл с постусловием, цикл с параметром. Реализация стандартных управляющих структур на современных языках программирования. Примеры использования управляющих структур. Правила проектирования и оформления структурных программ.
Концепции процедурного программирования. Функции. Основные понятия. Принципы использования функций в программах. Список параметров функций. Вызов функций на исполнение. Формальные и фактические параметры. Механизм передачи параметров. Процедурные типы. Параметры процедурного типа. Примеры использования. Области действия объявлений и описаний функций. Локальность и глобальность. Организация интерфейса диалоговых программ.
Понятие рекурсии. Рекурсивные определения и алгоритмы. Программирование рекурсивных алгоритмов: рекурсивные функции. Механизм рекурсивных вызовов. Бинарное дерево как рекурсивная структура данных. Рекурсивные процедуры обхода дерева: инфиксная форма, префиксная форма, постфиксная форма. Особенности использования рекурсии при построении дерева.
Концепции модульного программирования. Модули: назначение, структура, трансляция, тестирование. Особенности использования модулей. Модульные программы. Стандартные модули в системах программирования: назначение и правила использования. Организация взаимодействия программных модулей. Построение многомодульных программ средствами языка программирования высокого уровня. Запуск внешних программ. Командная строка. Многопрограммные комплексы.
Тема 6. Инструментальные средства разработки программ.
Современные интегрированные среды проектирования программ. Состав и назначение элементов интегрированной среды программирования: текстовый редактор, транслятор, редактор связей, компоновщик, загрузчик, отладчик, инструктор, библиотекарь, профайлер. Схема обработки программы на языке программирования. Трансляция, виды трансляторов. Основные этапы трансляции. Набор, редактирование, отладка и выполнение программ в интегрированной среде программирования. Интерфейс пользователя среды.
Распределение времени по разделам программы и видам занятий
Номер и наименование раздела программы