Технология

Автор работы: Пользователь скрыл имя, 24 Ноября 2011 в 18:44, курсовая работа

Краткое описание

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

Содержимое работы - 1 файл

курсовая програмирование.docx

— 85.28 Кб (Скачать файл)
 

История возникновения программирования. Основные принципы и  подходы при создании языков программирования. 

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

         Так, программа умножения числа 2 на 2 записывается так.

DVA: dw 02 Назвали ячейку DVA и записали туда 2.
ENT Начало программы.
LDA (DVA) Загрузили DVA в  сумматор (команда 0..).
MULT (DVA) Умножили DVA на сумматор (В..).
IPRT Печать (СОО).
HLT Останов машины (FOO).
  

         Естественно, потребовалась специальная программа, переводящая вышеописанные предложения на родной язык компьютера. Такая программа называется транслятором с языка Ассемблера. 
          Изобретение Ассемблера очень сильно облегчило жизнь программистов. И это не замедлило сказаться: производительность их труда сразу же возросла в 2—3 раза. Но тем не менее работа на машине все равно требовала достаточно высокой профессиональной подготовки. 
          А первые компьютеры работали отнюдь не на программистов. Работали они, в первую очередь, на физиков, рассчитывающих всякие смертоубийственные штучки. И вот представьте себе ситуацию, когда из-за каждой мелочи, которая спокойно и вручную-то считается за несколько суток, физики отрывали программистов от фундаментальных вещей типа разработки программы, играющей в «крестики-нолики»! 
          Такое положение более не могло быть терпимым. Лучшие программистские умы решили до такой степени очеловечить язык общения с компьютерами, чтобы он стал доступен даже физикам. 

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

         В 1953 году Джон Бэкус предложил разработать язык, позволяющий резко упростить программирование модели IBM-704. 
          Естественно, предстояло разработать и транслятор — программу, переводящую предложения (операторы) нового языка в машинные коды. 
          С самого начала группе Бэкуса не очень-то доверяли, поскольку более ранние попытки совершенствования машинного кода, в том числе язык Ассемблера, нередко рекламировались как создание «почти человеческих» средств общения с компьютером. 
          Тем не менее система, названная FORTRAN (FORmula TRANslator — переводчик формул), в апреле 1957 года была готова и позволяла не только «переводить формулы» в машинный язык, но и, самое главное, автоматизировала организацию циклов. Так, небезызвестная вам конструкция

Делать от i:= l до 10 { ... } 
записывается на Фортране «почти по-человечески»: 
DO l I = 1, 10 
1 CONTINUE 
          Здесь единицей отмечен последний оператор цикла, поскольку в заголовке имеется ссылка на эту метку. 

         Успех системы превзошел все ожидания: уже к 1958 году более половины всех машинных команд на компьютерах IBM-704 было получено не вручную, а с помощью транслятора с языка Фортран. 
          Подчеркнем еще раз следующее: Фортран создавался не как принципиально новое средство программирования, а как некий довесочек к конкретной машине, делающий ее более конкурентоспособной на рынке ЭВМ. 
          Несмотря на отдельные недостатки, позволяющие некоторым идеологам программирования считать Фортран «ископаемым интеллектуальным тупиком, цепью наростов, соединенных кусочками синтаксиса, преподавание которого должно быть уголовно наказуемо, как серьезное преступление», язык быстро стал популярен и его адаптировали для машин многих других марок. 
          Адаптация, как вы понимаете, выразилась в том, что были написаны программы-переводчики с Фортрана на язык этих самых других машин, что очень даже непросто. 
          Тем не менее впервые в истории программирования выяснилось, что написать программу по хорошо поставленному заданию (имеющемуся описанию языка Фортран) несравнимо легче и дешевле, чем «писать то, не знаю что». Иными словами, хорошая постановка задачи сама по себе, безо всякого программирования, тоже стала цениться чрезвычайно высоко.  

         Таким образом, Фортран выполнил и другую не менее важную роль: он стал тем средством, с помощью которого самые различные БИ — компьютеры нашли общий язык между собой. 
          Фортран несколько раз улучшался и дополнялся, дожив до конца восьмидесятых годов, когда его почти вытеснили более современные языки. До самого последнего времени он был языком создания программ для ЭВМ Национального управления по аэронавтике и космическим исследованиям США (NASA). Мало того, его варианты до сих пор используются для создания программного обеспечения вычислительного характера для самых мощных компьютеров. 
          Неожиданно обнаружившееся свойство Фортрана быть универсальным языком для различных БИ оторвало идеологов программирования от написания программ для игры в «крестики-нолики» и шахматы, побудив разработать «значительно лучший, универсальный, изящный и замечательный» язык, пригодный на все случаи жизни. Тем более что писать трансляторы для него предстояло совсем другим людям. 

         Несмотря на девиз разработчиков «Лучшее — враг хорошего», разработка нового языка, названного Алгол (ALGOrith-mic Language), заняла более двух лет, и он использовался в основном на Европейском континенте, скорее для того, чтобы подтвердить существование в Европе специалистов по языкам программирования. 
          Получился изящный язык, который Грейс Хоппер (о ней мы еще поговорим) определила так: «Похож на большую поэму: простой и ясный с точки зрения математики, но отнюдь не практичный». 
          Если высоколобые европейские аналитики от программирования поставили во главу угла изящность универсального языка, то фирму IBM, потихоньку становившуюся мировым лидером в производстве компьютеров, обуяла гордыня суперуниверсальности.  

         В 1964 году ею был предложен язык PL/1. (Programming Language One — Язык Программирования НОМЕР ОДИН!) 
          Этот язык очень многие сравнивали со складным ножом со 100 лезвиями и недоумевали, почему в него не встроена кухонная раковина. Фирма надеялась, что язык станет кульминацией всего того, что напридумывали разработчики языков программирования. Так оно и оказалось. В языке на самом деле было множество «изюминок». 
          Тем не менее идея создания суперуниверсальных языков программирования оказалась настолько же плодотворной, как и идея создания телевизора со встроенной стиральной машиной, а большое количество изюма набивало оскомину. 
          Пока шла интеллектуальная грызня за наилучший и универсальнейший из языков программирования, теоретики программирования напоминали футбольные команды, стремящиеся отбить друг у друга единственный мяч — признание их языка самым замечательным и универсальным. 
          Выход между тем был подсказан давным-давно. В детском детективе «Старик Хоттабыч» каждому из игроков дали собственный мячик, с которым он мог забавляться безо всяких помех. 
          Первый такой «мячик» связан с именем бабушки современного программирования Грейс Мюррей Хоппер. 

         Семилетней девочкой Грейс проявила черты суперпрограммиста: она разобрала в доме все будильники, желая узнать, как они устроены, и не смогла собрать их обратно. 
          В разгар второй мировой войны доктор математики Грейс Хоппер вступила в резерв ВМФ США и в июне 1944 года получила офицерское звание. Заметим, что она дослужилась до адмирала, и перейдем к описанию ее достижений в области программирования. 
          Хоппер занималась разработкой программ для машины «Марк-1», о которой мы уже упоминали. По совершенно понятным для вас причинам она пользовалась восьмеричной системой счисления. 
          И вот как-то раз, столкнувшись с тем, что не может разобраться со своим банковским счетом (тяжелейшая проблема для доктора математики), Хоппер обратилась к своему брату-банкиру, который обнаружил, что все вычисления Грейс успешно проделывала в восьмеричной системе. Банк же по старинке считал деньги, пользуясь десятичной системой. 
          С этого момента Хоппер заинтересовалась системами, позволяющими общаться с машиной на более человеческом языке. Плодом ее интересов стали трансляторы А-1, А-2, А-3, В-0 и т. д. Но во всех этих трансляторах входной язык был все же очень далек от человеческого. 

         Целью Грейс Хоппер стала возможность программировать на английском языке. При этом тяжелая история с банковским счетом заставила ее ориентироваться не на физиков или на создание универсального языка программирования, а на такой язык, который бы облегчил в первую очередь экономические расчеты. 
          Не будем подробно описывать все перипетии, связанные с этим проектом. Отметим лишь, что военно-морские связи Хоппер позволили ей заинтересовать языком для деловых приложений военное ведомство США, которое имело свыше тысячи компьютеров всевозможных марок и размеров, использовавшихся в основном именно для экономических расчетов. 
          В итоге в 1959 году был создан комитет по разработке нового языка. В апреле 1960 года он опубликовал его описание, а в конце этого же года несколько фирм уже предлагали трансляторы. 
          COBOL (COmmon Business Oriented Language — универсальный язык, предназначенный для бизнеса) сразу же был оценен деловым миром Америки. Конечно, аристократы от программирования считают его чересчур многословным и громоздким, однако, поскольку в нем используются обычные английские слова, даже администраторы и менеджеры, не знакомые с программированием, без труда понимают программный код. 
          Даже сегодня, в возрасте более четверти века, язык далек от кончины. Стоимость программ, написанных на нем, оценивается в 50 млрд долларов. Он и до сих пор вполне эффективен, если речь идет об обработке деловой информации. На основе Кобола создан вполне современный язык работы с базами данных Clarion.
 

Часть 2 

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

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

  1. Разработка языка.
  2. Определение стандарта языка.
  3. Написание транслятора с языка программирования.
 

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

  • язык для деловых применений (COBOL);
  • язык научно-технических расчетов (FORTRAN);
  • язык обработки таблиц (APL — A Programming Language — язык программирования — скромненькое название);
  • язык программирования металлообрабатывающих станков (APT — Automatically Programmed Tools — автоматически программируемые инструменты);
  • язык, на котором, по мнению специалистов, можно моделировать работу мозга и быстро создавать системы искусственного интеллекта (IPL — Information Processing Languages — языки обработки информации);
  • язык для управления объектами в режиме реального времени и с некоторой претензией на универсальность (ADA);
  • язык «среднего» уровня для системных программистов, позволяющий получать максимально быстро работающие программы, занимающие минимум памяти (С — Си);
  • язык для «критичных» задач, работающих в режиме реального времени, и для бортовых компьютеров (FORTH);
  • язык для обучения программированию (PASCAL);
  • язык программирования для детишек (LOGO);
  • язык для тех, кто не способен изучать программирование, но очень хочет программировать (BASIC);
  • и так далее, и тому подобное...
 

         Достаточно сказать, что только в военном ведомстве США в начале семидесятых годов использовалось до 450 языков высокого уровня и их диалектов (Это, кстати, послужило причиной появления языка ADA с его архаичной претензией на универсальность). 
          В ситуации, когда стоимость компьютеров постоянно падает при резком повышении их мощности, трансляторы обрастают множеством различных «погремушек и свистулек», по выражению Эдсгера Дийкстры — выдающегося теоретика программирования. Эти погремушки и свистульки вроде как должны сделать хорошую жизнь программистов еще краше. Насколько это так — мнения очень существенно расходятся. 
          Говоря о языках программирования, нельзя обойти вниманием идеологию структурного программирования, связанную как раз с именем Дийкстры. Собственно говоря, именно структурное программирование лежит в основе второй главы нашего учебника, а конструкции языка Паркетчика — не что иное, как предложенное Дийкстрой ядро всех современных процедурно-ориентированных структурных языков. 

         Поработав с Паркетчиком, вы, наверное, даже не заметили, что он, в отличие от «Малютки», не допускает передачи управления из любого места программы в любое другое. Отсутствие так называемого оператора GOTO — оператора передачи управления — характерная черта хорошо структурированных программ. 
          Казалось бы, Дийкстра исходил из того, что программам не хватает четкой математической логической структуры — типичная придирка интеллектуального сноба. Однако реализация его идей фирмой IBM при создании банка данных газеты «Нью-Йорк тайме» привела к резкому удешевлению работы и ускорению сроков сдачи комплекса. 
          Можно смело сказать, что чисто умозрительная на первый взгляд идея Дийкстры привела к экономии миллиардов долларов во многих странах. Теперь ни один из языков программирования не имеет права на существование, если на нем нельзя реализовать структурно-ориентированное проектирование сверху вниз. 

         Структурно-ориентированным стал даже Basic в варианте Quick Basic фирмы Microsoft, с вариантом которого вы имели дело, когда писали программы на языке QBasic. 
          Одним словом, как заявил специалист по системному программированию Харлан Миллс: «Не думаю, что полеты космического «челнока» стали бы возможны без применения структурного программирования». 
          Средством обучения структурному программированию и должен был стать язык Pascal. Но по многим причинам он вырос из этих рамок, став одним из основных языков. Конечно, существует мнение, что все ученики и последователи «высокомерного» Никлауса Вирта, придумавшего этот язык, будут великолепно читать комментарии и понимать слова BEGIN и END, но уж никак не способны потянуть на большее... Впрочем, о каком языке не злословили?           Вернемся к «очеловечиванию» языков программирования, описанному в предыдущем параграфе. Оно шло по пути все большего использования слов из человеческого языка и его грамматики, но не покушалось на святая святых — процедурный стиль программирования. Эти слова — процедурный стиль — означают, что компьютер выступает в качестве БИ, которому нужно подробно и точно описать всю последовательность действий (процедур), необходимую для получения результата. Да и сама фоннеймановская архитектура ЭВМ словно бы предполагает рассматривать компьютер как мощный, но глуповатый БИ. 

         Разумеется, теоретики программирования не могли смириться с тем, что компьютеру, словно малолетке, нельзя сказать: «Подумай сам и получи результат, связанный с исходными данными известными тебе соотношениями». 
          Например, если тренер предложил вам построиться по росту, то он вряд ли рассказывает вам алгоритм выполнения задачи. Разве что для младших ребятишек в самый первый раз он пояснит, что тот, кто выше, должен стоять правее (то есть укажет в явном виде связь между исходными данными — вашим ростом и результатом). 
          Представлялось соблазнительным предложить такой язык программирования, на котором достаточно было бы описать исходные данные, указать требуемый результат и связи между ними. А построение решения свести к некоей стандартной процедуре, которая была бы встроена в транслятор с этого языка. 
          В 1972 г. шотландский ученый Р. Ковальский предложил использовать в качестве такого языка модификацию языка математической логики, оперирующего с высказываниями и операциями над ними (о нем мы немного говорили, когда рассматривали сложные условия и «лампочную» арифметику компьютера). Эта идея была практически реализована и обобщена учеными Франции, Шотландии, Португалии, России и других стран. 
          Разработанный язык программирования получил название PROLOG (от французского PROgramming et LOGicque). И он действительно явился прологом в новом направлении, получившем название логического программирования. 
          Логическое программирование основано на том, что компьютер должен решать задачу в свойственной человеку манере. И быть может, в дальнейшем вам придется поближе познакомиться с началами математической логики и принципами работы этого языка.  

         Другой подход, начавший развиваться практически одновременно с логическим программированием, опирался на математическое понятие функции и получил название функционального. 
          Первым языком функционального программирования является язык LISP, созданный в начале 1960-х годов. Слово LISP является аббревиатурой английской фразы LISt Processing. Это достаточно распространенный язык. Он широко используется для решения задач искусственного интеллекта, особенно в США. 
          В настоящее время существуют версии языка LISP для большинства современных компьютеров. Но этот язык не является единственным средством функционального программирования. К подобным языкам относятся и такие, как Miranda, Hope, FP. 
          В начале 80-х годов наметился еще один подход к программированию. На плечи ЭВМ теперь перекладывается и установление связей между исходными данными и результатами. Мы предлагаем компьютеру описание объектов, с которыми ему предстоит работать, а он пусть сам установит, как эти объекты связаны между собой. 

         Такой акцент на описание свойств объектов получил название объектно-ориентированного. К объектно-ориентированным относятся языки C++ и Smalltalk. 
          Но это взгляд с высот сегодняшнего дня. А исторически объектно-ориентированное программирование выросло из расширения возможностей ЭВМ и того круга задач, которые с помощью ЭВМ решаются. 
          Первые языки программирования умели обрабатывать только одно число. Обработка массивов чисел оформлялась циклом. 

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

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

 
 
 
 
 

7.1. Основные понятия 
Модульная программа — это такая программа, в которой любую часть логической структуры можно изменить, не вызывая изменений в остальных частях программы. Однако, что же это на самом деле означает? Каким образом программист узнает, что он написал модульную программу? Важнее понять, каким образом узнает руководитель, что программист написал модульную программу?  
Модуль характеризуют: 
• один вход и один выход — на входе программный модуль получает определенный набор исходных данных, выполняет содержательную обработку и возвращает один набор результатных данных, т.е. реализуется стандартный принцип IPO (Input-Process-Output) — вход-процесс-выход; 
• функциональная завершенность — модуль выполняет перечень регламентированных операций для реализации каждой отдельной функции в полном составе, достаточных для завершения начатой обработки; 
• логическая независимость — результат работы программного модуля зависит только от исходных данных, но не зависит от работы других модулей; 
• слабые информационные связи с другими программными модулями — обмен информацией между модулями должен быть по возможности минимизирован; 
• обозримый по размеру и сложности программный элемент. 
7.2. Свойства модулей 
7.2.1. Размеры модулей в модульной программе 
В своих попытках определить характерные черты модульных программ специалисты предлагают следующие стандарты размеров модуля: 
1. Некоторые пользователи и программисты фирмы IBM предлагают определить модуль как нечто, укладывающееся в 4096 байтах памяти. 
2. Аналогично некоторые, программирующие для систем фирмы Honeywell UNIVAC и других, предлагают считать модулем всякую последовательность кодов, занимающую 512 слов памяти, или 1024 слова, или 2048 слов и т. д. 
3. Некоторые руководители отделов программирования высказывали пожелания, чтобы модуль определялся как раздел программы, который может быть написан и отлажен одним программистом за один месяц. Считая приближенно, что средний программист может написать от 10 до 15 отлаженных команд вдень, получим отсюда размер модуля равным 200—300 программным командам. 
4. Л. Констентайн высказал предположение, что большинство хороших модульных программ не содержит модулей, превышающих 100—200 команд. 
5. Р. Хенри, предположил, что модуль не должен состоять более чем из 20 операторов на языке высокого уровня. У него такое чувство, что если программист записывает что-то с помощью более чем 20 операторов, то скорее всего он программирует не один функциональный элемент, а в этом случае следует употреблять больше одного модуля. 
6. Сотрудник фирмы IBM Бейкер в своей статье (Baker F. Т. «Chief Programmer Team Management of Production Programming», IBM Systems Journal, v. 11, №2 ) рекомендует своим программистам ограничивать размеры модулей 50 операторами на языке ПЛ/1. 
Должно быть ясным, что наложение ограничений на размер программы не гарантирует того, что она будет модульной. Один из программистов проекта для ВВС США сделал интересное замечание по поводу правила о 500 Кобол - операторах в модуле: «Черт с ними, я соглашусь со всеми глупыми ограничениями вроде этого, большинство из них можно довольно просто обойти. Предлагаемое правило модульности слишком просто. Я пишу программу, не обращая никакого внимания на это правило 500 операторов, а затем, если окончательная программа получается длиной в 3000 операторов, я просто делаю: чик! чик! чик! — получаю шесть модулей! Вот и все!». 
С другой стороны, приведенные выше положения не лишены определенного смысла. Один руководитель сказал как-то: «Слушай, я знаю, что модульная программа должна поддаваться разбиению на несколько подпрограмм, но у меня нет времени, чтобы убедиться в том, что программисты обеспечивают это должным образом. Они способны принести мне главную программу, состоящую из 500 операторов, к которой добавлены два-три модуля по 10 операторов, и всю эту чепуху назвать модульной программой! Единственное средство, которое может меня убедить в том, что они правильно представляют себе саму идею модульности и правильно применяют это понятие, состоит в том, чтобы определить жесткий стандарт в 50 операторов на модуль. Если у них появляется необходимость в каком-либо исключении, они сначала должны разъяснить мне его суть». 
7.2.2. Независимость 
Одним из наиболее абстрактных понятий, связанных с модульностью, является понятие независимости: в модульной программе каждый модуль не зависит от других. Под этим, конечно, подразумевается, что его можно изменить или модифицировать, не вызывая каких-либо последствий в других модулях. Должно быть ясным, однако, что понятие независимости не является абсолютным. Нам было бы очень трудно сказать, что модуль Х на 99% или 44% независимее всех других модулей (если бы нам это удалось, то, может быть, в конечном счете мы сказали бы, что программа Z модульна на 99% или 44%). 
С другой стороны, может иметь смысл представление о независимости по отношению к другим факторам, например к базе данных, объему рабочей памяти, другим модулям и т. д. На самом деле мы хотим задать следующий вопрос: если изменить один фактор в программе, то как это повлияет на данный модуль? Так, по-видимому, разумно рассматривать независимость некоторого модуля (и в конечном счете его модульность) по отношению к таким факторам, как: 
1. Логическая структура программы, т. е. алгоритм. Если вся программа (или система) зависит от некоторого специального подхода (например, в задаче обхода сети построение программы зависит от выбора эвристического алгоритма обхода сети), то в скольких модулях потребуется внести изменения при изменении алгоритма? 
2. Аргументы, или параметры, модуля. В этом отношении зависимость модуля может быть довольно сильной: если изменяется число, тип или формат аргументов, то не следует слишком удивляться, если это потребует больших изменений в модуле. 
3. Внутренние переменные таблиц и константы. Многие модули -зависят от общих таблиц (например, от разделов протоколов и сообщений в ряде экономических систем и систем коммутации сообщений); если изменяется структура таких таблиц, то мы можем ожидать, что модули также изменятся. 
4. Структура и формат базы данных. В большей степени эта зависимость аналогична зависимости от общих переменных и таблиц, упомянутой выше, с той разницей, что с практической точки зрения базу данных удобнее считать независимой от программы. 
5. Модульная структура управления программой. Некоторые пишут модуль не особенно задумываясь над тем, каким образом он будет использоваться. Допустим, что, после того как мы записали модуль X, мы вдруг узнаем, что необходимо обеспечить его реентерабельность. Какую часть логической структуры модуля нам придется изменить? Какие бы возникли трудности, если бы вдруг нам захотелось использовать модуль рекурсивно? 
Предполагая эти факторы неизменными, мы могли бы установить независимость отдельных модулей программы. Если интерфейсы между модулями определены, то в этом случае можно было бы заменять модуль функционально ему эквивалентным (т. е. таким, который принимает те же самые входные данные и вырабатывает те же значения выходных данных), не вызывая при этом никаких последствий ни в каком другом модуле программы. В зависимости от того, насколько возможны такие замены, имеет смысл говорить о том, что мы имеем модульную программу. 
Заметим, что определенное таким образом свойство независимости нарушается, если некоторые модули могут произвольно передавать или получать управление от одного к другому или если они могут модифицировать друг друга. Таким образом, следующее условие модульности и составляющая часть независимости — условие «один вход — один выход», т. е. модульная программа, должна состоять из модулей, которые имеют одну точку входа и одну точку выхода. Мы можем несколько ослабить это требование, допуская, быть может, существование модулей с более чем одним входом; важно при этом, что точки входов должны быть строго определены и другие модули не могут входить в данный в произвольной точке. 
7.2. Преимущества и недостатки модульности 
Прежде чем обсудить некоторые методы написания модульных программ, мы должны привести некоторые доводы в пользу модульности, а также ряд контрдоводов. 
7.2.1. Доводы в пользу модульности 
Мы уже упоминали в этой главе достоинства модульности; здесь достаточно было бы просто их повторить. Тем не менее всякому программисту следует подумать о каждом из нижеследующих пунктов в связи с его собственной программой — просто, чтобы убедиться, что они имеют к ней отношение. 
1. Модульные программы легко составлять и отлаживать. Функциональные компоненты такой программы могут быть написаны и отлажены порознь. 
2. Модульную программу легче сопровождать и модифицировать. Функциональные компоненты могут быть изменены, переписаны или заменены без изменений в остальных частях. 
3. Руководству легче управлять разработкой модульной программы. Более сложные модули могут быть переданы более опытным программистам; простые модули могут быть написаны младшими программистами. Разбивая программу на модули, которые могут быть созданы за; один месяц, руководитель может быть уверен, что ни один из программистов не окажется слишком перегруженным сложными элементами программы. 
7.2.2. Доводы против модульности 
Напомнив многие достоинства модульности, мы можем теперь поставить вопрос: почему же многие программы не отличаются большей степенью модульности. С уверенностью можно сказать, что большинство программ пишется немодульными. Многие крупные фирмы расходуют до 50% бюджета, выделенного на обработку данных, на сопровождение, модификацию и совершенствование программ, и большую часть этих действий можно было бы выполнять совершенно тривиально, если бы с самого начала проектировалась модульная программа. 
Большинство программистов не понимают смысла модульности. Вероятно, основная причина недостаточной модульности программ состоит в том, что большинство программистов в действительности не знают, что такое модульность. У них есть интуитивное представление о значении этого слова и методах достижения модульности, но, по-видимому, они не задумывались над формализованными теоремами и алгоритмами. Не многие программисты согласятся, что именно в этом заключена действительная причина недостаточной модульности их программ; они обычно приводят какие-нибудь другие доводы из числа перечисленных ниже. Больше всего озадачивает то, что они представляют себе модульность абсолютным понятием и что, вводя несколько подпрограмм, они смогут ее обеспечить. 
Модульность требует большей дополнительной работы. Чтобы писать модульные программы, программист должен быть значительно более аккуратным на этапе проектирования программной разработки. Он должен проектировать свои программы по нисходящей схеме, начиная с верхних уровней всей программы (системы) и затем продвигаясь вниз к более детальному проектированию отдельных подпрограмм. На каждом шаге он должен спрашивать себя, легко ли будет внести изменения в проект и просто ли его модифицировать. В больших разработках каждый шаг проектирования должен сопровождаться составлением соответствующей документации, с тем чтобы различные пользователи и руководство могли выразить свое понимание и одобрение проводимой работы. 
Все это требует огромного терпения и заметного объема довольно кропотливой и усердной работы, и все это —до начала написания программы. Во многих случаях программист оказывается в условиях (часто им же и созданных!) необходимости писать программу как можно скорее, и он может отказаться от этой дополнительной работы. 
К сожалению, существующие методы решения этой проблемы не вполне удовлетворительны. Руководитель, имеющий дело с непокладистым программистом, может просто приказать ему писать программу модульно. В иных случаях руководитель может попытаться убедить его, показав, что модульность позволит в дальнейшем облегчить изменение программы. Однако часто руководителю очень трудно доказать, что модульный подход определенно облегчит модификации программ; во всяком случае, это может не заинтересовать программиста, поскольку он полагает, что за модификацию программы будет отвечать кто-нибудь другой. 
Модульный подход иногда требует большего времени ЦП. Эта проблема возникает прежде всего в тех случаях, когда программа отличается, наличием большого числа подпрограмм, написанных на языках высокого уровня. Кобол, Фортран и ПЛ/1 упоминаются в этой связи в первую очередь; так, в одной версии ПЛ/1 требуется 198 мкс только на то, чтобы войти в подпрограмму и выйти из нее! Для обеспечения модульности может также потребоваться больше времени ЦП, если части программы с командами ввода-вывода совершенно отделены от ее вычислительных частей; входная запись может быть несколько раз передана подпрограммам, прежде чем начнется ее фактическая обработка. 
Если этот вопрос может оказаться критическим, программисту (или его руководителю) следует сделать оценку, позволяющую установить, действительно ли требование модульности существенно увеличивает время прохождения программы. Жесткие правила, безусловно запрещающие программистам пользоваться операторами вызова подпрограмм (которые часто встречаются в стандартных учебниках Кобола для некоторых малых ЭВМ), вообще говоря, заслуживают порицания. В большинстве случаев модульный под 
ход требует дополнительно 5—10% времени ЦП; представляется, что это приемлемая плата за возможность легко изменять программу; исключение составляют очень специальные случаи (например, некоторые прикладные системы реального времени или программы, расходующие по нескольку часов машинного времени). 
В модульном подходе может потребоваться несколько больший объем памяти. Если каждой подпрограмме отводится отдельная часть рабочей памяти, то всей программе может потребоваться несколько больший объем памяти; однако это не обязательно так, если промежуточные результаты хранятся в списке, располагаемом в памяти магазинного типа (исключение составляют случаи вызова многократно вложенных подпрограмм). В тех случаях, когда программа реализуется на основе большого числа подпрограмм, для обеспечения связей между ними также может потребоваться дополнительная память. 
И в этом случае программисту следует получить надежную оценку того, что модульный подход требует существенно большего объема памяти. В большинстве случаев модульность не приводит к увеличению объема программ более чем на 5—10%. Это не может приводить к каким-либо осложнениям, за исключением случаев, когда на длину программы накладывается произвольное ограничение, или при использовании мини-ЭВМ из-за чисто физических ограничений. 
 
ТЕМА 8. СТРУКТУРНОЕ ПРОГРАММИРОВАНИЕ 
Несмотря на довольно широкое освещение структурного программирования в мировой литературе, этот подход до сих пор остается неизвестным многим программистов. И если они все-таки что-то слышали о нем (обычно в крайне упрощенной форме), то склонны его отвергать как неоправданно ограничительный и нецелесообразный. 
8.1. Основные предпосылки структурного программирования 
Профессор Э. Дейкстра был одним из первых инициаторов структурного программирования. В 1965 г. на конгрессе он высказал предположение, что оператор GO TO мог бы быть исключен из языков программирования. Больше того он заявил, что "квалификация программиста обратно пропорциональна числу операторов GO TO в его программах!" Несмотря на то что конгресс 1965 г. происходил в Нью-Йорке и отличался многочисленностью участников из целого ряда стран, это заявление вызвало сравнительно слабую реакцию. Многие программисты только что отказались от пользования Фортраном II и связывали свои дальнейшие планы с переходом на Фортран IV, одним из краеугольных камней которого является почтенный оператор GO TO.  
Будучи не из тех, с кем можно не посчитаться, Дейкстра повторно изложил свои идеи в письме редактору Communications of ACM в марте 1968 г. Он сформулировал также некоторые из своих идей по нисходящему проектированию систем (которые, по-видимому, развиваются параллельно и в согласии с идеей структурного программирования) в статье, представленной Первому симпозиуму по основам операционных систем,— статье, перепечатанной в последствии в майском номере Communications of ACM за 1968 г.  
Цели, которые преследует Дейкстра во всей своей работе, представляются весьма постоянными. Основным был вопрос: возможно ли повысить на порядок уровень наших способностей в программировании и какие средства (теоретические, организационные и технические) можно было бы использовать в процесс создания программы, чтобы достичь этого уровня. Больше, чем что-либо другое, Дейкстру, по-видимому, волновала проблема доказательства правильности программы для ЭВМ, т. е. разработка математически строгих методов доказательства правильности программ, которые бы устранили необходимость в дорогостоящих, кропотливых и чаще всего недостаточных специальных приемах их испытаний. В той же статье он формулирует это так: 
«...Я сосредоточил свое внимание не на вопросе «как мы доказываем правильность данной программы», а на вопросе «какими должны быть структуры программ, чтобы без чрезмерных усилий мы могли находить- доказательство их правильности, если даже программы оказываются большими?», и как следствие этого на вопросе «как нам составить для данной задачи такую хорошо структурированную программу?». Мое стремление рассматривать лишь такие «хорошо структурированные» программы (как подмножество всех возможных программ) основывается на уверенности в том, что мы можем найти такое подмножество, которое удовлетворяет нашим нуждам программирования, т. е. что для всякой программируемой задачи в этом подмножестве найдутся в достаточном количестве реалистические программные решения». 
На той же самой конференции по методам программного обеспечения, на которой Дейкстра сообщил некоторые из своих идей по структурному программированию, Дж. Арон, фирма IBM, докладывал об эксперименте, известном под названием «проект суперпрограммиста». В этом эксперименте одному программисту, д-ру X. Миллсу, было предложено проделать нечто почти невозможное — выполнить за шесть месяцев работу, представляющую, как оказалось, проект на 30 человеко - лет. Хотя было сказано лишь то, что эксперимент был успешным (д-р Миллс, например, был скрыт от пользователей, чтобы их не смущал тот факт, что две группы программистов работали над одним и тем же проектом!), его результатом явились некоторые новые идеи в организации разработки, проектировании систем и в проектировании программ, прежде всего — идеи нисходящего проектирования.  
Успех этого эксперимента побудил IBM испробовать те же идеи в более крупной разработке — проекте информационно-поисковой системы для газеты Нью-Йорк, Тайме [10]. Так же как и в предыдущем случае, основные усилия в этом проекте были посвящены поиску новых эффективных методов организации и управления разработкой, хотя значительная роль отводилась также структурному программированию и нисходящему программированию. Этот проект был весьма интересен тем, что он отличался сравнительно большим масштабом (около 83 000 операторов исходной программы), практическим характером (реальный заказчик платил реальные деньги за работающую систему) и успешной реализацией (производительность программистов оказалась приблизительно в пять раз выше производительности среднего программиста). 
После успешного завершения проекта для Нью-Йорк Тайме отделение федеральных систем фирмы IBM, в котором впервые зародилась идея использования «суперпрограммиста», начало внедрять свои идеи в других отделениях IBM и в организациях нескольких крупных пользователей продукции фирмы IBM.  
8.2. Цели и задачи структурного программирования 
Структурное программирование представляет собой нечто большее, чем один лишь отказ от оператора GO TO, и мы намерены обсудить это более подробно. Тем не менее изложение этого предмета различным группам безразличных к нему программистов показывает, что сначала важно подчеркнуть позитивные цели этого метода программирования. Мы уже рассмотрели один негативный аспект структурного программирования (по крайней мере, с точки зрения среднего программиста), т. е. исключение GO TO.  
Суть структурного программирования состоит в ряде ограничений и правил программирования, которые обеспечивают соответствие программы очень строгому образцу, исключая тем самым бессистемность, не удобочитаемость и запутанность, которые порождают ошибки и затрудняют тестирование и сопровождение.  
Это также может показаться нежелательным аспектом, заставляющим программиста выразить свое неудовольствие следующими словами: "Еще правила и ограничения в дополнение к стандартам, которые мне уже приходится соблюдать. Программировать стало совсем неинтересно; у меня нет возможности для творчества". В то же время, если мы сможем показать, что вводимые ограничения позволяют программисту удвоить число команд, которые он пишет за день, то это, быть может, вовсе не так уж плохо! 
Основные проблемы, на решение которых направлено структурное программирование. 
8.2.1. Уменьшение трудностей тестирования 
Мотивы, побудившие первоначально Дейкстру к разработке структурного программирования, остаются определяющими и сегодня. Этот подход дает возможность тестировать большие программы и программные системы тщательно и в. полном объеме. Имеющиеся здесь трудности хорошо известны многим программистам: 
1. Трудоемкость и стоимость тестирования больших программ возрастает экспоненциально с увеличением их размеров. Стоимость проверки изменения в большой системе может быть в десятки раз больше стоимости внесения самого изменения. 
2.Ошибки всегда остаются в больших системах, особенно в таких, которые требуют постоянного сопровождения, усовершенствования и других изменений. В каждой новой версии операционной системы имеются сотни ошибок и это число остается относительно неизменным. Изучение операционных систем разных изготовителей подтверждают это явление; число ошибок в разных системах может быть большим или меньшим, но каждый изготовитель, по-видимому, характеризуется своей константой. 
3. Издержки от плохо тестированных программ неуклонно возрастают по мере того, как общество возлагает на электронные вычислительные системы все более и более ответственные функции. В случае больших систем коллективного пользования, всегда есть опасность, что какая-нибудь не выявленная ошибка может быть причиной огромных человеческих жертв, денежных потерь или неуправляемости систем принятия решений (например, систем управления воздушным транспортом). 
Стандартная процедура тестирования заключается в том, что программист выбирает то, что он считает хорошим тестовым случаем, и проводит его испытание. После того как опробовано достаточное количество случаев, программист прекращает эту работу и заявляет, что программа испытана. Но как заметил проф. Дейкстра, «тестирование доказывает наличие ошибок, но не их отсутствие». 
Другой возможный путь спасения лежит в области автоматического доказательства правильности программ. Несколько исследователей изучали проблему автоматического доказательства правильности произвольной программы. В одном из случаев, выполненных Хоором, доказательство правильности программы, содержащей 12 операторов, включает восемнадцать лемм. Более того, все еще сохраняется такое чувство, что число операторов «доказательства» может возрастать с увеличением длины проверяемой программы быстрее, чем по линейному закону. Одной из причин разработки Дейкстрой структурного программирования было то, что автоматическое доказательство программ, отвечающих некоторому структурному образцу, может быть значительно упрощено.  
Вместе с тем программы, написанные на основе принципов структурного программирования с использованием подхода «сверху - вниз», оказываются более простыми в тестировании.  
8.2.2. Более высокая производительность программистов 
Хотя это может казаться очевидным, стоит заметить, что облегчение тестирования обычно повышает производительность программиста, т. е., используя этот подход, программист может написать большее число отлаженных команд программы в день.  
Повышение производительности более существенно, чем может это казаться на первый взгляд. Если каждый программист может сделать вдвое больше, то для выполнения данного проекта требуется лишь половина программистов; часто это позволяет уменьшить число уровней в организационной структуре управления и значительно улучшить психологическую атмосферу и общение между программистами. Среди N программистов отдельный программист чувствует себя как малая спица в большом колесе, среди N12 программистов он может почувствовать себя более важной составляющей и работать с большим упорством и научиться значительно большему в искусстве (а может науке?) программирования. 
8.2.3. Ясность и читабельность программ 
Структурное программирование обладает тем дополнительным преимуществом, что повышает читабельность программ. Поведение многих неструктурированных программ часто ближе к броуновскому движению, чем к сколько-нибудь организованному процессу. Всякая попытка прочесть листинг приводит человека в отчаяние тем, что в такой программе обычно исполняются несколько операторов, после чего управление передается в некоторую точку несколькими страницами ниже, где исполняются еще несколько операторов и управление снова передается в какую-то случайную точку, там используются еще какие-то операторы и т. д. После нескольких таких передач читатель забывает, с чего все началось, и теряет ход мысли.  
Структурированным программам, напротив, свойственна тенденция к последовательной организации и исполнению. Еще в большей степени это справедливо в отношении программ, отвечающих определенным форматам и соглашениям, выделяющим вложенные уровни циклов и операторов IF-THEN.  
8.2.4. Эффективность 
Один из наиболее распространенных доводов против структурного программирования сводится к утверждению, что оно приводит к менее эффективным программам. Повышенное внимание уделяется тому обстоятельству, что использование операторов вызова подпрограмм как альтернативы операторов GO TO увеличивает время ЦП, требуемое для прохождения всей программы, и в ряде случаев связано с необходимостью значительного увеличения памяти. В других ситуациях программисту проще повторить небольшие фрагменты программы (предпочитая этот прием обращению к «общему» разделу программы), дабы не нарушать правил структурного программирования. Хотя обычно это не приводит к увеличению времени ЦП, ясно, что на это требуется дополнительная память. 
В настоящее время признано, что программы, написанные на языках высокого уровня, таких, как Си, Basic, Java потенциально оказываются более эффективными при использовании принципов структурного программирования. В конечном счете эффективность программы, написанной на языке высокого уровня, зависит от качества объектного кода, формируемого компилятором с такого языка (если, конечно, не принимать во внимание эффективность или неэффективность программы, заложенные в ней как внутренние свойства проекта, что часто оказывается значительно важнее рассматриваемых деталей кодирования). Таким образом достигаемый при оптимизации выигрыш значительно превышает все виды не эффективностей, внутренне присущих принципам структурного программирования.  
8.3. Теория и методы структурного программирования 
Итак, понятие структурного программирования представляет собой некоторые принципы написания программ в соответствии с набором жестких правил и имеет целью облегчение процессов тестирования, повышение производительности программистов и улучшение читабельности программ. Прежде всего, к методам структурного программирования относится отказ от оператора GO TO и замена его рядом других более структурированных операторов передачи управления. Сюда относятся также идеи нисходящего проектирования, а также ряд других менее важных ограничений и соглашений, касающихся программирования. 
8.3.1. Теоретические основания структурного программирования 
Известно, что формальные системы теории вычислимости не требуют понятия GO TO. Так, общие рекурсивные функции Клини, системы Поста, алгоритмы Маркова и лямбда-исчисление Чёрча строятся без использования механизма GO ТО. В области, более практической, ряд исследователей пытались найти решение проблемы написания программ таким образом, чтобы их правильность поддавалась доказательству. Эти усилия были приложены в направлении анализа программ, организованных по нисходящей схеме. В этой схеме вся программа (или система) сначала рассматривается как независимый «вызываемый» модуль (и действительно, часто она именно так и функционирует, поскольку всякая прикладная программа вызывается операционной системой как подпрограмма, и при этом она отрабатывает возврат управления операционной системе по окончании или сбою). На следующем этапе проектирования исходная (уровня 0) программа расчленяется на подпрограммы модулей уровня 1; эти последние подвергаются декомпозиции на подмодули уровня 2; процесс декомпозиции продолжается до тех пор, пока проектировщик не придет к настолько малым составляющим блокам, которые могут быть легко закодированы. 
При тестировании всей программы важно иметь возможность определить поведение подмодулей уровня k независимо от конкретных условий на более высоком уровне. Это позволило бы нам доказывать правильность подмодулей уровня (k+1) независимо от смысла, придаваемого им на k-м шаге декомпозиции. Это в свою очередь с необходимостью приводит к требованию, чтобы всякий модуль проектировался с единственным входом и единственным выходом; что опять ведет к представлению о программе как множестве вложенных модулей, каждый из которых имеет один вход и один выход. 
По Бому и Джакопини для построения программы нам требуется три основных составляющих блока: 
1. Функциональный блок. 
2. Конструкция обобщенного цикла. 
3. Конструкция принятия двоичного, или дихотомического, решения. 
Функциональный блок, показанный на рис. 8.1, можно представлять как отдельный вычислительный оператор (или команду в машинном коде) или как любую другую реальную последовательность вычислений с единственным входом, и единственным выходом, как в подпрограмме.  
 
  
Рис. 8.1. Функциональный блок. 
Так, функциональным блоком может быть  
Ø команда «считать в сумматор» в языке ассемблера,  
Ø оператор MOVE в Коболе, или  
Ø типичное вычисляемое выражение в Visual Basic.  
Организация цикла, показанная на рис. 8.2а, в литературе часто упоминается как элемент DO-WHILE. Конструкция принятия двоичного решения показана на рис. 8.2б; по очевидным причинам его часто называют элементом IF-THEN-ELSE. 
Заметим, что конструкции, показанные на рис.8.2а, б, могут сами рассматриваться как функциональные блоки, поскольку они обладают только одним входом и одним выходом. Таким образом, мы можем ввести преобразование операции цикла в функциональный блок, как это показано на рис. 8.3а, и в последующем рассматривать всякий такой оператор цикла эквивалентом (несколько более сложного) функционального блока. Аналогично мы можем ввести преобразование конструкции принятия решения вида, представленного на рис. 8.3б, к функциональному блоку, как это показано на рис. 8.3б. Наконец, мы можем привести всякую последовательность функциональных элементов к одному функциональному элементу, как это показано на рис. 8.3в. 
а) б) 
Рис. 8.2. Две логические конструкции. а — циклическая конструкция; б — конструкция IF-THEN-ELSE. 
Всякая программа, составленная из функциональных блоков, операторов цикла и элементов IF-THEN-ELSE (а в соответствии с результатами Бома и Джакопини программа может быть построена с использованием только этих элементов), поддается последовательному преобразованию, как это иллюстрируется рис. 8.3. а—в, к единственному функциональному блоку. В то же время обратная последовательность преобразований может быть использована в процессе проектирования программы по нисходящей схеме, т. е. исходя из единственного функционального блока, который постепенно раскрывается в сложную структуру основных элементов. 
 
 
Рис. 8.3. Преобразования. 
 
Отметим также взаимосвязь между этой идеей «вложенной структуры» и идеей модульности. Программа, построенная путем применения приведенных выше преобразований, является модульной в строгом смысле слова. Любой из функциональных элементов может быть заменен эквивалентным, не вызывая никаких последствий в остальной части программы. Еще важнее то, что преобразования Бома — Джакопини могут быть применены к исходной программе, разбивая ее тем самым на меньшие модули, которые в свою очередь могут быть разбиты на еще меньшие модули и т. д. В соответствии с этой схемой процесс преобразований может продолжаться до тех пор, пока мы не достигнем уровня «атомарных» модулей т. е. отдельных вычислительных операторов, операторов IF-THEN-EISE и элементарных циклов типа DO-WHILE. 
В этом, таким образом, состоит существенное различие в подходах структурного программирования и модульного программирования. Попытки применить модульное программирование обычно характеризуются хорошим началом: программист решает разбить большую программу на модули. Часто, однако, он не пытается разбить каждый из полученных модулей на меньшие модули. В результате получается программа, содержащая главный модуль и небольшое число больших (например, в несколько сот операторов) модулей первого уровня, которые не легко разделить на составляющие. Отметим важность этого обстоятельства: если модули не поддаются дальнейшему разбиению, то они должны рассматриваться как неделимые структуры, а это означает, что тестирование, отладка, сопровождение или понимание таких модулей будут затруднены в связи с необходимостью целостного восприятия больших блоков программного кода. Ситуация обычно осложняется еще тем, что эти модули, введенные неформально в соответствии с общими представлениями модульного программирования, часто оказываются зависимыми один от другого — они изменяют логику друг друга, пользуются общими областями рабочей памяти и т. д. 
8.3.2. Реализация структурного программирования 
Теоретическая основа структурного программирования допускает реализацию его принципов на многих современных языках программирования. Соответствующие правила очень просты: все операции в программе должны представлять собой либо непосредственно исполняемые в линейном порядке выражения (например, обычные арифметические операторы), либо одну из следующих трех управляющих конструкций: 
1. Вызовы процедур, подпрограмм и функций—любое допустимое обращение к замкнутой подпрограмме с одним входом и одним выходом. Заметим, что подпрограммы не являются абсолютно необходимым условием возможности реализации структурного программирования.  
2. Вложенные на произвольную глубину операторы IF-THEN-ELSE. 
3. Некоторые циклические структуры. Чаще всего в таком качестве используются конструкции DO-LOOP. 
Хотя перечисленных средств достаточно для построения произвольной программы для ЭВМ, в ряде организаций находят удобным использовать дополнительно некоторые их «расширения».  
1. Конструкция CASE. Встречается во многих языках и может быть представлена в нескольких формах 
2. Конструкция цикла FOR-NEXT 
 
Основные вопросы 
1. Программа, предметная (прикладная) область, постановка задачи - 
2. Алгоритм, свойства алгоритм решения задачи 
3. Классификация специалистов занимающиеся разработкой программ 
4. Программный продукт, сопровождение программного продукта  
5. Мобильность, надежность, эффективность, модифицируемость, коммуникативность программных продуктов  
6. Типичные методы структурного проектирования  
7. Этапы создания программных продуктов 
8. Нисходящего проектирования, основное назначение нисходящего проектирования, основные проблемы нисходящего проектирования 
9. Нисходящее кодирование, основные довода в пользу нисходящего кодирования  
10. Идея нисходящего тестирования, схема восходящее тестирование, схема нисходящее тестирование, преимуществом нисходящего тестирования, этапы нисходящего тестирования, достоинства нисходящего тестирования 
11. Метод структурированного разбора, его достоинства, основные правила метода структурированного разбора  
12. Модульное программирование, важнейшие характеристики модуля 
13. Доводы в пользу модульности программ, довода против модульности программ 
14. Структурное программирование, основная идея структурного программирования, основные проблемы, на решение которых направлено структурное программирование, основные трудности тестирования больших программ  
15. Кому важен исход тестирования программы 
16. Случаи получения бесполезных программ 
17. Порочные приемы написания программ, отладка которых вызывает дополнительные трудности 
 
 
ЛИТЕРАТУРА 
1. Иодан Э. Структурное проектирование и конструирование программ. М.: Мир. 1979. 
2. Боэм Б, Браун Дж., Каспар X. и др. Характеристики качества программного обеспечения / Пер. с англ. Е.К. Масловского. - М.: Мир, 1981. 
3. Липаев В.В. Проектирование программных средств. -М.: Высшая школа, 1990. 
4. Майерс Г. Надежность программного обеспечения / Пер. с англ. Ю.Ю.Галимова: Под ред. В.Ш. Кауфмана. - М.: Мир, 1980. 
 
 
 
 

Три основных принципа языков объективно-ориентированного программирования

По Бьерну Страуструпу, автору C++, язык может называться объектно-ориентированным, если в нем  реализованы три концепции: объекты, классы и наследование. Однако теперь принято считать, что такие языки  должны держаться на других трех китах: инкапсуляции, наследовании иполиморфизме. Этот философский сдвиг произошел из-за того, что со временем мы стали понимать: построить объектно-ориентированные системы без инкапсуляции и полиморфизма так же невозможно, как без классов и наследования.  

Инкапсуляция

Как я уже  говорил, инкапсуляция, или утаивание информации (information hiding), — это возможность скрыть внутреннее устройство объекта от его пользователей, предоставив через интерфейс доступ только к тем членам объекта, с которыми клиенту разрешается работать напрямую. Поскольку в том же контексте я говорил также об абстрагировании, то считаю нужным пояснить разницу между этими похожими понятиями. Инкапсуляция подразумевает наличие границы между внешним интерфейсом класса (открытыми членами, видимыми пользователям класса) и деталями его внутренней реализации. Преимущество инкапсуляции для разработчика в том, что он может открыть те члены класса, которые будут оставаться статичными, или неизменяемыми, скрыв внутреннюю организацию класса, более динамичную и в большей степени подверженную изменениям. Как уже говорилось, в С# инкапсуляция достигается путем назначения каждому члену класса своего модификатора доступа — public, private или protected.

Абстрагирование

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

При объявлении классов в объектно-ориентированных  языках вы можете использовать такие  имена и интерфейсы, которые отражают смысл и назначение объектов предметной области. "Удаление" элементов, не связанных напрямую с решением задачи, позволит вам полностью сосредоточиться  на самой задаче и решить ее более  эффективно. Перефразируя высказывание из книги Брюса Эккеля (Вшсе Eckel) "Thinking in Java", можно сказать: в большинстве случаев умение достичь решения проблемы сводится к качеству применяемого абстрагирования.

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

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

При определении  нужной степени абстрагирования  класса важно помнить и о программисте клиентского кода. Представьте, что  вы пишете основное ядро базы данных. Возможно, вы прекрасно разбираетесь в таких  понятиях БД, как курсоры (cursors), управление фиксацией (commitment control) икортежи (tuples). Однако многие разработчики, не столь искушенные в программировании БД, не собираются вникать в тонкости этих понятий. Используя терминологию, непонятную клиентам вашего класса, вы не достигнете основной цели абстрагирования — повысить эффективность работы программиста путем представления предметной области в понятных ему и естественных терминах.

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

ПРИМЕЧАНИЕ Может показаться, что применение объектно-ориентированных технологий главным образом исчерпывается более упрощенным созданием классов. При этом на самом деле достигается некоторый выигрыш в производительности, однако долговременные выгоды вы получите, поняв, что основное назначение ООП в облегчении программирования клиентам классов. При разработке своих классов вы всегда должны ставить себя на место программиста, которому предстоит работать либо с экземплярами этих классов либо с производными от них классами.

О пользе абстрагирования

Наличие в  классах абстрагирования, которое  максимально удобно для программистов, работающих с этими классами, имеет  первостепенное значение при разработке повторно используемого ПО. Если вы выстроите интерфейс, на который не влияют изменения в реализации, то вашему приложению долгое время не понадобятся никакие модификации. Вспомните пример с расчетом зарплаты. При работе с объектом Employee и функциями, обеспечивающими расчет зарплаты, клиенту нужны лишь несколько методов, таких как CalculatePay, GetAddress и GetEmployeeType. Если вы знакомы с предметной областью задачи, вы без труда определите, какие методы понадобятся пользователям класса. Скажем так: если при проектировании класса вам удается сочетать хорошее знание предметной области с прогнозом относительно дальнейших перспектив использования класса, можно гарантировать, что большая часть интерфейса этого класса останется неизменной, даже в случае возможного совершенствования реализации класса. В данном примере для пользователя главным является только класс Employee, в котором, с его точки зрения, от версии к версии лучше бы ничего не менять.

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

Наследование

Наследованием называют возможность при описании класса указывать на его происхождение (kind-of relationship) от другого класса. Наследование позволяет создать новый класс, в основу которого положен существующий. В полученный таким образом класс можно внести свои изменения, а затем создать новые объекты данного типа. Этот механизм лежит в основе создания иерархии классов. После абстрагирования наследование — наиболее значимая часть общего планирования системы. Производным(derived class) называется создаваемый класс, производный от базового(base class). Производный класс наследует все методы базового, позволяя задействовать результаты прежнего труда.

Информация о работе Технология