Концепции современного программирования
Цель лекции: Показать, что различные концепции программирования имеют существенно различную эффективность и что для современных численных методов необходимы современные парадигмы программирования.
Изначально программирование возникло как математическая дисциплина и первым применением новой области знания стало решение естественно-научных задач. Не будет большой ошибкой сказать, что первые программируемые вычислительные машины возникли в ходе создания ядерного оружия в США и СССР. При этом первыми программистами были математики, физики, электронщики. На первоначальном этапе программирование было наукой, искусством, развлечением, но никак не ремеслом. Основными проблемами, стоявшими перед программистами были мизерные (по современным меркам) объемы оперативной памяти и низкое быстродействие компьютеров того времени. С расширением сферы программирования стали возникать проблемы с организацией процесса программирования. После того как программное обеспечение стало программным продуктом, то вопросы технологии программирования стали выходить на первый план.
Мы рассмотрим историю различных парадигм программирования. Первоначально программирование начиналось без каких-либо единых подходов, поскольку программы писались в машинных кодах и программирование сильно зависело от конкретной машины. Однако с развитием вычислительной техники и с построением серийных компьютеров возникли универсальные языки программирования высокого уровня. Первым известным языком программирования был язык Фортран. Этот язык, название которого является сокращением от "транслатор формул", представлял собой уже в определенном смысле новую парадигму программирования, которую мы назовем парадигмой языков высокого уровня. Программирование на языке Фортран представляет собой написание программ с использованием формализованных команд, которые в определенном смысле можно считать математическим текстом. Конечно, для многих классических математиков некоторые команды вызывали удивление и непонимание. Например команда присвоения:
вызывала недоумение. Забегая вперед, заметим, что Н. Вирт при создании языка Паскаль операция присвоения обозначена через "$:=$". Тем не менее с языка Фортран началось алгоритмическое программирование, когда появился более-менее стандартный язык для написания программ. При этом стало возможным писать программы "впрок", когда при создании программ можно было использовать ранее созданные тексты программ. Многие часто используемые подпрограммы стали оформляться в виде специальных библиотек. С дальнейшим развитием компьютерной техники и расширением сферы программирования возникла следующая парадигма программирования, которую мы назовем "парадигма процедурного программирования". Процедурное программирование основано на том, что при создании больших программных продуктов необходимо разбивать всю программу на отдельные подпрограммы (процедуры) и использовать написанные ранее подпрограммы, оформленные в виде подключаемых библиотек. Именно с этого момента стало возможным создания сложных программных комплексов. Если первоначально программы писались одним программистом в каждый момент времени, то при создании сложных программных комплексов необходимо привлекать целый коллектив программистов, которые работали одновременно. Это потребовало изменений в организацию процесса программирования. Процедурное программирование позволяло разделить процесс программирования на отдельные задачи, которые можно было выполнять одновременно разными программистами. При этом вместо написания большой программы, создавались относительно небольшие подпрограммы, что позволяло значительно легче отлаживать программы и модернизировать ранее созданные комплексы.
Если на начальном этапе развития программирования схема создания программы была следующая:
- Построение алгоритма и его формальное описание
- Программирование (кодирование алгоритма)
- Отладка программы
- Использование программы
то с развитием процедурного подхода появился еще один важный этап - проектирование. С появлением этапа проектирования программы становятся программными комплексами. Развитое процедурное программирование становится новой парадигмой программирования, которая называется парадигмой структурного программирования.
Под структурным программированием мы будем понимать такой метод разработки программ, который начинается с тщательного проектирования структуры программы методом "сверху вниз". При этом каждый блок должен быть спроектирован таким образом, чтобы его создание и отладка могла быть автономной. Часто под структурным программированием понимают и использование "правил хорошего тона", например исключение оператором GOTO, а также подробное документирование текстов программ. Идеология структурного программирование имеет своих основных авторов, к которым относятся такие известные ученые, как Э.В.Дейкстра и Н.Вирт.
Следующая парадигма программирования - "парадигма объектно-ориентированного программирования" Конечно, слово "следующая" нужно понимать не в хронологическом порядке, а по распространению. Поскольку, если структурное программирование было создано в 70-х годах, то объектно-ориентированное программирование появилось в языке Simula, в 1967 году. Основы объектно-ориентированного программирования в языке мы рассмотрим подробно в нашем курсе в других главах. Объектно-ориентированное программирование явилось новым шагом в технологии программирования и позволило создавать сложные программные комплексы. В настоящее время подход объектно-ориентированного программирования является стандартом для промышленного программирования. Заметим, что, как правило, для реализации объектно-ориентированного подхода необходима соответствующая поддержка со стороны языковых конструкций. К числу наиболее распространенных объектно-ориентированнных языков относятся:
- C++
- Java
- C#
- Delphi
- VB.NET
- Perl
- PHP
- и многие другие.
Заметим, что объектно-ориентированный подход оказал большое влияние на архитектуру операционной системы Microsoft Windows. Хотя в начале основным средством для программирования под Windows был язык C, понятие "окна" в Windows вполне соответствовало понятию объекта или класса.
Любая программа - это обработка данных, однако часто программы создаются для обработки не просто данных, а данных описывающих те или иные объекты (персоны, машины, математические функции и т.д.). Понятие "объекта" включает в себя некоторую информацию, описывающую состояние объекта (при этом эта информация может быть видимой извне объекта, а может быть и невидимой изнутри объекта). Далее объект может уметь выполнять определенные команды, которые поступают из внешней среды по строго установленному интерфейсу. В объектно-ориентированном программировании это называется инкапсуляцией. Еще процедурное программирование учит, что необходимо минимизировать повторяющиеся куски программы. Необходимо писать такие программы, которые будут удовлетворять требованиям повторного использования кода. В объектно-ориентированном программировании для этого используются механизмы наследования и полиморфизма.
Хотя объектов может быть довольно много, но часто рассматриваемые объекты имеют общие черты. Для того, чтобы не создавать повторяющегося кода при описании объектов, был придуман механизм наследования. Допустим, что мы моделируем транспортную сеть, в которая состоит из автобусов, маршрутных такси, трамваев и троллейбусов. Соответственно, у нас должно быть четыре объекта: автобус, такси, трамвай и троллейбус. Каждый объект характеризуется рядом показателей: положение, скорость, количество пассажиров, возможность проехать по выбранному маршруту и т.д. Ясно, что некоторые характеристики присущи всем четырем объектам, далее часть характеристик (возможность проехать по данной улице) присущи только трамваю и троллейбусу потому, что для этого транспорта необходимо заранее проложить провода, а для трамвая и рельсы. Покажем, как такие объекты следует реализовать на основе объектно-ориентированного подхода. Первым делом введем объект "транспортное средство", все наши объекты будут наследниками от этого объекта. Далее введем объекты: "автотранспорт" и "электротранспорт". Эти объекты будут наследниками от объекта "транспортное средство". Наконец, введем объекты: "автобус", "такси", "трамвай" и "троллейбус". При этом первые два объекта наследники от объекта "автотранспорт", а последние два от объекта "электротранспорт". При наследовании объекта нам автоматически будет доступен код, который мы создавали для родительского класса. Технология полиморфизма позволяет, грубо говоря, модифицировать созданный ранее код для наследуемого класса. В следующей главе мы подробно рассмотрим объектно-ориентированное программирование на примере языка C\#.
А что после объектно-ориентированного программирования? В последнее время набирает популярность логическое и функциональное программирование. Функциональное программирование довольно радикально отличается от рассмотренных ранее парадигм программирования. Если рассматриваемые до этого парадигмы программирования были основаны на императивном программировании, то есть пошаговым заданием операций, то функциональное программирование основано на вычислениях функций. В программах, написанных с помощью функционального программирования, нет состояния программы в каждый момент времени. Считается, что функциональное программирование намного ближе к математическому мышлению. Действительно, функциональное программирование основано на -исчислении, известном в математической логике, посвященной теории вычислимости. Однако, большинство численных методов, применяемых на практике основаны на итерационных процедурах, поэтому пока функциональное программирование находит лишь ограниченное применение в вычислительной математике. Среди языков функционального программирования отметим:
- LISP
- Haskell
- ML
- F#
Интересно, что язык LISP был разработан в 1958 году, что раньше таких известных процедурных языков, как ALGOL-60, Pascal, C... Однако именно сейчас увеличился интерес к функциональным языкам программирования, что подтверждается поддержкой языка F# в Microsoft Visual Studio 2010. Наконец отметим, что по выражению Дмитрия Сошникова функциональное программирование позволяет больше думать и меньше писать.
Ключевые термины
Парадигма объектно-ориентированного программирования - использование объектов и классов, а также технологий инкапсуляции, наследования и полиморфизма.
Парадигма процедурного программирования - систематическое использование отдельных небольших модулей (подпрограмм или процедур.
Парадигма структурного программирования - метод разработки программ "сверху вниз", использование специальных правил хорошего тона при программировании.
Парадигма языков высокого уровня - использование машинно-независимых языков программирования, запись программ с помощью операторов языка программирования а не машинных команд.
Краткие итоги: Различные парадигмы программирования сменяются со временем, следуя за прогрессом вычислительной техники. Популярность парадигм программирования не всегда совпадает со временем появления на свет.