Uploaded by Антон Туманов

Лекция 1

advertisement
Существует 3 базовых структуры алгорима: следование или последовательность ветвление (рисунок
10), цикл(рисунок 11). Любая программа может быть представлена с помощью этих трёх управляющих
структур (Коррадо Бём и Джузеппе Якопини, 1965 г.).
Рис. 9. Следование
Рис. 10. Ветвление
Рис. 11. Цикл
5.1 Элементы схем алгоритмов
Основные элементы схем алгоритмов приведены на рисунке 12.
Основные элементы схем алгоритмов
Хороший программист — это тот, кто смотрит в обе стороны, переходя дорогу с односторонним
движением. (c) Даг Линдер
1. Учите основы
Понимание основ — это ключ к успеху в любой индустрии и любой профессии. До тех пор, пока вы
недостаточно хорошо знаете основы, вы не сможете стать хорошим программистом. Знание азов
позволит вам разрабатывать и реализовывать лучшие решения наилучшим способом. Если вы
ощущаете пробелы в своих знаниях, будь то основы computer science, или концепции языка, на котором
вы пишете, то никогда не поздно вернуться назад и повторить забытое.
2. Задавайте вопросы (как? почему?), когда пишете код
Есть одна вещь, которая отличает хорошего программиста от всех остальных — это желание знать, что
и как происходит. Есть люди, которые никогда не оставят в покое код, пока точно не будут знать, что
именно происходит при его выполнении. Я понимаю, что это приближает дедлайн, что у нас не всегда
есть на это время, и поэтому мы часто заканчиваем работать с кодом, как только он начинает выполнять
свои функции. И хотя поведение в подобных ситуациях это тема для другого разговора, каждый
программист может приложить как можно больше усилий для того, чтобы вникнуть в работу кода. И
поверьте, со временем это войдет в привычку, и вы будете делать это уже неосознанно.
3. Учите других — учитесь сами
Большинство из нас обращаются к форумам и группам только тогда, когда нам нужна помощь. Еще
одна вещь, которая отличает хорошего программиста от все остальных: хороший программист чаще
заглядывает в такие места, чтобы помочь другим. Такая помощь учит больше, чем помощь, оказанная
вам при решении вашей проблемы. Поверьте, после того как вы разберетесь в чужой проблеме и ее
контексте, поразмышляете над ней и дадите решение, вы научитесь гораздо большему.
4. Пишите простой, понятный, но в то же время логичный код
Как и в других областях, формула KISS (Keep it simple and short — делай короче и проще) работает и в
программировании. Пишите логичный код и избегайте усложнений. Иногда люди пишут сложный код
только для того, чтобы доказать, что они умеют писать такой код. Мой опыт подсказывает, что простой
и логичный код всегда работает хорошо, приносит меньше проблем и лучше поддается расширению.
Вспоминается отличная фраза:
Хороший код — это лучшая документация. Каждый раз, когда вы захотите добавить комментарий,
спросите себя: «Как я могу улучшить этот код, чтобы он не требовал комментирования?» (c) Стив
МакКоннелл
5. Уделяйте больше времени анализу проблемы, тогда вам понадобится меньше времени для ее
устранения
Уделяйте больше времени на понимание и анализ проблемы и разработку решения. А остальное будет
легко сделать. Разработка решения не означает использование языков или инструментов для
моделирования, вы можете просто смотреть на небо и думать о решении. У тех, кто привык стучать по
клавиатуре сразу же, как только узнал о проблеме, результат обычно не совпадает с ожидаемым.
Если вы не можете целиком понять общую структуру программы, пока принимаете душ, значит, вы не
готовы ее запрограммировать.
(c) Ричард Паттис
6. Будьте первым, кто проанализирует и оценит ваш код
Хотя это трудно, но попробуйте «сломать» ваш код до того, как это сделает кто-то другой. Со временем
вы научитесь писать почти безошибочный код. Всегда проводите подробную и беспристрастную
оценку своего кода. И никогда не бойтесь спрашивать, что другие думают о вашем коде. Работайте с
хорошими программистами и прислушивайтесь к их мнению — это поможет вам стать хорошим
программистом.
7. Не пугайтесь быстрой смены технологий
За все время работы в области IT, я встречал множество людей, которых не устраивала их работа, и
людей, которые меняли место работы, чтобы работать с новейшими технологиями. В таком стремлении
нет ничего плохого, однако ошибка в «новейших технологиях». Каждый день появляются новые
инструменты, API и фреймворки, призванные сделать разработку быстрой и простой. И эта тенденция
не снизится. Однако следует понять одну вещь: фундаментальные знания и основы меняются
значительно медленнее, чем фреймворки, новые инструменты и API. Можно провести аналогию с
морем, на поверхности которого находятся быстрые течения, однако на глубине вода спокойна и она
составляет большую часть объема. Поэтому держитесь «на глубине», поближе к основам. В мире Java
приложений уровня enterprise существует много веб-фреймворков, а новые выходят каждые две недели.
Однако основы клиент-серверной архитектуры, шаблона MVS (Model View Separation),
фильтров/сервлетов/JSP, упаковки ресурсов, обработки XML и т.д. остаются неизменны. Поэтому
лучше потратьте время на изучение этих основ, нежели на изучение вечно меняющихся фреймворков.
Поверьте, зная основы, изучить новые API и фрейморки будет куда легче.
8. «Костыли»* долго не работают
Множество программистов используют «костыли»: от недостатка времени, понимания проблемы или
опыта. Однако со временем такие решения делают код хуже: он становится менее расширяем и удобен в
поддержке. Всегда старайтесь написать такую реализацию, о которой вы знаете все. Я понимаю, что
«костыли» в некоторых ситуациях неизбежны, но тогда ситуация напоминает что-то вроде «всегда
говори правду, но иногда можешь соврать».
9. Читайте документацию
Хорошие программисты читают много документации. Это могут быть спецификации, JSR, API,
документы, туториалы и т.д. Чтение документации позволит вам понимать основы, и вы будете решать
задачи наилучшим способом.
10. Чужой код тоже может чему-то научить
Я работал с двумя отличными программистами, которые постоянно в своих IDE держали исходники
чужих проектов на Java, и обращались к ним каждый день. Они делали это не только из желания узнать,
как работают базовые вещи, но и из желания научиться писать хорошие программы. Чтение исходных
кодов известного open source проекта, или кодов, написанных вашим ведущим программистом, может
помочь вам писать код лучше.
И последнее: не сравнивайте себя с другими
Сравнение себя с другими выльется только в плохое самочувствие и нездоровую конкуренцию. У всех
есть свои сильные и слабые стороны. Важнее понять свои сильные и слабые стороны и работать над
ними. Я много раз видел, как даже так называемые fundoo-программисты (программисты с хорошей
фундаментальной подготовкой) делали глупые ошибки. Поэтому проанализируйте и запишите те вещи,
которые вам стоит улучшить в себе, и за работу. Программируйте в удовольствие и наслаждайтесь
этим.
Любой дурак может написать код, понятный компьютеру. Хороший программист пишет код, понятный
человеку.
(c) Мартин Фаулер
Методы программирования
Основными технологиями разработки программного обеспечения являются

Императивное программирование

Структурное программирование

Модульное программирование

Объектно-ориентированное программирование
Императивное программирование
Императивное программирование — это исторически первая методология
программирования, которой пользовался каждый программист,
программирующий на любом из «массовых» языков программирования – 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. Аксиома модульности Коуэна. Модуль — независимая программная
единица, служащая для выполнения некоторой определенной функции
программы и для связи с остальной частью программы. Программная
единица должна удовлетворять следующим условиям:
o
блочность организации, т.е. возможность вызвать программную единицу из
блоков любой степени вложенности;
o
синтаксическая обособленность, т.е. выделение модуля в тексте
синтаксическими элементами;
o
семантическая независимость, т.е. независимость от места, где программная
единица вызвана;
o
общность данных, т.е. наличие собственных данных, сохраняющихся при
каждом обращении;
o
полнота определения, т.е. самостоятельность программной единицы.
3. Сборочное программирование Цейтина. Модули — это программные
кирпичи, из которых строится программа. Существуют три основные
предпосылки к модульному программированию:
o
стремление к выделению независимой единицы программного знания. В
идеальном случае всякая идея (алгоритм) должна быть оформлена в виде
модуля;
o
потребность организационного расчленения крупных разработок;
o
возможность параллельного исполнения модулей (в контексте
параллельного программирования).
Определения модуля и его примеры. Приведем несколько дополнительных
определений модуля.

Модуль — это совокупность команд, к которым можно обратиться по имени.

Модуль — это совокупность операторов программы, имеющая граничные
элементы и идентификатор (возможно агрегатный).
Функциональная спецификация модуля должна включать:

синтаксическую спецификацию его входов, которая должна позволять построить на
используемом языке программирования синтаксически правильное обращение к
нему;

описание семантики функций, выполняемых модулем по каждому из его входов.
Разновидности модулей. Существуют три основные разновидности модулей:
1. "Маленькие" (функциональные) модули, реализующие, как правило, одну
какую-либо определенную функцию. Основным и простейшим модулем
практически во всех языках программирования является процедура или
функция.
2. "Средние" (информационные) модули, реализующие, как правило,
несколько операций или функций над одной и той же структурой данных
(информационным объектом), которая считается неизвестной вне этого
модуля. Примеры "средних" модулей в языках программирования:
o
задачи в языке программирования Ada;
o
кластер в языке программирования CLU;
o
классы в языках программирования C++ и Java.
3. "Большие” (логические) модули, объединяющие набор "средних" или
"маленьких" модулей. Примеры "больших" модулей в языках
программирования:
o
модуль в языке программирования Modula-2;
o
пакеты в языках программирования Ada и Java.
Набор характеристик модуля предложен Майерсом [Майерс 1980]. Он состоит из
следующих конструктивных характеристик:
1. размера модуля;
В модуле должно быть 7 (+/-2) конструкций (например, операторов для
функций или функций для пакета). Это число берется на основе
представлений психологов о среднем оперативном буфере памяти человека.
Символьные образы в человеческом мозгу объединяются в "чанки" —
наборы фактов и связей между ними, запоминаемые и извлекаемые как
единое целое. В каждый момент времени человек может обрабатывать не
более 7 чанков.
Модуль (функция) не должен превышать 60 строк. В результате его можно
поместить на одну страницу распечатки или легко просмотреть на экране
монитора.
2. прочности (связности) модуля;
Существует гипотеза о глобальных данных, утверждающая, что глобальные
данные вредны и опасны. Идея глобальных данных дискредитирует себя так
же, как и идея оператора безусловного перехода goto. Локальность данных
дает возможность легко читать и понимать модули, а также легко удалять их
из программы.
Связность (прочность) модуля — мера независимости его частей. Чем выше
связность модуля — тем лучше, тем больше связей по отношению к
оставшейся части программы он упрятывает в себе. Можно выделить типы
связности, приведенные ниже.
Функциональная связность. Модуль с функциональной связностью
реализует одну какую-либо определенную функцию и не может быть разбит
на 2 модуля с теми же типами связностей.
Последовательная связность. Модуль с такой связностью может быть
разбит на последовательные части, выполняющие независимые функции, но
совместно реализующие единственную функцию. Например, один и тот же
модуль может быть использован сначала для оценки, а затем для обработки
данных.
Информационная (коммуникативная) связность. Модуль с информационной
связностью — это модуль, который выполняет несколько операций или
функций над одной и той же структурой данных (информационным
объектом), которая считается неизвестной вне этого модуля. Эта
информационная связность применяется для реализации абстрактных типов
данных.
Обратим внимание на то, что средства для задания информационно
прочных модулей отсутствовали в ранних языках программирования
(например, FORTRAN и даже в оригинальной версии языка Pascal). И только
позже, в языке программирования Ada, появился пакет — средство задания
информационно прочного модуля.
3. сцепления модуля с другими модулями;
Сцепление — мера относительной независимости модуля от других модулей.
Независимые модули могут быть модифицированы без переделки других
модулей. Чем слабее сцепление модуля, тем лучше. Рассмотрим различные
типы сцепления.
o
Независимые модули — это идеальный случай. Модули ничего не
знают друг о друге. Организовать взаимодействие таких модулей
можно, зная их интерфейс и соответствующим образом перенаправив
выходные данные одного модуля на вход другого. Достичь такого
сцепления сложно, да и не нужно, поскольку сцепление по данным
(параметрическое сцепление) является достаточно хорошим.
o
Сцепление по данным (параметрическое) — это сцепление, когда
данные передаются модулю, как значения его параметров, либо как
результат его обращения к другому модулю для вычисления
некоторой функции. Этот вид сцепления реализуется в языках
программирования при обращении к функциям (процедурам). Две
разновидности этого сцепления определяются характером данным.

Сцепление по простым элементам данных.

Сцепление по структуре данных. В этом случае оба модуля должны
знать о внутренней структуре данных.
4. рутинности (идемпотентность, независимость от предыдущих обращений)
модуля.
Рутинность — это независимость модуля от предыдущих обращений к
нему (от предыстории). Будем называть модуль рутинным, если результат его
работы зависит только от количества переданных параметров (а не от
количества обращений).
Модуль должен быть рутинным в большинстве случаев, но есть и случаи,
когда модуль должен сохранять историю. В выборе степени рутинности
модуля пользуются тремя рекомендациями.
o
В большинстве случаев делаем модуль рутинным, т. е. независимым от
предыдущих обращений.
o
Зависящие от предыстории модули следует использовать только в тех
случаях, когда это необходимо для сцепления по данным.
o
В спецификации зависящего от предыстории модуля должна быть четко
сформулирована эта зависимость, чтобы пользователи имели возможность
прогнозировать поведение такого модуля.
Метод объектно-ориентированного программирования (ООП).
Метод структурного программирования оказался эффективен при написании
программ «ограниченной сложности». Однако с возрастанием сложности
реализуемых программных проектов и, соответственно, объема кода создаваемых
программ, возможности метода структурного программирования оказались
недостаточными.
Основной причиной возникших проблем можно считать то, что в программе не
отражалась непосредственно структура явлений и понятий реального мира и
связей межу ними. При попытке анализа и модификации текста программы
программист вынужден был оперировать искусственными категориями.
Чтобы писать все более сложные программы, необходим был новый подход к
программированию. В итоге были разработаны принципы ОбъектноОриентированного Программирования. OOП аккумулирует лучшие идеи,
воплощённые в структурном программировании, и сочетает их с мощными
новыми концепциями, которые позволяют по-новому организовывать ваши
программы.
Надо сказать, что теоретические основы ООП были заложены еще в 70-х годах
прошлого века, но практическое их воплощение стало возможно лишь в середине
80-х, с появлением соответствующих технических средств.
Методология ООП использует метод объектной декомпозиции, согласно которому
структура системы (статическая составляющая) описывается в терминах объектов и
связей между ними, а поведение системы (динамическая составляющая) - в
терминах обмена сообщениями между объектами. Сообщения могут быть как
реакцией на события, вызываемые как внешними факторами, так и порождаемые
самими объектами.
Объектно-ориентированные программы называют «программами, управляемыми
от событий»,в отличие от традиционных программ, называемых «программам,
управляемыми от данных».
Основные методы и концепции ООП

Метод объектно-ориентированной декомпозиции – заключается в выделении
объектов и связей между ними. Метод поддерживается концепциями
инкапсуляции, наследования и полиморфизма.

Метод абстрактных типов данных – метод, лежащий в основе инкапсуляции.
Поддерживается концепцией абстрактных типов данных.

Метод пересылки сообщений – заключается в описании поведения системы в
терминах обмена сообщениями между объектами. Поддерживается концепцией
сообщения.
Вычислительная модель чистого ООП поддерживает только одну операцию –
посылку сообщения объекту. Сообщения могут иметь параметры, являющиеся
объектами. Само сообщение тоже является объектом.
Объект имеет набор обработчиков сообщений (набор методов). У объекта есть
поля – персональные переменные данного объекта, значениями которых являются
ссылки на другие объекты. В одном из полей объекта хранится ссылка на объектпредок, которому переадресуются все сообщения, не обработанные данным
объектом. Структуры, описывающие обработку и переадресацию сообщений,
обычно выделяются в отдельный объект, называемый классом данного объекта.
Сам объект называется экземпляром указанного класса.
Синтаксис и семантика
В синтаксисе чистых объектно-ориентированных языков все может быть записано
в форме посылки сообщений объектам. Класс в объектно-ориентированных
языках описывает структуру и функционирование множества объектов с
подобными характеристиками, атрибутами и поведением. Объект принадлежит к
некоторому классу и обладает своим собственным внутренним состоянием.
Методы — функциональные свойства, которые можно активизировать.
В объектно-ориентированном программировании определяют три основных
свойства:

Инкапсуляция. Это сокрытие информации и комбинирование данных и
функций (методов) внутри объекта.

Наследование. Построение иерархии порожденных объектов с
возможностью для каждого такого объекта-наследника доступа к коду и
данным всех порождающих объектов-предков. Построение иерархий
является достаточно сложным делом, так как при этом приходится
выполнять классифицирование.
Большинство окружающих нас объектов относится к категориям,
рассмотренным в книге [Шлеер, Меллор 1993]:

o
Реальные объекты – абстракции предметов, существующих в физическом
мире;
o
Роли – абстракции цели или назначения человека, части оборудования или
организации;
o
Инциденты – абстракции чего-то произошедшего или случившегося;
o
Взаимодействия – объекты, получающиеся из отношения между другими
объектами.
Полиморфизм (полиморфизм включения) — присваивание действию одного
имени, которое затем разделяется вверх и вниз по иерархии объектов,
причем каждый объект иерархии выполняет это действие способом,
подходящим именно ему.
У каждого объекта есть ссылка на класс, к которому он относится. При приеме
сообщения объект обращается к классу для обработки данного сообщения.
Сообщение может быть передано вверх по иерархии наследования, если сам класс
не располагает методом для его обработки. Если обработчик событий для
сообщения выбирается динамически, то методы, реализующие обработчиков
событий, принято называть виртуальными.
Естественным средством структурирования в данной методологии являются
классы. Классы определяют, какие поля и методы экземпляра доступны извне, как
обрабатывать отдельные сообщения и т. п. В чистых объектно-ориентированных
языках извне доступны только методы, а доступ к данным объекта возможен
только через его методы.
Взаимодействие задач в данной методологии осуществляется при помощи обмена
сообщениями между объектами, реализующими данные задачи.
Для поддержки концепции ООП были разработаны специальные объектноориентированные языки программирования. Все языки OOП можно разделить на
три группы.

Чистые языки, в наиболее классическом виде поддерживающие объектноориентированную методологию. Такие языки содержат небольшую языковую часть
и существенную библиотеку, а также набор средств поддержки времени
выполнения.

Гибридные языки, которые появились в результате внедрения объектноориентированных конструкций в популярные императивные языки
программирования.

Урезанные языки, которые появились в результате удаления из гибридных языков
наиболее опасных и ненужных с позиций ООП конструкций.
Общие принципы разработки программного обеспечения
Программное обеспечение различается по назначению, выполняемым функциям,
формам реализации. В этом смысле всякое ПО — сложная, достаточно уникальная
программная система. Однако существуют некоторые общие принципы, которые
следует использовать при разработке ПО.

Частотный принцип. Основан на выделении в алгоритмах и в
обрабатываемых структурах групп действий и данных по частоте
использования. Для действий, которые чаще встречаются при работе ПО,
обеспечиваются условия их наиболее быстрого выполнения. К данным, к
которым происходит частое обращение, обеспечивается наиболее быстрый
доступ. «Частые» операции стараются делать более короткими.

Принцип модульности. Под модулем в общем случае понимают
функциональный элемент рассматриваемой системы, имеющий
оформление, законченное и выполненное в пределах требований системы,
и средства сопряжения с подобными элементами или элементами более
высокого уровня данной или другой системы. Способы обособления
составных частей ПО в отдельные модули могут быть существенно
различными. Чаще всего разделение происходит по функциональному
признаку. В значительной степени разделение системы на модули
определяется используемым методом проектирования ПО.

Принцип функциональной избирательности. Этот принцип является
логическим продолжением частотного и модульного принципов и
используется при проектировании ПО, объем которого существенно
превосходит имеющийся объем оперативной памяти. В ПО выделяется
некоторая часть важных модулей, которые постоянно должны быть в
состоянии готовности для эффективной организации вычислительного
процесса. Эту часть в ПО называют ядром или монитором. В состав
монитора, помимо чисто управляющих модулей, должны войти наиболее
часто используемые модули. Программы, входящие в состав монитора,
постоянно хранятся в оперативной памяти. Остальные части ПО
размещаются на внешних запоминающих устройствах и загружаются в
оперативную память только по вызову, перекрывая друг друга при
необходимости.

Принцип генерируемости. Основное положение этого принципа
определяет такой способ исходного представления ПО, который бы
позволял осуществлять настройку на конкретную конфигурацию
технических средств, круг решаемых проблем, условия работы пользователя.

Принцип функциональной избыточности. Этот принцип учитывает
возможность выполнения одной и той же работы (функции) различными
средствами. Особенно важен учет этого принципа при разработке
пользовательского интерфейса для выдачи данных из-за психологических
различий в восприятии информации.

Принцип умолчания. Применяется для облегчения организации связей с
системой как на стадии генерации, так и при работе с уже готовым ПО.
Принцип основан на хранении в системе некоторых базовых описаний
структур, модулей, конфигураций оборудования и данных, заранее
определяющих условия работы с ПО. Эту информацию ПО использует в
качестве заданной, если пользователь забудет или сознательно не
конкретизирует ее.
Общесистемные принципы. При создании и развитии ПО рекомендуется
применять следующие общесистемные принципы:

принцип включения, который предусматривает, что требования к созданию,
функционированию и развитию ПО определяются со стороны более сложной,
включающей его в свой состав системы;

принцип системного единства, который состоит в том, что на всех стадиях
создания,функционирования и развития ПО его целостность будет обеспечиваться
связями между подсистемами, а также функционированием подсистемы
управления;

принцип развития, который предусматривает в ПО возможность его наращивания
и совершенствования компонентов и связей между ними;

принцип комплексности, который заключается в том, что ПО обеспечивает
связность обработки информации как отдельных элементов, так и для всего объема
данных в целом на всех стадиях обработки;

принцип информационного единства, т. е. во всех подсистемах, средствах
обеспечения и компонентах ПО используются единые термины, символы, условные
обозначения и способы представления;

принцип совместимости, который состоит в том, что язык, символы, коды и
средства обеспечения ПО согласованы, обеспечивают совместное
функционирование всех его подсистем и сохраняют открытой структуру системы в
целом;

принцип инвариантности, который предопределяет, что подсистемы и компоненты
ПО инвариантны к обрабатываемой информации, т.е. являются универсальными
или типовыми.
Жизненный цикл программного обеспечения
Понятие "жизненный цикл" предполагает нечто рождающееся, развивающееся и
умирающее. Подобно живому организму программные изделия создаются,
эксплуатируются и развиваются во времени.
Жизненный цикл программного обеспечения включает в себя все этапы его
развития: от возникновения потребности в нем до полного прекращения его
использования вследствие морального старения или потери необходимости
решения соответствующих задач.
Можно выделить несколько фаз существования программного изделия в течение
его жизненного цикла. Общепринятых названий для этих фаз и их числа пока еще
нет. Но и особых разногласий по этому вопросу нет. Поэтому существует несколько
вариантов разбиения жизненного цикла программного обеспечения на этапы.
Вопрос о том, лучше ли данное конкретное разбиение, чем другие, не является
основным. Главное, необходимо правильно организовать разработку
программного обеспечения с их учетом.
По длительности жизненного цикла программные изделия можно разделить на два
класса: с малым и большим временем жизни. Этим классам программ
соответствуют гибкий (мягкий) подход к их созданию и использованию и жесткий
промышленный подход регламентированного проектирования и эксплуатации
программных изделий. В научных организациях и вузах, например, преобладают
разработки программ первого класса, а в проектных и промышленных
организациях — второго.
Программные изделия с малой длительностью эксплуатации создаются в
основном для решения научных и инженерных задач, для получения конкретных
результатов вычислений. Такие программы обычно относительно невелики. Они
разрабатываются одним специалистом или маленькой группой. Главная идея
программы обсуждается одним программистом и конечным пользователем.
Некоторые детали заносятся на бумагу, и проект реализуется в течение нескольких
дней или недель. Они не предназначены для тиражирования и передачи для
последующего использования в другие коллективы. По существу, такие программы
являются частью научно-исследовательской работы и не могут рассматриваться
как отчуждаемые программные изделия.
Их жизненный цикл состоит из длительного интервала системного анализа и
формализации проблемы, значительного этапа проектирования программ и
относительно небольшого времени эксплуатации и получения результатов.
Требования, предъявляемые к функциональным и конструктивным
характеристикам, как правило, не формализуются, отсутствуют оформленные
испытания программ. Показатели их качества контролируются только
разработчиками в соответствии с их неформальными представлениями.
Сопровождение и модификация таких программ не обязательны, и их жизненный
цикл завершается после получения результатов вычислений. Основные затраты в
жизненном цикле таких программ приходятся на этапы системного анализа и
проектирования, которые продолжаются от месяца до 2 лет, в результате чего
жизненный цикл программного изделия редко превышает 3 года.
Программные изделия с большой длительностью эксплуатации создаются для
регулярной обработки информации и управления. Структура таких программ
сложная. Их размеры могут изменяться в широких пределах, однако все они
обладают свойствами познаваемости и возможности модификации в процессе
длительного сопровождения и использования различными специалистами.
Программные изделия этого класса допускают тиражирование, они
сопровождаются документацией как промышленные изделия и представляют
собой отчуждаемые от разработчика программные продукты.
Их проектированием и эксплуатацией занимаются большие коллективы
специалистов, для чего необходима формализация программной системы, а также
формализованные испытания и определение достигнутых показателей качества
конечного продукта. Их жизненный цикл составляет десятки лет. До 90% этого
времени приходится на эксплуатацию и сопровождение. Вследствие массового
тиражирования и длительного сопровождения совокупные затраты в процессе
эксплуатации и сопровождения таких программных изделий значительно
превышают затраты на системный анализ и проектирование.
Все последующее изложение акцентирует внимание на теме разработки крупных
(сложных)программных средств управления и обработки информации.
Обобщенная модель жизненного цикла программного изделия может выглядеть
так:
1. Системный анализ:
o
исследования;
o
анализ осуществимости:
 эксплуатационной;

экономической;

коммерческой.
2. Проектирование программного обеспечения:
o
o
o
конструирование:
 функциональная декомпозиция системы, ее архитектура;

внешнее проектирование программного обеспечения;

проектирование базы данных;

архитектура программного обеспечения;
программирование:
 внутреннее проектирование программного обеспечения;

внешнее проектирование программных модулей;

внутреннее проектирование программных модулей;

кодирование;

отладка программ;

компоновка программ;
отладка программного обеспечения.
3. Оценка (испытания) программного обеспечения.
4. Использование программного обеспечения:
o
эксплуатация;
o
сопровождение.
Системный анализ. В начале разработки программного обеспечения проводят
системный анализ (предварительное его проектирование), в ходе которого
определяются потребность в нем, его назначение и основные функциональные
характеристики. Оцениваются затраты и возможная эффективность применения
будущего программного изделия.
На этом этапе составляется перечень требований, то есть четкое определение того,
что пользователь ожидает от готового продукта. Здесь же осуществляется
постановка целей и задач, ради реализации которых и разрабатывается сам
проект. В фазе системного анализа можно выделить два направления:
исследование и анализ осуществимости.
Исследования начинаются с того момента, когда руководитель разработки
осознает потребность в программном обеспечении.
Работа состоит в планировании и координации действий, необходимых для
подготовки формального рукописного перечня требований к разрабатываемому
программному изделию.
Исследования заканчиваются тогда, когда требования сформированы в таком
виде, что становятся обозримыми и при необходимости могут быть
модифицированы и одобрены ответственным руководителем.
Анализ осуществимости есть техническая часть исследований и начинается тогда,
когда намерение руководства окрепнет настолько, что назначается руководитель
проекта, организующий проектирование и распределение ресурсов (рабочей
силы).
Работа заключается в исследовании предполагаемого программного изделия с
целью получения практической оценки возможности реализации проекта, в
частности определяются:

осуществимость эксплуатационная, будет ли изделие достаточно удобно для
практического использования?

осуществимость экономическая, приемлема ли стоимость разрабатываемого
изделия? Какова эта стоимость? Будет ли изделие экономически эффективным
инструментом в руках пользователя?

осуществимость коммерческая,будет ли изделие привлекательным, пользоваться
спросом, легко устанавливаемым, приспособленным к обслуживанию, простым в
освоении?
Эти и другие вопросы необходимо решать главным образом при рассмотрении
указанных выше требований.
Анализ осуществимости заканчивается, когда все требования собраны и одобрены.
Прежде чем продолжить дальнейшую работу над проектом необходимо
удостовериться, что вся необходимая информация получена. Эта информация
должна быть точной, понятной и осуществимой. Она должна представлять собой
полный комплекс требований удовлетворяющих пользователя к
разрабатываемому программному продукту, оформляемый в виде спецификации.
При несоблюдении данного требования можно значительно замедлить
реализацию проекта в будущем вследствие многократного повторного обращения
к пользователю за уточнением неверно трактованных деталей, неоговоренных
условий и, как следствие, потребуется переделка уже разработанных его частей.
Часто в период системного анализа принимается решение о прекращении
дальнейшей разработки программного обеспечения.
Проектирование программного обеспечения. Проектирование является
основной и решающей фазой жизненного цикла программного обеспечения, во
время которого создается и на 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-приложение (в принципе это частный случай клиент-серверного приложения,
в котором клиенской части просто нет, вместо нее выступает браузер)
Download