Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Стили и методы программирования. Учебное пособие для СПО

.pdf
Скачиваний:
0
Добавлен:
08.09.2026
Размер:
2 Мб
Скачать
☆
степени, чем абстрактнее описание задачи, тем лучше для выработки метода программирования.
Рассмотрим пример. Пусть имеется предприятие с некоторым количеством различных станков, выпускающее детали. Оно получает разнообразные заказы, и в соответствии с приоритетами заказов и технологией обработки деталей нужно динамически строить расписание работы станков. Решив эту задачу и столкнувшись через некоторое время с задачей документооборота в фирме, производящей спецификации большого числа разнообразных лекарств для потребителя и для специалистов, можно усмотреть подобие двух задач. В хорошо организованной бюрократической машине каждый исполнитель работает подобно станку, выполняя свою операцию, точно так же имеется сеть зависимостей (некоторые исполнители могут работать лишь после того, как им подготовили материал другие), и те же методы (а часто даже и почти те же программы) переносятся на планирование бумажного потока.
Таким образом, для систематизации сведений и превращения их в знания необходимо выделять общие характеристики найденных частных решений. Эти общие характеристики превращаются в приемы, а затем, при их осознании и обобщении, в методы.
Метод — сильное оружие, и поэтому наряду с большими достоинствами он обязательно имеет большие недостатки. В частности, применение метода требует достаточно высокого интеллектуального уровня и владения рядом навыков, прививаемых точными науками, в частности, умением переходить к абстрактному от конкретного и обратно. Поскольку подавляющее большинство специалистов не владеют мышлением на таком уровне, чтобы они могли применять метод в чистом виде, метод конкретизируют в методику.
Методика — это набор шаблонов, процедур и рецептов, конкретизирующих метод
3)
. Например, метод многоаспектного моделирования, положенный в основу UML (Unified Modelling language), конкретизирован в методику.
Примером методики служит общая часть RUP (Rational Unified Process). Сам UML лишь поставляет материал для конкретной методики: формы,
Стили и методы программированияНепейвода Н.Н.
281
в которых можно строить систему взаимосвязанных моделей, описывающих разные стороны создаваемой программы, и процедуры отладки таких описаний. Методикой может пользоваться уже
значительное меньшинство
4)
специалистов, поскольку ее применение требует элементарных операций конкретизации и композиции и доступно людям, владеющим комбинационным мышлением. Но для успешного применения в коллективе методика должна конкретизироваться далее.
Тот же самый RUP, помимо методики применения многоаспектных моделей, содержит часть, касающуюся регламента работы программистского коллектива согласно данной методике. Это — технология. В технологии расписываются действия, даются шаблоны документов, определяются роли и функции лиц, задействованных в проекте. Поскольку в программистских коллективах чернорабочий имеет квалификацию техника обычного "железного" производства, программистские технологии, в отличие от индустриальных, оставляют некоторый простор для конкретизации и модификации, давая возможность подставлять в шаблоны целые серии динамически определяемых конкретных действий. На "железном" производстве технология — строго определенная последовательность операций, примерно соответствующая отлаженной программе в программистском мире.
RUP позволил в десятки раз поднять производительность труда программистов при решении задач, с бедным содержанием и навороченным пользовательским интерфейсом. Знакомство с конкретной методикой индустриального программирования должно быть предметом отдельного курса, оно полезно и для тех, кто никогда в жизни не будет пользоваться такой методикой, поскольку может выйти непосредственно на уровень метода. Для них эта методика будет примером конкретизации метода, которую такой человек должен делать, если не желает оставаться непонятым.
Но, кроме методов, есть еще два этажа. Первый из высших этажей начали рассматривать в 70-е гг. XX века. Это методологии
программирования
5)
. Методология на самом деле ортогональна методам: это надстройка на другом крыле программистского здания. В этом случае достаточно сослаться на структурное программирование.
Стили и методы программированияНепейвода Н.Н.
282
Метод реализуется при помощи методологии и в ее рамках.
Второй из высших этажей впервые был систематически исследован в книге [22]
. Он связан с тем, что в программировании мы имеем дело с намного более абстрактными и идеальными понятиями, чем в материальном производстве. Поэтому длина логических построений возрастает, сложность формализации используемых методов убывает, и мы в гораздо большей степени зависим от логики наших рассуждений, чем от "материи".
Логика построения отличается от логики оформления готовых рассуждений, которую преподают на стандартных курсах и на которой основана классическая версия математики. Уже с начала ХХ века в математике появилось конструктивное направление (вариантами которого являются, в частности, интуиционизм и советский конструктивизм), для которого главное не доказательство, а содержащееся в нем идеальное построение. Основатель конструктивного направления Л. Э. Я. Брауэр (Голландия) установил, что причины, по которым математическое доказательство не всегда дает построение, лежат в логике. Так появилось понятие конструктивной логики, в которой мы интересуемся не истинностью формул, а их реализуемостью (возможностью построения решения задачи заданными средствами).
В дальнейшем выяснилось, что разным классам средств и ресурсных ограничений соответствуют разные логики. Эти логики не просто расходятся, а противоречат друг другу. Для того чтобы сохранить хотя бы минимальную внутреннюю согласованность наших рассуждений в ходе построения сложного искусственного объекта (в нашем случае программы), нужно с самого начала определить класс средств и ресурсных ограничений, которые необходимо принимать во внимание.
Таким образом возникают классы способов построения программ, логически противоречащие друг другу и применимые каждый в своей области. Эти классы дают самую общую картину программ, базирующихся на выбранных моделях вычислительной обстановки.
Структурное программирование соответствует взгляду на вычислительную обстановку как на хранилище отдельных значений, связанных отношениями, и взгляду математики XIX века на мир как на
Стили и методы программированияНепейвода Н.Н.
283
то, что может быть описано числовыми характеристиками; роль программы при этом — вычисление новых значений по старым.
Автоматное программирование соответствует рассмотрению действий как изменяющих вычислительную обстановку существенно и глобально, в результате чего то, что было, может оказаться неверным.
Сентенциальное программирование рассматривает вычислительную обстановку как глобальную регулярную структуру значений, которая может проверяться на соответствие регулярно заданным шаблонам и изменяться столь же регулярной подстановкой в нее других подструктур и перекоммутированием связей.
Событийное программирование рассматривает вычислительный мир как нечто изменяющееся во времени, а программу как совокупность активных агентов, посылающих друг другу сообщения через внешний мир и производящих в нем действия, в результате которых внешний мир может активизировать других агентов.
Все эти четыре стиля используют лишь ограниченные (с точки зрения логических формул) фрагменты соответствующих конструктивных логик. Например, в структурном стиле можно считать, что в формуле лишь одна импликация, поскольку все действия преобразуют значения, а не функции.
Функциональное программирование в его нынешней форме соответствует взгляду математики конца ХХ века на мир как на совокупность структур высших порядков и функционалов, производящих на базе одних структур другие. Оно единственное использует полный язык конструктивной логики, в котором реализациями сложных формул являются функционалы.
Таким образом, появилось понятие стиля программирования как совокупности парадигм и методологий, соответствующих одному и тому же классу логических методов, используемых при построении программ.
На первом уровне все стили укладываются в следующий морфологический ящик по координатам действия-условия, локальность­глобальность, выявляющий назначение каждого стиля.
Стили и методы программированияНепейвода Н.Н.
284
Действия Условия Стиль
Локальные Локальные Структурный
Глобальные Локальные Автоматный
Локальные Глобальные Событийный
Глобальные Глобальные Сентенциальный
В каждом стиле имеются ипостаси. Ипостаси — формы, в которых высокоуровневое и абстрактное понятие стиля проявляется в наших конкретных построениях. Ипостаси логически друг другу не
противоречат
6)
, но фактически непримиримо враждуют (порою даже больше, чем разные стили ), если пытаться использовать их вперемежку. Это связано с тем, что ипостаси настроены на разные дисциплины расходования ресурсов.
В структурном стиле имеется две ипостаси: циклическая и рекурсивная. Циклическая ипостась хороша, когда структуры данных статические и не слишком глубокие, и очередной слой информации практически полностью используется следующим слоем. Рекурсивная ипостась хороша, когда структуры данных динамические и глубокие, и на следующем слое нужна лишь малая часть предыдущего слоя, либо слои сами по себе имеют малую ширину.
В автоматном стиле имеется две ипостаси: последовательная и параллельная. Последовательная ипостась хороша, когда система представляется как единый блок информации, а параллельная — когда система делится на относительно автономные взаимодействующие подсистемы.
Заметим, что на автоматном стиле можно проследить, как одни и те же программы можно реализовать разными методами программирования, лежащими внутри одного и того же стиля. Методы реализации близки к технологической дисциплине, они мешают друг другу при произвольном смешении скорее технологически, чем логически или ресурсно. Если программа построена эклектической смесью нескольких методов, ее труднее отлаживать и особенно модифицировать.
В событийном стиле имеются две ипостаси: от событий и от приоритетов. Здесь можно увидеть, что стиль появляется и существует
Стили и методы программированияНепейвода Н.Н.
285
независимо от языковой поддержки и порою даже вопреки ей.
В функциональном стиле ипостаси пока не появились, поскольку он сам по себе не был осознан и все время смешивается с рекурсивной ипостасью структурного программирования. Здесь можно увидеть, как недостаток внимания к интерфейсам между разными стилями приводит к интеграции одного стиля в другой. Даже когда (как в данном случае) они неплохо взаимодействуют, они еще лучше существовали бы по отдельности.
Когда нужно использовать различные стили и как они взаимодействуют?
Опыт автора показал, что в очень многих случаях студенты, которые имеют все исходные данные для того, чтобы решить задачу подстановкой конкретных значений в общее утверждение, тем не менее теряются, не увидев в частном общее.
Пример. После того как была решена задача, можно ли разложить на множители многочлен x2 + (p - 1) над полем вычетов по модулю
p, студенты глубоко задумались над вопросом, можно ли разложить
многочлен x2 + 2 в поле по модулю 3.
В связи с этим дадим некоторые советы по практическому применению общих принципов.
Внимание!
Не воспринимайте то, что дано ниже, как шаблоны! За шаблонами идите по другим адресам.
1. Если у Вас появляется много структурных или (не дай Бог) неструктурных переходов либо все время приходится присваивать значения признакам, а затем их проверять, посмотрите, нельзя ли выделить модуль в автоматном стиле.
2. Если Вы начинаете думать о задаче как о вычислении (не обязательно над числами), пользуйтесь структурным программированием.
Стили и методы программированияНепейвода Н.Н.
286
3. Если Вы намерены переложить понравившуюся Вам нечисленную (например, алгебраическую, топологическую или логическую) теорему в алгоритм для решения Вашей задачи, подумайте о функциональном стиле.
4. Если у Вас каждое следующее значение требует немногих данных, но различные значения обращаются к одним и тем же, даже не пытайтесь программировать рекурсивно, рекурсивное описание можете сохранить в качестве документации к программе, а саму программу пишите в терминах циклов и массивов.
5. Если данные строго подразделяются на ветви, используемые разными последующими значениями, часто лучше писать рекурсивно.
6. Если Вы начинаете смотреть на данные как на активные единицы, которые взаимодействуют между собой, воспользуйтесь объектно­ориентированным программированием.
7. Если в предыдущем случае объекты в том виде, как они предоставляются современными системами программирования,
не подошли, воспользуйтесь каким-либо пакетом
7)
для организации квазипараллельной работы в условном времени и программируйте от событий.
8. Если Вам нужно преобразовывать сложные структурированные тексты в другие тексты, не поленитесь воспользоваться Рефалом (или новым языком сентенциального конкретизационного программирования, если он уже появится к тому времени, когда Вы будете это читать).
9. В частности, если исходные данные или конечный результат имеет формат символьного файла, совершенно неудобоваримого для стандартных процедур ввода/вывода используемой Вами основной системы программирования, пишите препроцессоры или постпроцессоры на Рефале (либо, в крайнем случае, на Perl).
10. Если Вы путаетесь в многочисленных условиях, когда удача либо неудача порою проявляется после нескольких попыток и неясно, куда нужно вернуться, посмотрите, нельзя ли проверку условий
запрограммировать на языке Prolog
8)
(или на новом языке сентенциального унификационного программирования, если он уже появится к тому времени, когда Вы будете это читать).
О сочетании стилей
Стили и методы программированияНепейвода Н.Н.
287
Если программа хотя бы средней величины (перерастает 500 строк на стандартном языке), то наверняка в ней найдутся модули, требующие разных стилей.
Дадим некоторые практические рекомендации по сочетанию стилей.
1. Структурное и автоматное программирование нужно как можно жестче разделять на уровне модулей. Вопрос о языковой совместимости и интерфейсах здесь не возникает, поскольку оба они реализуются стандартными средствами традиционных языков.
2. Две ипостаси структурного программирования также нужно как можно жестче разделять средствами модульности.
3. Параллельная ипостась автоматного программирования в настоящий момент может быть реализована практически только последовательными средствами, воспользуйтесь системой моделирования в условном времени.
4. Событийное программирование стоит выделять в самостоятельные модули либо целиком выносить в Perl­программы.
5. В настоящий момент имеется целый ряд языков, основанных на идеях функционального программирования и продолжающих тенденции языка LISP на более современном уровне. Они имеют интерфейсы с С++ и Java, так что для написания модуля в функциональном стиле внутри большой, в основном традиционной, программы лучше воспользоваться Ocaml или Haskell.
6. Common Lisp обладает достаточно удовлетворительно проработанными интерфейсами с C++. Эти интерфейсы могут эффективно использоваться профессионалами. В отношении же концептуальной целостности Lisp остается несравненным, и, если значительная часть Вашей программы функциональная, стоит пользоваться им.
7. Common Lisp обладает удовлетворительно проработанными интерфейсами с Prolog. Но автор не считает разумным писать программу, соединяя функциональный стиль и унификационную ипостась сентенциального, и будет благодарен за примеры противного, если такие найдутся.
8. Тем не менее указанный в предыдущем пункте интерфейс полезен,
Стили и методы программированияНепейвода Н.Н.
288
если Вы решили использовать задел, накопленный на одном из двух упомянутых языков, в новой программе, написанной на другом. Но будьте готовы к тому, что концептуальные несовместимости могут в некоторый момент оказаться хуже необходимости переписать используемые программы по-своему.
9. Хороших интерфейсов с Рефалом нигде нет. Но, несмотря на это, есть один практический совет. Системы ввода/вывода Common Lisp и Prolog настолько неудобны и морально устарели, что написание пре- и постпроцессоров обработки входных и выходных данных на Рефале (либо Perl) окупается, а в Lisp или Prolog есть смысл пользоваться лишь простейшими возможностями ввода и вывода, принимающими предварительно обработанные и полностью укладывающиеся в структуру языка данные и выдающими их также без учета пользовательского формата.
1)
Мы не употребляем здесь глагол "позволила", поскольку логика не открывала блестящие перспективы, а безжалостно обнажала неустранимые недостатки "квазирелигии прогресса", которая до сих пор остается основой "научного" мировоззрения большинства рационалистически мыслящих людей. Это мировоззрение основывается на слепой вере в прогресс и его блага. Его элементом является убежденность во всесилии (хотя бы в принципе) человеческого разума и возможности в любой ситуации сделать рациональный выбор, основываясь лишь на знании.
2)
Здесь приходится говорить очень жестко, поскольку именно легковесное и самоуверенное полузнание (а то и просто воинствующее невежество, выдающее себя за знание) является раковой опухолью современной цивилизации.
3)
В идеале это так. Но в жизни не всегда методика опирается на метод,
чаще — на традиции.
4)
Здесь термин значительное меньшинство не является опиской: уже
достаточно много, но еще не большинство.
5)
Стоит заметить, что "методология" в программировании не согласуется с употреблением этого термина в обычной жизни и в производстве. Она соответствует парадигме в науке и технологической дисциплине в стандартном производстве.
6)
В словаре сказано о том, что ипостаси противоречат друг другу. Но
Стили и методы программированияНепейвода Н.Н.
289
это противоречие не между логиками, лежащими в основе формализаций, а между конкретными формализациями. В математической теории неформализуемости ипостаси действительно противоречат как теории, но любая математическая теория является абстрагированием. В реальности выявление противоречий между ипостасями требует достаточно тонких формализаций, простые фрагменты которых часто совпадают.
7)
К несчастью, языки семейства SIMULA, поддерживающие
квазипараллельную работу, безнадежно морально устарели.
8)
Но, если остальные преобразования хоть сколько-нибудь не сводятся к подстановке новых значений в хорошо организованные иерархические структуры, не пытайтесь делать на нем основные вычисления и преобразования! Вызов модулей на других языках даже через файлы окупится. Тем более это важно потому, что те, кто слишком рьяно стремятся преодолеть нелогичности "логического программирования", быстро оказываются безнадежно инфицированными Prolog-хакерством, а оно неизлечимо и отупляет.
Стили и методы программированияНепейвода Н.Н.
290