учебники, программирование, основы, введение в,

 

Программирование от переиспользования

Может показаться странным, что в обзоре стилей мы специально не выделили модульное программирование. Однако очевидно, что требование разрабатывать программы, в которых выделены автономные и, соответственно, легко заменяемые другими фрагменты, решающие логически замкнутые задачи, является общелогическим и общетехнологическим, а не исключительным, присущим какой-либо специальной методике.
Именно это требование обычно трактуется как модульность программного изделия. Преимущества модульности, понимаемой в общем смысле это возможности замены модуля без изменения всего остального и повторного использования программных фрагментов. Последнее уже можно рассматривать как особый стиль программирования, для которого модульность является одним из средств.
Средства поддержки модульности характеризуют даже не стиль, а конкретный язык, поддерживающий этот стиль. Поэтому правомерно говорить не о стиле, а о конкретном языке, в какой мере он поддерживает модульное построение программ в рамках своего стиля.
Что нужно для переиспользования
Стиль от переиспользования характеризуется тем, что при составлении программы стремятся максимально использовать то, что уже сделано - самим программистом, его коллегами или же вообще где-либо. Давно уже общепризнано, что в идеале на смену программированию как кодированию алгоритмов должно прийти программирование как сборка из заранее заготовленных блоков - сборочное программирование. Этот термин введен в 1978 г. Г. С. Цейтин позднее отделил это понятие от теоретических рассмотрений и представил его с программистской стороны. Заметим, что математики уже давно отказались от построения новых теорем (соответствующих в информатике программам) и новых понятий (соответствующих абстрактным типам данных) с пустого места. В математике весьма профессионально переиспользуются ранее полученные результаты. Здесь из-за концептуального единства системы понятий и строгих критериев обоснованности не возникает проблемы совместимости новых версий, которая зачастую губит попытки не только переиспользования, но и просто использования старых программ.
Понятно, что при использовании готовых строительных блоков возможны потери. Тем не менее потенциальная выгода за счет переиспользования просто колоссальна. В математике, где имеются точные оценки, показано, что длину доказательства (читай - программы) можно сократить в БАШНЮ ЭКСПОНЕНТ РАЗ без существенной потери эффективности лишь за счет введения лемм (читай - использования уже построенных программ).
Рассмотрим две реальные ситуации, которые демонстрируют разработку, не ориентированную и ориентированную на переиспользование. В первой ситуации действует программист, работающий с очень развитой системой, в которой есть все. Тем не менее он пишет свою процедуру лексикографического упорядочивания строк, потому что «легче самому написать, чем найти, а потом ведь еще и подгонять придется». Вывод: во-первых, здесь начисто отсутствует стремление к переиспользованию, а во-вторых, переиспользованию могут препятствовать затруднения поиска того, что требуется включить в составляемую программу, а также проблемы совместимости версий.
Вторая ситуация - другая крайность. Программисту на Java потребовалось построить синтаксический анализ. На вопрос о том, как он это делает, получен ответ: «Зачем это знать? У меня есть пакет JavaCC, который все делает, как надо!» Вместе с тем, дальнейшие расспросы показали, что этот программист не представляет себе даже того, какого типа метод анализа поддерживает JavaCC, и, следовательно, ничего не может сказать о том, как задание грамматики для данного пакета связано с эффективностью анализа. Узнав возможные варианты, программист призадумался, но ничего менять не стал. Почему? Ответ простой: «Так ведь все уже работает!» Короче говоря, качество использования готовых компонентов системы зависит от знания о них.
Ситуация опять-таки та же самая, что в математике, особенно в прикладной. Квалифицированное использование теоретических результатов требует знания соответствующей теории, а порою и идей доказательств результатов (поскольку в реальной ситуации предположения теории никогда не выполняются точно). Глубина требуемого знания различна: иногда достаточно общего представления, как, например, при использовании математических функций, иногда нужны сведения о принципах реализации, но всегда можно указать необходимый уровень знакомства с переиспользуемым.
Сравнение ситуаций показывает, что одних пожеланий и директивных указаний для переиспользования мало. Необходимы знания о том, что можно воспользоваться переносимыми компонентами и как именно. Получение же знаний часто требует существенных трудозатрат. Нужно, чтобы само переиспользуемое программное обеспечение было приспособлено для этого, в частности, чтобы оно следовало накопленной в математике хорошей практике, а не игнорировало ее как чистую теорию.
Расшифровка предыдущего предложения позволит вдумчивому читателю самому вывести все условия, необходимые для обеспечения переиспользования в конкретной обстановке. Любые указания здесь бесполезны (если человек еще не осознал предыдущее) либо вредны (если он думает, что осознал, на самом деле ничего не понимая). Можно дать лишь общий совет для тех, кто еще занимается (само)образованием.
В математике для программиста важны не столько конкретные результаты (за ними можно обратиться и к справочной литературе), сколько структура понятий и доказательств, способы введения абстракций и применения исключительно абстрактных понятий в частных ситуациях.
Иногда переиспользованию способствует применение развитых языковых средств. В частности, C++ и Java довольно часто позволяют переносить программы из одной операционной обстановки в другую (перенос - одна из форм переиспользования). Но те, кто реально занимался переносом программ, наверняка вспомнят при чтении предыдущего предложения досадные несоответствия, выявлению и предупреждению которых C++ и Java никак не помогают.
Переиспользованию способствует повышение уровня понятий языка, хотя бы до второго-третьего типа (объектная ориентированность языка). Иногда ООП помогает также возможностями наследования свойств и методов объектов (но часто в самых критических ситуациях плохо продуманная концепция наследования столь же сильно и мешает).
Все это - зародыши поддержки стиля программирования, нацеленного на переиспользование. Но в основном сложившаяся практика в значительной степени препятствует переиспользованию, а уровень систем поддержки еще недостаточно высок и неадекватен общей задаче применения данного стиля.
Переиспользование зависит также от общего уровня знаний и умений программиста (тот, кто способен подняться до уровня метода, склонен к переиспользованию, а тот, кто не может подняться выше тактического планирования, обычно избегает его).
Если ограничиться деятельностью программиста, то, прежде всего, нужно указать на два аспекта данного стиля: применение существующих компонентов и разработка переиспользуемых компонентов.
Применение переиспользуемых компонентов характеризуется следующими особенностями стиля:

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

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

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

http://localhost:3232/img/empty.gifhttp://localhost:3232/img/empty.gifПереиспользование и стили

В характеристиках обоих аспектов программирования от переиспользования нет явного упоминания специфики традиционных моделей вычислений. Поэтому они не зависят от стиля, в котором написаны компоненты, и в значительной степени от особенностей вычислительных систем, на которых они реализуются. Но компоненты и модель вычислений выполняют роль фундамента, на котором базируется надстройка переиспользования. А устойчивость здания и даже его архитектура существенно зависят от качества фундамента.
Рассмотрим ранее представленные стили с точки зрения их приспособленности к сочетанию со стилем переиспользования.
Автоматное программирование явно связано с глобальным для каждой программы понятием набора состояний, и использовать фрагмент программы в отрыве от этого набора, вообще говоря, лишено смысла. Это указывает на естественные рамки переиспользования для данного стиля.
Во-первых, если фрагмент может быть выделен как черный ящик, т. е. нас интересует лишь соотношение между его входными и выходными данными, то он на уровне программы может рассматриваться как самостоятельный узел переработки данных и тем самым получает независимость от состояний программы. В свою очередь, именно это позволяет использовать такой фрагмент, как самостоятельный узел переработки другой программы - переиспользовать его. На этом принципе строятся все библиотечные математические функции, реализацию которых достаточно часто не требуется даже знать при использовании.
Обычными единицами переиспользования при любом стиле программирования являются процедуры. Именно они автономно описываются, и если оказываются независимыми от общего с другими компонентами контекста, то, по сути дела, становятся теми самыми черными ящиками, о которых только что шла речь.
Еще один возможный случай, когда часть, ясно осознанная программистом, состояний внешней программы может быть интерпретирована как гомоморфный образ части (опять же, явно выделенной) состояний компонента (серый ящик). Тогда компонент можно даже модифицировать, т. е. возможно его переиспользование как фрагмента либо шаблона. Однако приемы установления гомоморфизма между состояниями - та высокоуровневая надстройка над автоматным программированием, которая пока еще не создана, и поэтому здесь программист в высшей степени зависит от качества содержательного концептуального анализа.
Как ни странно, немногим лучше приспособлено к сочетанию со стилем переиспользования структурное программирование. Требования к структуре информационного пространства задачи и к согласованию с ним других компонентов программы обеспечивают регламентированные связи между подзадачами, а значит, облегчается (но не исчезает) задача выделения самостоятельных компонентов. В дополнение к полностью самостоятельным переиспользуемым компонентам здесь можно указать на переиспользование, при котором в новом применении обеспечивается необходимая часть контекста (т. е. переиспользуются компонент и эта часть контекста; смотри модули языков Modula-2 и Object Pascal). Модули можно рассматривать как серые ящики для структурного программирования. Возможности модуляризации достаточно хорошо исследованы и корректно реализованы в упомянутых выше языках.
Следует отметить, что для рассмотренных случаев задача переиспользования достаточно часто требует модификации того, что предоставляется. Рассмотрим простейший пример, который не выходит за рамки переиспользования черного ящика. Функция вычисления квадратного корня определена только для неотрицательных аргументов. Вопрос: нужна ли в переиспользуемой подпрограмме вычисления этой функции проверка? С одной стороны, она повышает надежность программирования, но с другой - оказывается избыточной, когда точно известно (можно доказать), что аргумент больше нуля. В рамках традиционной техники простых механизмов отключения проверки быть не может, поскольку все они нарушают принцип черного ящика. В императивных языках подобные многовариантные подпрограммы традиционно трактуются как нечто грубейшим образом неструктурное и даже неавтоматное, как уродство.
Внимание!
Многовариантность как форма серых ящиков соответствует естественному расширению логики - исчислению предикатов с частично упорядоченными кванторами - и может быть корректно добавлена к императивным структурным языкам, а к языкам автоматного программирования она добавляется естественно, и стоит лишь пожалеть об отсутствии в них такой возможности.
При использовании сентенциального стиля возможности перестройки переиспользуемого компонента под ситуацию гораздо выше за счет высокоуровневых принципов вычислений. Однако точных практических оценок этого пока что нет. Как правило, повторно используются крупные и содержательно полные фрагменты программ, чаще всего именно те, которые являют собой базовые механизмы, развивающие модель вычислений языка. Недостаточность опыта разработки больших производственных проектов с существенным использованием этого стиля вынуждает рассуждать о нем предположительно.
Но неимперативность по своей сути лучше приспособлена для переиспользования и, в частности, для шаблонного переиспользования, поскольку соотношения вместо приказов легче автоматически трансформировать или даже просто переинтерпретировать в другой обстановке (отсутствие императивности - еще один фактор, обусловливающий уже не раз упоминавшуюся практически абсолютную переносимость математических результатов). Неудивительно, например, что базы данных-фактов в программах на языке Prolog часто становятся их общими частями.
В событийном программировании заранее предписана декомпозиция программы: выделение в ней уровней генерации событий и их обработки. Уже само это наталкивает на мысль осуществимости общего генератора для однотипных программ. Нельзя забывать и о том, что событийный механизм или управление с помощью приоритетов неизбежно повышает автономность обработчиков: например, они могут не обязательно рассчитывать на определенную последовательность вызовов, и, как следствие, их гибкость повышается.
Сегодня наиболее отлаженными с точки зрения переиспользования являются программы в объектно-ориентированном и в функциональном стиле. Общая причина тому - гибкие средства абстракции соответствующих языков и четкое отделение интерфейсов от реализаций. Это повышает потенциальные и реальные возможности переиспользования. Вместе с тем, скажем, объектно-ориентированный стиль эффективен лишь для достаточно больших систем и тем самым вдохновляет программистов на построение больших систем классов и объектов, которые сильно взаимосвязаны. А это, в свою очередь, заставляет технологизировать разработки. Технология в настоящее время связывается с наработкой типовых моделей фрагментов объектно-ориентированных систем. Создаются шаблоны проектирования (так называемые паттерны), которые предписано использовать, чтобы минимизировать связи в системе, обеспечивать ее развиваемость.
Проектирование с учетом повторного использования результатов в будущем возможно осуществлять на четырех уровнях.

  1. Уровень приложений. В ходе ведения проекта заботятся о том, чтобы при декомпозиции и разработке компонентов системы выявлялись компоненты-кандидаты на переиспользование. Эти компоненты выделяются в самостоятельные единицы и оформляются независимо от проекта.
  2. Уровень спецификаций и документации. В спецификациях четко описываются стоящие за программой призраки, в документации отделяются подпорки от решений и не забывают о призраках.
  3. Уровень инструментов. Разработка проекта практически всегда включает в себя создание инструментальных средств, поддерживающих унификацию: единые библиотеки общедоступных для проекта средств, общий контекст и единообразные средства доступа к нему, средства поддержки выполнения технологических соглашений и регламентов, шаблоны проектирования и др. Этот инструментарий (или часть его) во многих случаях может быть оформлен независимо от проекта для возможного переиспользования в виде библиотек.
  4. Уровень решений. Ценность для переиспользования может представлять архитектурный уровень проекта. Хорошие архитектурные решения, как правило, допускают распространение за рамки конкретного проекта, в котором они появились. Это могут быть фрагменты, которые пригодны для использования в качестве образцов для других проектов, и тогда их переиспользование требует оформления соответствующего шаблона. Другой вариант независимого решения - каркас проекта, т. е. набор взаимосвязанных компонентов, требующих доопределения, в результате чего может быть построено приложение, подсистема, модуль и др. Использование шаблонов или каркасов в другом проекте связывает стиль переиспользования со стилем программирования от образцов.

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

http://localhost:3232/img/empty.gifПрограммирование от образцов

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

  • Дается программа, написанная в каком угодно стиле, в которой нужно кое-что изменить. Точно известно, в каких местах нужно это изменять. В результате получается новая программа. Этот случай часто на профессиональном языке называется патчем программы. Квалифицированные программисты пользуются набором таких образцов, их стали включать и в руководства по ООП.
  • Дан набор программных инструментов, разработанный специалистами, и методика их применения, которая включает в себя схему составления требуемой программы. В идеальном случае применяется содержательно описанный алгоритм, порождающий программу. Такой набор часто называется технологической или инструментальной системой для некоторого класса приложений.
  • Предоставляется среда разработки новой программы, как и в предыдущем случае, созданная заранее специалистами, включающая в себя описания, фиксирующие систему понятий новой программы. Сама программа пишется обычным образом. Это один из распространенных способов работы и профессионалов, и полупрофессионалов, и дилетантов.
  • Программирование от макета. Разработчики быстро готовят прототип, который рассматривается как макет. Макет затем доводится до реального программного изделия. От макета в программной системе часто остается лишь система понятий, сам метод разработки полностью меняется (например, макет был написан на языке Prolog, а окончательная программа - на Java). Макет (особенно в системах, поддерживающих его представление в графической форме, таких как UML) нередко становится частью документации готовой программы.
  • Предоставляется технологический фрейм: нечто, для чего известны слоты, т. е. позиции (пункты, пустые значения того или иного типа, в том числе и процедурного), которые требуется заполнить. В результате должна получиться программа, архитектурная схема которой задана априори. Это, собственно говоря, и есть программирование от образцов в самой чистой форме, которое, в свою очередь, распадается на ряд направлений:
    • семантические сети искусственного интеллекта;
    • фирменная методика и технология, которая погружает один из предшествующих случаев в систему стандартизованных форм и документов. Пример Rational Unified Process (RUP);
    • табличное программирование, примеры которого приведены в данном пособии;
    • компонентное программирование, например, с использованием XML или иного языка разметки (которая задает фрейм) и языка обработчиков разметки (разделение, как говорят на программистском жаргоне, на парсер и обработчик). Это как раз то, что дает объектная модель документа.
  • Предоставляется технический фрейм - то, что нужно заполнять. Он, в отличие от технологического фрейма, совершенно не требует знания логики будущей программы. Это - облегченный и упрощенный вариант предыдущего подхода. Вообще говоря, неясно, программирование ли это, но такой подход очень даже востребован (см., например, язык Forms из Oracle), а потому замалчивать его нельзя, тем более что результат - все равно программа.

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


http://localhost:3232/img/empty.gif

 
На главную | Содержание | < Назад....Вперёд >
С вопросами и предложениями можно обращаться по nicivas@bk.ru. 2013 г.Яндекс.Метрика