1 основные парадигмы программирования и их особенности. Существуют только структурная и объектная парадигмы программирования

(ОСНОВЫ АЛГОРИТМИЗАЦИИ И ПРОГРАММИРОВАНИЯ)
  • Парадигмы и технологии программирования
    Задачи главы 1. Изучить понятия «парадигма программирования», «технология программирования». 2. Получить общее представление о современных технологиях создания программного обеспечения. 3. Изучить этапы создания структурной программы. 4. Познакомиться с моделями жизненного цикла разработки программного...
  • Парадигмы программирования SE
    SWEBOK включает ряд парадигм программирования См.: Лаврищева Е. М. Парадигмы программирования сборочного типа в программнойинженерии // УКРПрог-2014. № 2-3. С. 121-133. . В его учебные курсы по программированию включены следующие: процедурное программирование (курс CS1011 «Programming fundamentals»),...
    (ПРОГРАММНАЯ ИНЖЕНЕРИЯ И ТЕХНОЛОГИИ ПРОГРАММИРОВАНИЯ СЛОЖНЫХ СИСТЕМ)
  • ПАРАДИГМЫ ПРОГРАММИРОВАНИЯ
    МОДУЛЬНОЕ ПРОГРАММИРОВАНИЕ. БАЗОВЫЕ ПОНЯТИЯ Одна из ключевых проблем современного программирования - повторное использование модулей и компонентов (КПИ). Ими могли быть программы, подпрограммы, алгоритмы, спецификации и т. п., пригодные для использования при разработке новых более сложных ПС....
    (ПРОГРАММНАЯ ИНЖЕНЕРИЯ. ПАРАДИГМЫ, ТЕХНОЛОГИИ И CASE-СРЕДСТВА)
  • Процедурная парадигма
    Процедурная парадигма была хронологически первой и долгое время превалировала. В настоящее время она постепенно уступает свое место объектно-ориентированной парадигме, хотя все еще занимает порядка половины рынка разработки программного обеспечения. Она применяется на всех уровнях разработки программного...
    (АЛГОРИТМИЗАЦИЯ И ПРОГРАММИРОВАНИЕ)
  • Декларативная и процедурная память
    Еще одним самостоятельным, независимым от других способом функциональной организации памяти является ее разделение на декларативную и процедурную. Эти два способа организации памяти имеют вполне понятную функциональную основу. Форма декларативной памяти предназначена для поддержки мыслительных...
    (Психология и педагогика)
  • Рейтинг: / 0
    Подробности Просмотров: 3084

    Парадигмы программирования

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

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

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

    Некоторые представители: Fortran, Pascal, C.

    Директивная программа предписывает, как достичь результата, пошагово описывая действия. Поэтому такое программирование является достаточно легким для понимания.

    В структурном программировании от входных данных полностью зависит последовательность выполнения команд.

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

    Функциональное и логическое программирование

    Представители функциональных языков: List, Haskell.

    Представитель логических языков: Prolog.

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

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

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

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

    Представители объектно-ориентированных языков: С++, Java, Python.

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

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

    В основе ООП лежат такие понятия как наследование, полиморфизм и инкапсуляция.

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

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

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

    Прикладное программирование подчинено проблемной направленности, отражающей компьютеризацию информационных и вычислительных процессов численной обработки, исследованных задолго до появления ЭВМ. Именно здесь быстро проявился явный практический результат. Естественно, в таких областях программирование мало чем отличается от кодирования, для него, как правило, достаточно операторного стиля представления действий. В практике прикладного программирования принято доверять проверенным шаблонам и библиотекам процедур, избегать рискованных экспериментов. Ценится точность и устойчивость научных расчетов. Язык Фортран -- ветеран прикладного программирования, постепенно стал несколько уступать в этой области Паскалю, Си, а на суперкомпьютерах -- языкам параллельного программирования, таким как Sisal.

    Теоретическое программирование придерживается публикационной направленности, нацеленной на сопоставимость результатов научных экспериментов в области программирования и информатики. Программирование пытается выразить свои формальные модели, показать их значимость и фундаментальность. Эти модели унаследовали основные черты родственных математических понятий и утвердились как алгоритмический подход в информатике. Стремление к доказательности построений и оценка их эффективности, правдоподобия, правильности, корректности и других формализуемых отношений на схемах и текстах программ послужили основой структурированного программирования и других методик достижения надежности процесса разработки программ, например грамотное программирование. Стандартные подмножества Алгола и Паскаля, послужившие рабочим материалом для теории программирования, сменились более удобными для экспериментирования аппликативными языками, такими как ML, Miranda, Scheme, Haskell и другие. Теперь к ним присоединяются нововведения в C и Java.

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

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

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

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

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

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

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

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

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

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

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

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

    Ладно. Введение это очень весело, но вы его все равно не читаете, так что кому интересно - добро пожаловать под кат!

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



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

    Это были машинные коды, языки ассемблера и ранние высокоуровневые языки, вроде Fortran.

    Ключевые моменты:

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

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

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

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

    Основные понятия:

    - Инструкция
    - Состояние

    Порожденные понятия:

    - Присваивание
    - Переход
    - Память
    - Указатель

    Как основную:
    - Языки ассемблера
    - Fortran
    - Algol
    - Cobol
    - Pascal
    - C
    - C++
    - Ada
    Как вспомогательную:
    - Python
    - Ruby
    - Java
    - C#
    - PHP
    - Haskell (через монады)

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

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



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

    Основоположниками структурного программирования были такие знаменитые люди как Э. Дейкстра и Н. Вирт.

    Языками-первопроходцами в этой парадигме были Fortran, Algol и B, позже их приемниками стали Pascal и C.

    Ключевые моменты:

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

    В структурном программировании мы по прежнему оперируем состоянием и инструкциями, однако вводится понятие составной инструкции (блока), инструкций ветвления и цикла.

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

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

    Основные понятия:

    - Блок
    - Цикл
    - Ветвление

    Языки поддерживающие данную парадигму:

    Как основную:
    - C
    - Pascal
    - Basic
    Как вспомогательную:
    - C#
    - Java
    - Python
    - Ruby
    - JavaScript

    Поддерживают частично:
    - Некоторые макроассемблеры (через макросы)

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

    Процедурное программирование



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

    Собственно еще раз были введены дополнительные понятия, которые позволили по-новому взглянуть на программирование.

    Этим понятием на этот раз была процедура.

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

    Ключевые моменты:

    Процедура - самостоятельный участок кода, который можно выполнить как одну инструкцию.

    В современном программировании процедура может иметь несколько точек выхода (return в C-подобных языках), несколько точек входа (с помощью yield в Python или статических локальных переменных в C++), иметь аргументы, возвращать значение как результат своего выполнения, быть перегруженной по количеству или типу параметров и много чего еще.

    Основные понятия:

    - Процедура

    Порожденные понятия:

    - Вызов
    - Аргументы
    - Возврат
    - Рекурсия
    - Перегрузка

    Языки поддерживающие данную парадигму:

    Как основную:
    - C
    - C++
    - Pascal
    - Object Pascal
    Как вспомогательную:
    - C#
    - Java
    - Ruby
    - Python
    - JavaScript

    Поддерживают частично:
    - Ранний Basic

    Стоит отметить, что несколько точек входа из всех этих языков поддерживаются только в Python.

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



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

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

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

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

    Ключевые моменты:

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

    Например файл List.mod включающий в себя класс List
    и функции для работы с ним - модуль.

    Папка Geometry, содержащая модули Shape, Rectangle и Triangle - тоже модуль, хоть и некоторые языки разделяют понятие модуля и пакета (в таких языках пакет - набор модулей и/или набор других пакетов).

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

    Основные понятия:

    - Модуль
    - Импортирование

    Порожденные понятия:

    - Пакет
    - Инкапсуляция

    Языки поддерживающие данную парадигму:

    Как основную:
    - Haskell
    - Pascal
    - Python
    Как вспомогательную:
    - Java
    - C#
    - ActionScript 3

    Поддерживают частично:
    - C/C++

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

    Вместо заключения

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

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

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

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

    Единственно прошу, если прочитали по диагонали - комментируйте сдержано.

    Что нам говорит Флойд о парадигмах?

    Термин «парадигма программирования» ввел Роберт Флойд (""R. W. Floyd."" ""Communications of the ACM"", 22(8):455-460, 1979. Русский перевод см. в кн.: Лекции лауреатов премии Тьюринга за первые двадцать лет (1966-1985), М.: МИР, 1993.). Он в своей лекции в 1979 году говорит о следующем:

    Знакомый пример парадигмы программирования - это структурное программирование, которая, кажется, доминирующей парадигмой в методологии программирования. Она разделяется на две фазы. В первой фазе, нисходящего проектирования, проблема разделяется на небольшое количество более простых подпроблем. Это постепенное иерархическое разложение продолжается пока возникнет выделенные подпроблемы, которые достаточно просты, чтобы с ними справиться непосредственно. Вторая фаза парадигмы структурного программирования влечет за собой работу вверх от конкретных объектов и функций к более абстрактным объектам и функциям, используемые всюду в модулях, произведенных нисходящим проектированием. Но парадигма структурного программирования не универсальна. Даже её самые ярые защитники признали бы, что её отдельно недостаточно, чтобы сделать все сложные проблемы легкими. Другие парадигмы высокого уровня более специализированного типа продолжают быть важными. (Это не точный перевод, а авторская компиляция на основе лекции Р. Флойда, но максимально придерживаясь его слов. Формулировки изменены и скомпонованы лишь для выделения основной мысли Р.Флойда и понятного его изложения.)

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

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

    Особенности парадигм более высокого уровня

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

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

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

    Основы парадигмы структурного программирования

    Мы знаем, что идеи о структурном программировании возникли после доклада Э. Дейкстры еще в 1965 году, где он обосновал отказ от оператора GOTO. Именно этот оператор превращал программы в неструктурированные (Спагетти-код), а Дейкстра доказал, что возможно написать программы без использования этого оператора в результате чего программы станут структурными.

    Но одно дело теория, а другое практика. В этом смысле, представляет интерес рассмотреть, какая ситуация была к 1975 году. Это хорошо видно по книге Э. Йодана (). Рассмотреть это важно потому, что сейчас спустя более 30 лет, принципы уже хорошо известные тогда, сейчас переоткрываются, и возводятся в новый ранг. Но при этом теряется исторический контекст, и иерархия важности этих принципов, что первично, а что вторично. Эта ситуация аморфности очень хорошо характеризует сегодняшнее состояние программирования.

    Но что было тогда? Как описывает Йодан, все начинается с ответа на вопрос: «Что значит написать хорошую программу?». Вот первый критерий, на какие вопросы должна отвечать парадигма программирования высокого уровня. Если она не отвечает прямо на этот вопрос, а рассказывает вам как можно получить некоторые интересные характеристики вашей программы, то вы имеете дело с парадигмой низкого уровня - подходом при программировании.

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

    Примечателен, довольно характерный спор программистов:
    * Программист А: “Моя программа в десять раз быстрее вашей, и она занимает в три раза меньше памяти!”
    * Программист Б: “Да, но ваша программа не работает, а моя - работает!”

    Но программы постоянно усложняются и поэтому нам недостаточно того, что программа просто работает. Нужны определенные методы верификации правильности работы программы и самого программиста. Причем это не тестирование программы, а проведение некоторой систематической процедуры проверки именно правильности программы в смысле её внутренней организации. То есть уже тогда, говоря современным языком, говорили о ревизии кода (Code review).

    Кроме того, уже тогда говорили о гибкости программы - о простоте ее изменения, расширения и модификации. Для этого необходимо постоянно отвечать на вопросы определенного вида. “Что будет, если мы захотим расширить эту таблицу?”, “Что произойдет, если однажды мы захотим определить новую программу изменений?”, “А что, если нам придется изменить формат таких-то выходных данных?”, “Что будет, если кто-то решит вводить данные в программу другим способом?”.

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

    Кроме того, центральное внимание уделяли размеру и неизменности модуля. Причем что касается неизменности модуля, то она рассматривалась не целиком, а с выделением отдельных факторов:
    1. Логическая структура программы, т.е. алгоритм. Если вся программа зависит от некоторого специального подхода, то в скольких модулях потребуется внести изменения при изменении алгоритма?
    2. Аргументы, или параметры, модуля. Т.е. изменение спецификации интерфейсов.
    3. Внутренние переменные таблиц и константы. Многие модули зависят от общих таблиц, если изменяется структура таких таблиц, то мы можем ожидать, что модули также изменятся.
    4. Структура и формат базы данных. В большей степени эта зависимость аналогична зависимости от общих переменных и таблиц, упомянутой выше, с той разницей, что с практической точки зрения базу данных удобнее считать независимой от программы.
    5. Модульная структура управления программой. Некоторые пишут модуль не особенно задумываясь над тем, каким образом он будет использоваться. Но если изменились требования. Какую часть логической структуры модуля нам придется изменить?

    Эти и множество других аспектов (которые мы тут не рассмотрели) в целом и формулируют представление о структурном программировании. Забота об этих аспектах и делает структурное программирование парадигмой высокого уровня.

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

    Как мы могли видеть все принципы организации хороших программ рассматриваются в структурном программировании. Появление еще одного или группы неизвестных до этого принципов написания хороших программ могло бы изменить парадигму? Нет. Это всего лишь расширило бы способы и идеологию написания структурированных программ, т.е. парадигму структурного программирования.

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

    Уже признано, что причиной появления объектно-ориентированной парадигмы стала необходимость писать все более и более сложные программы, в то время как парадигма структурного программирования имеет некий предел, после которого развивать программу становится невыносимо сложно. Вот, например, что пишет Г. Шилдт:

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

    Чтобы понять причину, почему именно объектно-ориентированное программирование, позволило писать более сложные программы и практически убрать проблему возникновения предела сложности, обратимся к одному из основоположников ООП - Гради Бучу (). Свое объяснение ООП он начинает с того, что значит сложность и какие системы можно считать сложными. То есть целенаправленно подходит к вопросу написания сложных программ. Далее переходит к вопросу связи сложности и человеческих возможностей понять эту сложность:

    Существует еще одна главная проблема: физическая ограниченность возможностей человека при работе со сложными системами. Когда мы начинаем анализировать сложную программную систему, в ней обнаруживается много составных частей, которые взаимодействуют друг с другом различными способами, причем ни сами части системы, ни способы их взаимодействия не обнаруживают никакого сходства. Это пример неорганизованной сложности. Когда мы начинаем организовывать систему в процессе ее проектирования, необходимо думать сразу о многом. К сожалению, один человек не может следить за всем этим одновременно. Эксперименты психологов, например Миллера, показывают, что максимальное количество структурных единиц информации, за которыми человеческий мозг может одновременно следить, приблизительно равно семи плюс-минус два. Таким образом, мы оказались перед серьезной дилеммой. """Сложность программных систем возрастает, но способность нашего мозга справиться с этой сложностью ограничена. Как же нам выйти из создававшегося затруднительного положения?"""

    Затем он говорит о декомпозиции:

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

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

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

    Теперь, если посмотреть внимательнее, оказывается, что объектно-ориентированная парадигма есть не что иное как моделирование вообще, наиглавнейший аспект которого наиболее четко выразил С. Лем:

    Моделирование - это подражание Природе, учитывающее немногие ее свойства. Почему только немногие? Из-за нашего неумения? Нет. Прежде всего потому, что мы должны защититься от избытка информации. Такой избыток, правда, может означать и ее недоступность. Художник пишет картины, но, хотя мы могли бы с ним поговорить, мы не узнаем, как он создает свои произведения. О том, что происходит в его мозгу, когда он пишет картину, ему самому неизвестно. Информация об этом находится в его голове, но нам она недоступна. Моделируя, следует упрощать: машина, которая может написать весьма скромную картину, рассказала бы нам о материальных, то есть мозговых, основах живописи больше, чем такая совершенная «модель» художника, какой является его брат-близнец. Практика моделирования предполагает учет некоторых переменных и отказ от других. Модель и оригинал были бы тождественны, если бы процессы, происходящие в них, совпадали. Этого не происходит. Результаты развития модели отличаются от действительного развития. На это различие могут влиять три фактора: упрощенность модели по сравнению с оригиналом, свойства модели, чуждые оригиналу, и, наконец, неопределенность самого оригинала. (фрагмент произведения «Сумма технологий», Станислав Лем, 1967)

    Таким образом, С. Лем говорит о абстрагировании как основе моделирования. В тоже время абстрагирование и есть главный признак объектно-ориентированной парадигмы. Г. Буч по этому поводу пишет:

    Разумная классификация, несомненно, - часть любой науки. Михальски и Степп утверждают: «неотъемлемой задачей науки является построение содержательной классификации наблюдаемых объектов или ситуаций. Такая классификация существенно облегчает понимание основной проблемы и дальнейшее развитие научной теории». Почему же классификация так сложна? Мы объясняем это отсутствием «совершенной» классификации, хотя, естественно, одни классификации лучше других. Кумбс, Раффья и Трал утверждают, что «существует столько способов деления мира на объектные системы, сколько ученых принимается за эту задачу». Любая классификация зависит от точки зрения субъекта. Флуд и Кэрсон приводят пример: «Соединенное Королевство… экономисты могут рассматривать как экономический институт, социологи - как общество, защитники окружающей среды - как гибнущий уголок природы, американские туристы - как достопримечательность, советские руководители - как военную угрозу, наконец, наиболее романтичные из нас, британцев - как зеленые луга родины».
    """Поиск и выбор ключевых абстракций.""" Ключевая абстракция - это класс или объект, который входит в словарь проблемной области. """Самая главная ценность ключевых абстракций заключена в том, что они определяют границы нашей проблемы""": выделяют то, что входит в нашу систему и поэтому важно для нас, и устраняют лишнее. Задача выделения таких абстракций специфична для проблемной области. Как утверждает Голдберг, «правильный выбор объектов зависит от назначения приложения и степени детальности обрабатываемой информации».

    Как мы уже отмечали, определение ключевых абстракций включает в себя два процесса: открытие и изобретение. Мы открываем абстракции, слушая специалистов по предметной области: если эксперт про нее говорит, то эта абстракция обычно действительно важна. Изобретая, мы создаем новые классы и объекты, не обязательно являющиеся частью предметной области, но полезные при проектировании или реализации системы. Например, пользователь банкомата говорит «счет, снять, положить»; эти термины - часть словаря предметной области. Разработчик системы использует их, но добавляет свои, такие, как база данных, диспетчер экрана, список, очередь и так далее. Эти ключевые абстракции созданы уже не предметной областью, а проектированием.

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

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

    upd. Я тут подумал, тема острая, я комментировать не буду. Факты я изложил в статье, а скатываться на уровень холивара не хочу. Если это не помогло задуматься - ну, что ж значит не повезло в этот раз. Действительно, будет конструктивно - если напишите контрдоводы в отдельной статье. Я же не берусь разрушать массовые стереотипы.

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

    Теги: Добавить метки