Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Для зачета пр ПрИн.docx
Скачиваний:
1
Добавлен:
01.05.2025
Размер:
792.65 Кб
Скачать

Сложность программного обеспечения

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

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

- задачи поддержания целостности информации объемом в сотни тысяч записей при параллельном доступе к ней с обновлениями и запросами;

- системы управления и контроля за реальными процессами (например, диспетчеризация воздушного или железнодорожного транспорта);

- среды разработки, которые упрощают создание приложений в конкретных областях;

- программы, которые имитируют определенные стороны человеческого интеллекта.

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

Сложность ПО вызывается четырьмя основными причинами:

  1. сложностью реальной предметной области, из которой исходит заказ на разработку;

  2. трудностью управления процессом разработки;

  3. необходимостью обеспечить достаточную гибкость программы;

  4. неудовлетворительными способами описания поведения больших дискретных систем.

Первая причина обусловлена следующими факторами:

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

  • Прибавьте к этому дополнительные требования: удобство, производительность, стоимость, надежность.

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

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

Вторая причина обусловлена:

  • Ростом числа строк программного кода.

  • Необходимостью в коллективной разработке.

  • Необходимостью в координации и согласовании работ отдельных исполнителей и при этом сохранения целостности основной идеи.

Третья причина - обеспечение гибкости программного продукта.

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

Четвертая причина – проблема описания поведения больших дискретных систем.

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

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

Пути ограничения сложности программного обеспечения

Способ управления сложными системами был известен еще в древности – «разделяй и властвуй». Поэтому первый способ преодоления сложности ПО – декомпозиция.

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

Существует 2 способа декомпозиции: алгоритмическая декомпозиция и объектно-ориентированная декомпозиция.

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

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

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

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

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

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

Эволюция технологий программирования

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

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

Языки этого поколения (Фортран, Алгол, PL/1) являются процедурными и поддерживают процедурный стиль программирования. Программа разрабатывается в терминах тех действий, которые она выполняет. Основной единицей программы является процедура (функция). Процедуры вызывают другие процедуры, все вместе они работают по определенному алгоритму, который ведет к решению задачи.

Ключевые понятия процедурного программирования – понятия структуры данных, оператора и процедуры (функции) как основной семантической единицы программы. Данные, объединенные в структуры, обрабатываются различными процедурами, а совокупности семантически связанных между собой процедур объединяются в программные модули.

С помощью таких языков решались в основном вычислительные задачи. Основными пользователями ЭВМ были физики, математики и инженеры.

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

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

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

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

Первой была осознана проблема ошибок. Возник вопрос, можно ли так организовать тестирование и проверку программ, чтобы добиться 100-процентной уверенности в ее правильности? Работы по организации процесса тестирования начали появляться в конце 60-х годов. Примерно к середине 70-х годов были заложены основы организации тестирования, которыми в основном пользуются в настоящее время.

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

Широкого практического применения эта теория не получила по двум причинам:

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

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

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

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

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

Следствием интенсивных исследований в области методов программирования стало появление большого числа средств автоматизации разработки программ (CASE-средств, Computer Aided Software Engineering). Предполагалось, что после записи задачи на каком-либо высокоуровневом языке эти системы автоматически (или хотя бы с минимальным участием человека) сгенерируют готовую, правильно работающую программу, которая адекватно решает поставленную задачу.

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

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

В 70-е годы была сформулирована модель процесса разработки. Эта модель выделяла несколько фаз процесса: анализ, кодирование, тестирование, внедрение. Каждая последующая ста­дия «вытекает» из предыдущей, поэтому и модель получила назва­ние каскадной.

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

Очередное увеличение сложности решаемых задач и размеров создаваемых программных систем привело к появлению и широкому распрост­ранению объектно-ориентированного подхода к программирования.

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

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

Структурный подход базируется на двух основополагающих принципах:

  1. использование процедурных языков программирования;

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

Задача решается применением последовательности действий. На вход программы подаются некоторые данные, программа выполняется и выдает ответ.

После этого начинается последовательное разложение всей задачи на более простые действия.

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

Прочитать адрес

Сверить адрес с базой имеющихся адресов

Если результат проверки положителен, напечатать Да,

в противном случае напечатать Нет.

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

program check_address (input, output); var

an_address : Address; begin

read_address(an_address);

if (check_database(an_address)) then

writelnTMIa") ; else

writeln("HeT");

end

Эта программа использует процедуру read_address для чтения адреса и процедуру check_database для сверки прочитанного адреса с информацией в базе данных. Теперь мы можем продол­жить процесс составления программы для процедур следующего уровня: чтение адреса и сверки с базой данных.

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

Программа компонуется с заглушками и может работать. Заглушки позволяют проверить логику верхнего уровня до реализации следующего уровня.

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

Объектно-ориентированное программирование

Причины появления объектно-ориентированного подхода к программированию

Объектно-ориентированное программирование появилось и по­лучило широкое распространение из-за существования трех важнейших проблем программирования.

1 - Развитие языков и методов программирования не успева­ло за растущими потребностями в про­граммах. Единственным реальным способом удовлетворить эти потребности был метод многократного использования уже разработанного, протестированного и отлаженного программного обеспечения.

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

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

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

Возникновению объектно-ориентированного подхода к проектированию систем способствовали следующие достижения науки и технологии:

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

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

3 - Теория построения и моделирования систем управления базами данных внесла в объектное программирование идеи построения отношений между объектами.

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

5 - Развитие философии и теории познания. Во многом объектно-ориентированное построение систем - это определенный взгляд на моделируемый реальный мир. Именно в этом аспекте философия и теория познания оказали сильное влияние на объектную модель. Еще древние греки рассматривали мир в виде объектов или процессов. Декарт выдвинул предположение, что для человека естественным представляется объектно-ориентированное рассмотрение окружающего мира. Минский предположил, что разум прояв­ляется как взаимодействие агентов, не умеющих по отдельности мыслить.

Объектно-ориентированный подход к программиро­ванию

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

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

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

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

Объектно-ориентированный подход к программированию поддерживается языками программирования, такими, как Смолток, Си++, Java и т.д. Языки являются главными инструментами объектно-ориентированного программирования, именно при их разработке появилось большинство тех идей, которые и составляют основу объектно-ориентированного метода.

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

Затем появились (и продолжают появляться) другие объектно-ориентированные языки, которые определяют современное состояние программирования. Наиболее распространенными из них стали Си++, С#, Java.

Компоненты объектно-ориентированного подхода

Рассмотрим 3 компонента объектно-ориентированного под­хода:

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

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

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

Именно объектно-ориентированная декомпозиция отличает объектно-ориентированное проектирование от структурного; в пер­вом случае логическая структура системы отражается абстрак­циями в виде классов и объектов, во втором - алгоритмами.

  1. Объектно-ориентированное программирование (ООП) – это методология программирования, которая основана на представлении программы в виде совокупностей объектов, каждый из которых является экземпляром определенного класса, а классы образуют иерархию на принципах наследования.

В данном определении можно выделить три части: 1) OOП использует в качестве базовых элементов объекты, а не алгоритмы; 2) каждый объект является экземпляром какого-либо определенного класса; 3) классы организованы иерархически. Программа будет объектно-ориентированной только при соблюдении всех трех указанных требований.

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

Основные положения объектной модели

Объектно-ориентированная технология основывается на так называемой объектной модели. Объектная модель (object model) - это совокупность основополагающих принципов, лежащих в основе объектно-ориентированного проектирования. Основными ее принципами являются: абстрагирование, инкапсуляция, модульность, иерархичность, типизация, параллелизм и сохраняемость. Каждый из этих принципов сам по себе не нов, но в объектной модели они впервые применены в совокупности.

Объектная модель имеет 4 главных элемента:

  1. абстрагирование;

  2. инкапсуляция (ограничение доступа);

  3. модульность;

  4. иерархия.

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

5. типизация;

6. параллелизм;

7. сохраняемость.

Они называются дополнительными, так как они полезны в объектной модели, но не обязательны.

Рассмотрим эти элементы.

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

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

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

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

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

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

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

3. Модульность - это свойство системы, которая была разделена на связные и слабо зацепленные между собой модули.

Разделение программы на модули позволяет частично уменьшить ее сложность, однако гораздо более важен тот факт, что разделение программы позволяет улучшить проработку ее частей. В языках программирования (Object Pascal, C++, Ada, CLOS) модуль - это самостоятельная языковая конструкция. В этих языках классы и объекты составляют логическую структуру системы, они помещаются в модули, образующие физическую структуру системы. Это свойство становится особенно полезным, когда система состоит из многих сотен классов. Модули играют роль физических контейнеров, в которых помещаются определения классов и объектов при логическом проектировании системы.

В разных языках программирования модульность поддерживается по-разному. Например, в C++ модулями являются раздельно компилируемые файлы. Для C++ традиционным является помещение интерфейсной части модулей в отдельные файлы с расширением .h (заголовочный файл). Реализация, то есть текст модуля, хранится в файлах с расширением .срр (файл реализации). Связь между файлами объявляется директивой макропроцессора #include. Такой подход строится исключительно на соглашении и не является строгим требованием самого языка.

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

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

  1. Структура модуля должна быть достаточно простой для вос­приятия;

  2. Реализация каждого модуля не должна зависеть от реализации других модулей;

  3. Должны быть приняты меры для обеспечения возможности внесения изменений там, где они наиболее вероятны;

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

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

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

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

4. Иерархия - это упорядочение абстракций, расположение их по уровням.

Абстракция - вещь полезная, но всегда, кроме самых простых ситуаций, число абстракций в системе намного превышает умственные возможности человека. Инкапсуляция позволяет в какой-то степени устранить это препятствие, убрав из поля зрения внутреннее содержание абстракций. Модульность также упрощает задачу, объединяя логически связанные абстракции в группы. Но этого оказывается недостаточно. Значительное упрощение в понимании сложных задач достигается за счет образования из абстракций иерархической структуры.

Основными видами иерархических структур применительно к сложным системам являются структура классов (иерархия «is-a») и структура объектов (иерархия «part of»).

Примеры иерархии: одиночное наследование, множественное наследование, агрегация. Иерархия «is а» определяет отношение обобщение-специализация, то отношение «part of» (часть) вводит иерархию агрегации.

Основным видом иерархии «is-a» является наследование. Наследование означает такое отношение между классами (отношение родитель-потомок), когда один класс заимствует структурную или функциональную часть одного или нескольких других классов (соответственно, одиночное и множественное наследование). Иными словами, наследование создает такую иерархию абстракций, в которой подклассы наследуют строение от одного или нескольких суперклассов. В наследственной иерархии общая часть структуры и поведения сосредоточена в наиболее общем суперклассе. По этой причине говорят о наследовании, как об иерархии обобщение-специализация. Суперклассы при этом отражают наиболее общие, а подклассы - более специализированные абстракции, в которых члены суперкласса могут быть дополнены, модифицированы и даже скрыты. Принцип наследования позволяет упростить выражение абстракций, делает проект менее громоздким и более выразительным

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

5. Типизация - это способ защититься от использования объектов одного класса вместо другого, или, по крайней мере, управлять таким использованием.

Другими словами, типизация - ограничение предъявляемых классу объектов, препятствующих взаимозамене различных классов и в большинстве случаев сильно сужающих возможность такой замены. Концепция типизации строится на понятии абстрактных типов данных. Тип - точное определение свойств строения или поведения, которое присуще некоторой совокупности объекта. Часто термины «тип» и «класс» считают эквивалентными. Более точно сказать, что класс реализует тип. Типизация - ограничение предъявляемых классу объектов, препятствующих взаимозамене различных классов и в большинстве случаев сильно сужающих возможность такой замены. Типизация позволяет выполнять описание абстракций таким образом, что реализуется поддержка проектных решений на уровне языка программирования.

Объектно-ориентированные языки программирования могут быть:

  1. строго типизированными,

  2. нестрого типизированными,

  3. совсем не типизированными.

Это позволяет говорить о типизации, как о второстепенном элементе. Сильно типизированные языки - это такие языки, в которых все выражения проводят проверку на соответствие типов. C++ поддерживает сильную типизацию. Различают статическую типизацию (раннее связывание) и динамическую типизацию (позднее связывание). Разделение имеет отношение ко времени, когда имена связывают с типами: статическая - во время компиляции; динамическая - во время исполнения программы.

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

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

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

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

  1. Промежуточные результаты вычисления выражений.

  2. Локальные переменные вызова процедур.

  3. Собственные переменные (глобальные).

  4. Данные, сохраняющиеся между вызовами основной программы.

  5. Данные остающиеся без изменений в различных версиях программы.

  6. Данные, которые переживают создавшую их программу.

Традиционно языки программирования реализовывают первые три уровня, а последние три связываются с технологией БД. Введение сохраняемости приводит нас к объектно-ориентированным базам данных.

Модели жизненного цикла ПО

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

Одним из ключевых понятий управления программными проектами является понятие жизненного цикла программных средств (ЖЦ).

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

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

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

Основным нормативным документом, регламентирующим ЖЦ ПО, является международный стандарт ISO/IEC 12207, который определяет понятие «Жизненный цикл программной системы» так:

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

Этот стандарт описывает 17 процессов жизненного цикла, распределенных по трем категориям – группам процессов:

  1. Основные процессы ЖЦ

    1. Заказ

    2. Поставка

    3. Разработка

    4. Эксплуатация

    5. Сопровождение

  2. Вспомогательные процессы ЖЦ

    1. Документирование

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

    3. Обеспечение качества

    4. Верификация

    5. Аттестация

    6. Совместный анализ

    7. Аудит

    8. Решение проблем

  3. Организационные процессы ЖЦ

    1. Управление

    2. Создание инфраструктуры

    3. Усовершенствование

    4. Обучение

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

Существуют три основные модели жизненного цикла ПО:

  1. Каскадная (водопадная) или последовательная

  2. Итеративная и инкрементальная (эволюционная, гибридная)

  3. Спиральная или модель Боэма

Рассмотрим их подробнее.