Предыдущая лекция | &nbsp; | Следующая лекция
:----------------:|:----------:|:----------------:
[Языки программирования](./t2l1.md) | [Содержание](../readme.md#тема-2-языки-и-методы-программирования) | [Системы контроля версий](./skv.md)

# Методы программирования

Основными технологиями разработки программного обеспечения являются

* Императивное программирование
* Структурное программирование
* Модульное программирование
* Объектно-ориентированное программирование

## Императивное программирование

*Императивное программирование* — это исторически первая методология программирования, которой пользовался каждый программист, программирующий на любом из «массовых» языков программирования – Basic, Pascal, C.

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

Методы и концепции.

* Метод изменения состояний — заключается в последовательном изменении состояний. Метод поддерживается концепцией алгоритма.
* Метод управления потоком исполнения — заключается в пошаговом контроле управления. Метод поддерживается концепцией потока исполнения.
 
>Вычислительная модель. Если под вычислителем понимать современный компьютер, то его состоянием будут значения всех ячеек памяти, состояние процессора (в том числе — указатель текущей команды) и всех сопряженных устройств. Единственная структура данных — последовательность ячеек (пар «адрес» - «значение») с линейно упорядоченными адресами.

В качестве математической модели императивное программирование использует машину Тьюринга-Поста — абстрактное вычислительное устройство, предложенное на заре компьютерной эры для описания алгоритмов.

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

Традиционное средство структурирования — подпрограмма (процедура или функция). Подпрограммы имеют параметры и локальные определения и могут быть вызваны рекурсивно. Функции возвращают значения как результат своей работы.

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

Императивные языки программирования. Императивные языки программирования манипулируют данными в пошаговом режиме, используя последовательные инструкции и применяя их к разнообразным данным. 
>Считается, что первым алгоритмическим языком программирования был язык Plankalkuel (от plan calculus), разработанный в 1945—1946 годах Конрадом Цузе (Konrad Zuse).

Большинствои из наиболее известных и распространенных императивных языков программирования было создано в конце 50-х — середине 70-х годов XX века. Это период 80-х и 90-х годов соответствует увлечениям новыми парадигмами, и императивных языков в это время практически не появлялось.

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

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

## Структурное программирование

Структурное программирование (СП) возникло как вариант решения проблемы уменьшения СЛОЖНОСТИ разработки программного обеспечения.

В начале эры программирования работа программиста ничем не регламентировалась. Решаемые задачи не отличались размахом и масштабностью, использовались в основном машинно-ориентированные языки и близкие к ним язык типа Ассемблера, разрабатываемые программы редко достигали значительных размеров, не ставились жесткие ограничения на время их разработки.

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

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

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

Подход базируется на двух основных принципах:

* Последовательная декомпозиция алгоритма решения задачи сверху вниз.
* Использование структурного кодирования.

Напомним, что данная методология является важнейшим развитием императивной методологии.

**Происхождение, история и эволюция**. Создателем структурного подхода считается Эдсгер Дейкстра. Ему также принадлежит попытка (к сожалению, совершенно неприменимая для массового программирования) соединить структурное программирование с методами доказательства правильности создаваемых программ. В его разработке участвовали такие известные ученые как Х. Милс, Д.Э. Кнут, С. Хоор.

**Методы и концепции, лежащие в основе структурного программирования**. Их три:

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

**Структурные языки программирования**. Основное отличие от классической методологии императивного программирования заключается в отказе (точнее, той или иной степени отказа) от оператора безусловного перехода.

[Пратт Т., 1979] "Важным для программиста свойством синтаксиса является возможность отразить в структуре программы структуру лежащего в ее основе алгоритма. При использовании для построения программы метода, известного под названием структурное программирование, программа конструируется иерархически - сверху вниз (от главной программы к подпрограммам самого нижнего уровня), с употреблением на каждом уровне только ограниченного набора управляющих структур: простых последовательностей инструкций, циклов и некоторых видов условных разветвлений. При последовательном проведении этого метода структуру результирующих алгоритмов легко понимать, отлаживать и модифицировать. В идеале у нас должна появиться возможность перевести построенную таким образом схему программы прямо в соответствующие программные инструкции, отражающие структуру алгоритма."

Теорема о структурировании (Бёма-Джакопини (Boem-Jacopini)): Всякую правильную программу (т.е. программу с одним входом и одним выходом без зацикливаний и недостижимых веток) можно записать с использованием следующих логических структур - последовательность, выбора и повторение цикла

*Следствие 1*: Всякую программу можно привести к форме без оператора goto.

*Следствие 2*: Любой алгоритм можно реализовать в языке, основанном на трех управляющих конструкциях -последовательность, цикл, повторение.

*Следствие 3*: Сложность структурированных программ ограничена, даже в случае их неограниченного размера.

Структурное программирование- это не самоцель. Его основное назначение- это получение хорошей ("правильной") программы, однако даже в самой хорошей программе операторы перехода goto иногда нужны: например - выход из множества вложенных циклов.

Практически на всех языках, поддерживающих императивную методологию, можно разрабатывать программы и по данной методологии. В ряде языков введены специальные заменители оператора goto, позволяющие облегчить управление циклами (например, Break и Continue в языке C).

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

## Модульное программирование

Модульное программирование - это такой способ программирования, при котором вся программа разбивается на группу компонентов, называемых модулями, причем каждый из них имеет свой контролируемый размер, четкое назначение и детально проработанный интерфейс с внешней средой. Единственная альтернатива модульности — монолитная программа, что, конечно, неудобно. Таким образом, наиболее интересный вопрос при изучении модульности — определение критерия разбиения на модули.

### Концепции модульного программирования. 

В основе модульного программирования лежат три основных концепции:

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

2. Аксиома модульности Коуэна. Модуль — независимая программная единица, служащая для выполнения некоторой определенной функции программы и для связи с остальной частью программы. Программная единица должна удовлетворять следующим условиям:

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

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

    - стремление к выделению независимой единицы программного знания. В идеальном случае всякая идея (алгоритм) должна быть оформлена в виде модуля;
    - потребность организационного расчленения крупных разработок;
    - возможность параллельного исполнения модулей (в контексте параллельного программирования).

**Определения модуля и его примеры**. Приведем несколько дополнительных определений модуля.

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

Функциональная спецификация модуля должна включать:

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

**Разновидности модулей**. Существуют три основные разновидности модулей:

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

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

    * задачи в языке программирования Ada;
    * кластер в языке программирования CLU;
    * классы в языках программирования C++ и Java.

1. "Большие” (логические) модули, объединяющие набор "средних" или "маленьких" модулей. Примеры "больших" модулей в языках программирования:

   * модуль в языке программирования Modula-2;
   * пакеты в языках программирования Ada и Java.

Набор **характеристик** модуля предложен Майерсом [Майерс 1980]. Он состоит из следующих конструктивных характеристик:

1. размера модуля;

    В модуле должно быть 7 (+/-2) конструкций (например, операторов для функций или функций для пакета). Это число берется на основе представлений психологов о среднем оперативном буфере памяти человека. Символьные образы в человеческом мозгу объединяются в "чанки" — наборы фактов и связей между ними, запоминаемые и извлекаемые как единое целое. В каждый момент времени человек может обрабатывать не более 7 чанков.  

    Модуль (функция) не должен превышать 60 строк. В результате его можно поместить на одну страницу распечатки или легко просмотреть на экране монитора.

1. прочности (связности) модуля;  

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

    *Связность* (прочность) модуля — мера независимости его частей. Чем выше связность модуля — тем лучше, тем больше связей по отношению к оставшейся части программы он упрятывает в себе. Можно выделить типы связности, приведенные ниже.  

    *Функциональная связность*. Модуль с функциональной связностью реализует одну какую-либо определенную функцию и не может быть разбит на 2 модуля с теми же типами связностей.  

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

    *Информационная* (коммуникативная) связность. Модуль с информационной связностью — это модуль, который выполняет несколько операций или функций над одной и той же структурой данных (информационным объектом), которая считается неизвестной вне этого модуля. Эта информационная связность применяется для реализации абстрактных типов данных.  

    Обратим внимание на то, что средства для задания информационно прочных модулей отсутствовали в ранних языках программирования (например, FORTRAN и даже в оригинальной версии языка Pascal). И только позже, в языке программирования Ada, появился пакет — средство задания информационно прочного модуля.

1. сцепления модуля с другими модулями;  

    *Сцепление* — мера относительной независимости модуля от других модулей. Независимые модули могут быть модифицированы без переделки других модулей. Чем слабее сцепление модуля, тем лучше. Рассмотрим различные типы сцепления.

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

    * Сцепление по данным (параметрическое) — это сцепление, когда данные передаются модулю, как значения его параметров, либо как результат его обращения к другому модулю для вычисления некоторой функции. Этот вид сцепления реализуется в языках программирования при обращении к функциям (процедурам). Две разновидности этого сцепления определяются характером данным.
       - Сцепление по простым элементам данных.
       - Сцепление по структуре данных. В этом случае оба модуля должны знать о внутренней структуре данных.

1. рутинности (идемпотентность, независимость от предыдущих обращений) модуля.

    *Рутинность* — это независимость модуля от предыдущих обращений к нему (от предыстории). Будем называть модуль рутинным, если результат его работы зависит только от количества переданных параметров (а не от количества обращений).

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

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

## Метод объектно-ориентированного программирования (ООП).

Метод структурного программирования оказался эффективен при написании программ «ограниченной сложности». Однако с возрастанием сложности реализуемых программных проектов и, соответственно, объема кода создаваемых программ, возможности метода структурного программирования оказались недостаточными.

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

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

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

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

Объектно-ориентированные программы называют «программами, управляемыми от событий»,в отличие от традиционных программ, называемых «программам, управляемыми от данных».

Основные **методы и концепции ООП**

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

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

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

**Синтаксис и семантика**

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

В объектно-ориентированном программировании определяют три основных свойства:

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

    Большинство окружающих нас объектов относится к категориям, рассмотренным в книге [Шлеер, Меллор 1993]:

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

* Полиморфизм (полиморфизм включения) — присваивание действию одного имени, которое затем разделяется вверх и вниз по иерархии объектов, причем каждый объект иерархии выполняет это действие способом, подходящим именно ему.

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

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

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

Для поддержки концепции ООП были разработаны специальные объектно-ориентированные языки программирования. Все языки OOП можно разделить на три группы.

* Чистые языки, в наиболее классическом виде поддерживающие объектно-ориентированную методологию. Такие языки содержат небольшую языковую часть и существенную библиотеку, а также набор средств поддержки времени выполнения.
* Гибридные языки, которые появились в результате внедрения объектно-ориентированных конструкций в популярные императивные языки программирования.
* Урезанные языки, которые появились в результате удаления из гибридных языков наиболее опасных и ненужных с позиций ООП конструкций.

## Общие принципы разработки программного обеспечения

Программное обеспечение различается по назначению, выполняемым функциям, формам реализации. В этом смысле всякое ПО — сложная, достаточно уникальная программная система. Однако существуют некоторые общие принципы, которые следует использовать при разработке ПО.
 
* **Частотный** принцип. Основан на выделении в алгоритмах и в обрабатываемых структурах групп действий и данных по частоте использования. Для действий, которые чаще встречаются при работе ПО, обеспечиваются условия их наиболее быстрого выполнения. К данным, к которым происходит частое обращение, обеспечивается наиболее быстрый доступ. «Частые» операции стараются делать более короткими. 

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

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

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

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

Общесистемные принципы. При создании и развитии ПО рекомендуется применять следующие общесистемные принципы:

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

## Жизненный цикл программного обеспечения

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

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

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

По длительности жизненного цикла программные изделия можно разделить на два класса: с **малым** и **большим** временем жизни. Этим классам программ соответствуют гибкий (мягкий) подход к их созданию и использованию и жесткий промыш­ленный подход регламентированного проектирования и эксплуа­тации программных изделий. В научных организациях и вузах, например, преобладают разработки программ первого класса, а в проектных и промышленных организациях — второго.

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

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

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

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

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

Все последующее изложение акцентирует внимание на теме разработки крупных (сложных)программных средств управления и обработки информации.

Обобщенная модель **жизненного цикла** программного изделия может выглядеть так:

1. Системный анализ:
    * исследования;
    * анализ осуществимости:
        -  эксплуатационной;
        -  экономической;
        -  коммерческой.

2. Проектирование программного обеспечения:
    * конструирование:
        - функциональная декомпозиция системы, ее архитектура;
        - внешнее проектирование программного обеспечения;
        - проектирование базы данных;
        - архитектура программного обеспечения;
    * программирование:
        - внутреннее проектирование программного обеспечения;
        - внешнее проектирование программных модулей;
        - внутреннее проектирование программных модулей;
        - кодирование;
        - отладка программ;
        - компоновка программ;
    * отладка программного обеспечения.

3. Оценка (испытания) программного обеспечения.
4. Использование программного обеспечения:
    * эксплуатация;
    * сопровождение.

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

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

*Исследования начинаются* с того момента, когда руководитель разработки осознает потребность в программном обеспечении.

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

*Исследования заканчиваются* тогда, когда требования сформи­рованы в таком виде, что становятся обозримыми и при необ­ходимости могут быть модифицированы и одобрены ответствен­ным руководителем.

*Анализ осуществимости* есть техническая   часть  исследований  и начинается  тогда,  когда  намерение  руководства  окрепнет  настолько, что назначается руководитель проекта, организующий проектирование и распределение ресурсов  (рабочей  силы).

Работа заключается в исследовании предполагаемого прог­раммного изделия с целью получения практической оценки возможности реализации проекта, в частности определяются:

* осуществимость эксплуатационная, будет ли изделие доста­точно удобно для практического использования?
* осуществимость экономическая, приемлема ли стоимость разрабатываемого изделия? Какова эта стоимость? Будет ли изделие экономически эффективным инструментом в руках пользователя?
* осуществимость коммерческая,будет ли изделие привлека­тельным, пользоваться спросом, легко устанавливаемым, прис­пособленным к обслуживанию, простым в освоении?    

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

Анализ осуществимости заканчивается, когда все требования собраны и одобрены.

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

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

Часто в период системного анализа принимается решение о прекращении дальнейшей разработки программного обеспе­чения.

**Проектирование программного обеспечения**. Проектиро­вание является основной и решающей фазой жизненного цикла программного обеспечения, во время которого создается и на 90% приобретает свою окончательную форму программное из­делие.

Эта фаза жизни охватывает различные виды деятельности проекта и может быть разделена на три основных этапа: *конст­руирование, программирование и отладку* программного из­делия.

*Конструирование* программного обеспечения обычно начи­нается ещё в фазе анализа осуществимости, как только оказы­ваются зафиксированными на бумаге некоторые предварительные цели и требования к нему.

К моменту утверждения требований работа в фазе конст­руирования будет в самом разгаре.

На этом отрезке жизни программного обеспечения осу­ществляют:

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

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

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

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

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

**Оценка (испытания) программного обеспечения**. В этой фазе программное изделие подвергается строгому системному испы­танию со стороны группы лиц, не являющихся разработчиками.

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

Фаза оценки начинается, как только все компоненты (мо­дули) собраны вместе и испытаны, т.е. после полной отладки готового программного продукта. Она заканчивается после полу­чения подтверждения, что программное изделие прошло все испытания и готово к эксплуатации.

Она продолжается так же долго, как и программирование.

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

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

Фаза использования программного изделия начинается тогда, когда изделие передается в систему распределения.

Это то время, в течение которого изделие находится в действии и используется эффективно.

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

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

Использование программного продукта определяется его эксплуатацией и сопровождением.

**Эксплуатация программного изделия** заключается в испол­нении, функционировании его на ЭВМ для обработки инфор­мации и в получении результатов, являющихся целью его соз­дания, а также, в обеспечении достоверности и надежности выдаваемых данных.

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

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

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

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

Возможны и обычно желательны перекрытия между разными фазами жизненного цикла программного изделия. Однако не должно быть никакого перекрытия между несмежными про­цессами.                                         

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

## Типы приложений

Традиционно приложения делят на две большие группы (по способу взаимодействия с пользователем):

- консольные, ввод и вывод информации в которых производится при помощи стандартных потоков ввода (stdin), поток вывода (stdout) и поток ошибок (stderr).

    Стандартные потоки открываются автоматически при запуске программы и связаны по умолчанию с монитором. Хотя вывод может быть перенаправлен в файл (или из файла) средствами операционной системы (>>, <<, >, <).   

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

    Одним из недостатков консольных приложений считается необходимость ввода команд, достоинством - лёгкое встраивание в скрипты и автоматизация действий.  
    В графических операционных системах (Windows, Mac), консольные программы хоть и играют достаточно важную роль, но практически не развиваются.  

    Широкое развитие консольные программы получили в UNIX-подобных операционных системах, где консольные инструменты развиваются и совершенствуются до сих пор.

- оконные приложения позволяют выводить информацию посредством растровых изображений с интенсивным использованием событийной модели. 
    >Историческая справка - Xerox, Apple (Lisa, Macintosh), Microsoft (Windows), UNIX (X Window System). 

    В настоящий момент используется два типа графических операционных систем: клиент-серверная (X Window) - приложение использует запрос к серверу X Window нарисовать что-то в определённой области и графическое ядро (Windows) - программа взаимодействует с операционной системой посредством Windows API, выступая по сути частью операционной системы. 
    
    Остановимся на оконных приложениях Windows. Все видимые элементы рассматриваются как окна, которые могут быть главным и дочерним (элементы управления). При создании окна приложения регистрируется функция окна, где происходит обработка событий. 
    
    Разработка при помощи Windows API требует определённых усилий и выполнения рутинных операций, поэтому было разработано несколько библиотек-обёрток, облегчающих построение Windows-приложений. Среди них следует отметить разработку Microsoft - MFC (очень сильно перекликается с API) и разработку Borland - VCL - более удобная, но менее гибкая система построения оконных приложений. 
    
    Достаточно популярность платформа .NET (изначально написна для Windows, но есть и реализация под Linux - Mono), в которой построением приложений занимается интерпретатор байт-кода - это позволяет ещё быстрее строить оконные приложения (недостаток - несовместимость и необходимость установки среды исполнения .NET-приложений).

    QT (кьют) - кросс-платформенный фреймфорк, изначально разработан для С++, но есть привязки и ко многим другим языкам программирования, в частности для питона есть модуль PyQT.

>Можно еще провести классификацию по принципу построения:
>
>* Standalone-приложение («Stand» и «Alone», что на русский дословно переводится как «остаться одному») — это такое программное обеспечение, которое не нуждается в каких-либо дополнительных программах и зависимостях для его установки и функционирования. 
>* Клиент-серверное приложение, программа состоит из двух и более частей, у клиента на компьютере тонкий клиент, обеспечивающий только взаимодействие с пользователем и локальной периферией, а вся бизнес-логика вынесена на отдельный сервер.
>* WEB-приложение (в принципе это частный случай клиент-серверного приложения, в котором клиенской части просто нет, вместо нее выступает браузер)

## Консольные приложения

Теория по консольным приложениям выше уже была, посмотрим как консольное приложение выглядит на C#:

```cs
namespace oap
{
    class Hello
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Как Вас зовут?");
            var Name = Console.ReadLine();
            Console.WriteLine($"Привет {Name}");
        }
    }
}
```

---

**КОНТРОЛЬНЫЕ ВОПРОСЫ**

* [Методы программирования](#методы-программирования)
* [Общие принципы разработки программного обеспечения](#общие-принципы-разработки-программного-обеспечения)
* [Жизненный цикл программного обеспечения](#жизненный-цикл-программного-обеспечения)
* [Типы приложений](#типы-приложений)

Предыдущая лекция | &nbsp; | Следующая лекция
:----------------:|:----------:|:----------------:
[Языки программирования](./t2l1.md) | [Содержание](../readme.md#тема-2-языки-и-методы-программирования) | [Системы контроля версий](./skv.md)
