Руководство по стилю программирования и конструированию по
Скачать 7.6 Mb.
|
ГЛАВА 4 Основные решения, которые приходится принимать при конструировании 59 4.1. Выбор языка программирования Избавляя разум от всей ненужной работы, хорошая нотация позволяет со- средоточиться на более сложных проблемах и в конечном счете повышает интеллект человечества. До появления арабской нотации умножение было весьма сложным, а деление даже целых чисел требовало усилий ведущих ма- тематиков. Возможно, ничто в современном мире не смогло бы удивить гре- ческого математика сильнее, чем то, что большинство европейцев умеют делить крупные числа. Это показалось бы ему абсолютно невозможным… Легкость выполнения операций над десятичными дробями — почти сверхъ- естественный результат постепенного обнаружения отличной нотации. Альфред Норт Уайтхед (Alfred North Whitehead) Язык программирования, на котором будет реализована система, заслуживает большого внимания, так как вы будете погружены в него с начала конструирова- ния программы до самого конца. Исследования показали, что выбор языка программирования несколькими спо- собами влияет на производительность труда программистов и качество создава- емого ими кода. Если язык хорошо знаком программистам, они работают более производительно. Данные, полученные при помощи модели оценки Cocomo II, показывают, что про- граммисты, использующие язык, с которым они работали три года или более, при- мерно на 30% более продуктивны, чем программисты, обладающие аналогичным опытом, но для которых язык является новым (Boehm et al., 2000). В более раннем исследовании, проведенном в IBM, было обнаружено, что программисты, облада- ющие богатым опытом использования языка программирования, были более чем втрое производительнее программистов, имеющих минимальный опыт (Walston and Felix, 1977). (Различия результатов двух исследований объясняются тем, что в мо- дели Cocomo II более тщательно изолируется влияние отдельных факторов.) Программмисты, использующие языки высокого уровня, достигают бо- лее высокой производительности и создают более качественный код, чем программисты, работающие с языками низкого уровня. Утверждается, что при работе с такими языками, как C++, Java, Smalltalk и Visual Basic, производи- тельность труда программистов, а также надежность, простота и понятность про- грамм в 5–15 раз выше, чем при использовании низкоуровневых языков, таких как ассемблер и C (Brooks, 1987; Jones, 1998; Boehm, 2000). Избавившись от необ- ходимости проводить праздничную церемонию каждый раз, когда оператор язы- ка C делает то, что было задумано, вы сэкономите время. Более того, высокоуров- невые языки выразительнее низкоуровневых. Каждая строка кода выполняет боль- ший объем работы. В табл. 4-1 указано типичное отношение функциональности команд некоторых языков к функциональности операторов языка C. Показатель, превышающий 1, означает, что строка кода на указанном языке выполняет боль- ше работы, чем строка кода на C. 6 0 ЧАСТЬ I Основы разработки ПО Табл. 4-1. Сравнение функциональности операторов высокоуровневых языков с функциональностью операторов C Язык Функциональность операторов в сравнении с языком C C 1 C++ 2,5 Fortran 95 2 Java 2,5 Perl 6 Python 6 Smalltalk 6 Microsoft Visual Basic 4,5 Источники: «Estimating Software Costs» (Jones, 1998), «Software Cost Estimation with Cocomo II» (Boehm, 2000) и «An Empirical Comparison of Seven Programming Languages» (Prechelt, 2000). Некоторые языки лучше выражают концепции программирования, чем другие. Здесь уместно провести параллель между естественными языками — скажем, ан- глийским — и языками программирования, такими как Java и C++. Изучая есте- ственные языки, лингвисты Сапир и Уорф (Sapir and Whorf) высказали предполо- жение, что способность к размышлению над определенными идеями связана с выразительной силой языка. Согласно гипотезе Сапира-Уорфа способность чело- века к обдумыванию определенных мыслей зависит от знания слов, при помощи которых можно выразить эту мысль. Если вы не знаете слов, то не сможете выра- зить мысль и, возможно, даже сформулировать ее (Whorf, 1956). Программисты испытывают аналогичное влияние языков программирования. «Слова», которые язык предоставляет программисту для выражения мыслей, не- сомненно, влияют на способ их выражения, а возможно, даже определяют, какие мысли можно выразить на данном языке. За доказательствами влияния, оказываемого языками программирования на мыш- ление программистов, далеко ходить не надо. Типичная история такова: «Мы пи- сали новую систему на C++, но большинство наших программистов не имели особого опыта работы на C++. Раньше они использовали Fortran. Они писали код, который компилировался на C++, но на самом деле это был замаскированный Fortran. В итоге они заставили C++ эмулировать недостатки языка Fortran (такие как операторы goto и глобальные данные) и проигнорировали богатый набор объектно-ориентированных возможностей C++». Данный феномен наблюдается в отрасли уже много лет (Hanson, 1984; Yourdon, 1986a). Описания языков История разработки некоторых языков и их общие возможности довольно инте- ресны. Ниже приведены описания языков, наиболее популярных в настоящее время. Ada Высокоуровневый язык общего назначения, основанный на языке Pascal. Разра- ботанный под патронажем Минобороны США, он особенно хорошо подходит для ГЛАВА 4 Основные решения, которые приходится принимать при конструировании 61 создания встроенных систем и систем, работающих в реальном времени. В языке Ada особое внимание уделяется абстракции данных и сокрытию информации, а также проводится различие между открытыми и закрытыми частями каждого класса и пакета. Название «Ada» было присвоено языку в честь Ады Лавлейс (Ada Lovelace) — женщины-математика, которую считают первым программистом в мире. Сегодня язык Ada используется преимущественно для разработки военных, космических и авиационных систем. Ассемблер Низкоуровневый язык, каждая команда которого соответствует одной команде компьютера. Вследствие этого ассемблер специфичен для отдельных процессо- ров — например, для конкретных процессоров Intel или Motorola. Ассемблер счи- тается языком второго поколения. Большинство программистов избегают его и используют, только если к быстродействию или компактности кода программы предъявляются повышенные требования. C Среднеуровневый язык общего назначения, первоначально тесно связанный с ОС UNIX. Некоторые свойства (структурированные данные, структурированная управ- ляющая логика, машинная независимость и богатый набор операторов) делают его похожим на высокоуровневый язык. Язык C также называют «портируемым языком ассемблера», поскольку он не строго типизирован, поощряет применение указателей и адресов и поддерживает некоторые низкоуровневые возможности, такие как побитовые операции. Язык C, разработанный в 1970-х компанией Bell Labs, предназаначался для сис- тем DEC PDP-11. На C были написаны ОС, компилятор C и приложения UNIX для систем DEC PDP-11. В 1988 г. для систематизации C был издан стандарт ANSI, ко- торый в 1999 г. был пересмотрен. В 1980-х и 1990-х гг. язык C был стандартом «де-факто» в области разработки программ для микрокомпьютеров и рабочих стан- ций. C++ Этот объектно-ориентированный язык был разработан на базе C в компании Bell Labs в 1980-х. Совместимый с языком C, он поддерживает классы, полиморфизм, обработку исключений, шаблоны и обеспечивает более надежную проверку ти- пов, чем C. Кроме того, он предоставляет разработчикам богатую и эффективную стандартную библиотеку. C# Эта комбинация объектно-ориентированного языка общего назначения и среды программирования разработана в Microsoft. C# имеет синтаксис, похожий на син- таксис C, C++ и Java, и включает богатый инструментарий, помогающий разраба- тывать приложения на платформах Microsoft. 6 2 ЧАСТЬ I Основы разработки ПО Cobol Напоминает английский язык и был разработан в 1959–1961 гг. для нужд Мин- обороны США. Cobol служит преимущественно для разработки бизнес-приложе- ний и до сих пор является одним из самых популярных языков, уступая лишь Visual Basic (Feiman and Driver, 2002). По мере развития языка в нем была реализована поддержка дополнительных математических функций и ряда объектно-ориенти- рованных возможностей. Аббревиатура «Cobol» расшифровывается как «COmmon Business-Oriented Language» (универсальный язык, ориентированный на коммер- ческие задачи). Fortran В этом первом высокоуровневом языке программирования были представлены концепции переменных и высокоуровневых циклов. Название расшифровывает- ся как «FORmula TRANslation» (транслятор формул). Разработанный в 1950-х, Fortran претерпел несколько значительных ревизий: так, в 1977 г. была разработана вер- сия Fortran 77, в которой была реализована поддержка блочных операторов if-then- else и манипуляций над символьными строками. В Fortran 90 были включены сред- ства работы с пользовательскими типами данных, указателями, классами, а также богатый набор функций для работы с массивами. Fortran применяется преимуще- ственно для разработки научных и инженерных приложений. Java Синтаксис этого объектно-ориентированного языка, разработанного Sun Micro- systems, Inc., напоминает C и C++. Java — платформенно-независимый язык: ис- ходный код Java сначала преобразуется в байт-код, который может выполняться на любой платформе в среде, известной как «виртуальная машина». Java широко используется для создания Web-приложений. JavaScript Этот интерпретируемый язык сценариев мало чем связан с Java. Чаще всего его используют для создания кода, выполняющегося на клиентской стороне, напри- мер, для разработки несложных функций и интерактивных приложений для Web- страниц. Perl Этот язык обработки строк основан на C и нескольких утилитах ОС UNIX. Perl часто используется для решения задач системного администрирования, таких как со- здание сценариев сборки программ, а также для генерации и обработки отчетов. Кроме того, на нем создают Web-приложения, такие как Slashdot. Аббревиатура «Perl» расшифровывается как «Practical Extraction and Report Language» (практи- ческий язык извлечений и отчетов). PHP Этот язык с открытым исходным кодом предназначен для разработки сценариев и имеет простой синтаксис, похожий на синтаксис языков Perl, JavaScript, C и оболочки Bourne Shell. PHP поддерживается всеми основными ОС и служит для ГЛАВА 4 Основные решения, которые приходится принимать при конструировании 63 создания интерактивных функций, выполняющихся на стороне сервера. PHP-код может быть встроен в Web-страницы для получения доступа к БД и отображения содержащейся в ней информации. Аббревиатура «PHP» первоначально расшиф- ровывалась как «Personal Home Page», но теперь означает «PHP: Hypertext Processor». Python Этот интерпретируемый интерактивный объектно-ориентированный язык под- держивает множество сред. Чаще всего его используют для написания сценариев и небольших Web-приложений, однако он поддерживает и некоторые средства, помогающие создавать более крупные программы. SQL SQL (Structured Query Language, язык структурированных запросов) «де-факто» яв- ляется стандартным языком выполнения запросов, обновлений реляционнных БД и управления ими. В отличие от других языков, описанных в этом разделе, SQL является «декларативным языком», т. е. определяет не последовательность, а резуль- тат выполнения некоторых операций. Visual Basic Basic (Beginner’s All-purpose Symbolic Instruction Code, универсальная система сим- волического кодирования для начинающих) — это высокоуровневый язык, первая версия которого была разработана в Дартмутском колледже в 1960-х. Visual Basic — это высокоуровневая объектно-ориентированная версия Basic, предназначенная для визуального программирования. Изначально Visual Basic был разработан в Microsoft для создания приложений Microsoft Windows. Позднее в нем была реали- зована поддержка настройки Microsoft Office и других приложений для настольных ПК, создания Web-приложений и других программ. По оценкам экспертов в самом начале первого десятилетия XXI века Visual Basic являлся самым популярным язы- ком среди профессиональных разработчиков (Feiman and Driver, 2002). 4.2. Конвенции программирования В высококачественном приложении должна быть очевидна связь между концептуальной целостностью архитектуры и ее низкоуровневой реализацией. Реализация должна соот- ветствовать высокоуровневой архитектуре и обладать внут- ренней согласованностью. В этом и заключается смысл принципов конструиро- вания, определяющих конвенции именования переменных, классов, методов, а также форматирования кода и оформления комментариев. При разработке сложной программы архитектурные принципы вносят в программу структурный баланс, а принципы конструирования — низкоуровневую гармонию, при наличии которой каждый класс воспринимается как неотъемлемая часть об- щего плана. Любая крупная программа требует применения контролирующей структуры, унифицирующей аспекты языка программирования. Красота крупной структуры частично заключается в том, как в ее отдельных компонентах выраже- ны особенности архитектуры. Без унификации ваша программа будет смесью Перекрестная ссылка Подроб- нее о силе конвенций см. раз- делы 11.3–11.5. 6 4 ЧАСТЬ I Основы разработки ПО небрежных вариаций стиля, заставляющих прилагать дополнительные усилия только для того, чтобы понять различия в стиле кодирования, которых вполне можно было избежать. Одно из условий успешного программирования — устра- нение ненужных вариаций, позволяющее сосредоточиться на действительно не- обходимых вариациях. См. об этом подраздел «Главный технический императив разработки ПО: управление сложностью» раздела 5.2. Что, если у вас есть отличный план создания картины, но одну ее часть вы реши- те писать в классическом стиле, другую в импрессионистском, а третью в кубист- ском? Как бы упорно вы ни следовали своему грандиозному плану, картина не будет концептуально целостной. Она будет похожа на коллаж. Программа тоже должна обладать низкоуровневой целостностью. Перед началом конструирования сформулируйте конвенции программи- рования. Детали конвенций кодирования относятся к такому низкому уровню, что после написания программы их почти невозможно изменить. В оставшейся части книги я еще не раз затрону конвенции кодирования. 4.3. Волны развития технологий Я видел, как взошла звезда ПК, в то время как звезда мэйнфреймов опустилась за горизонт. Я видел, как консольные программы были вытеснены программами с GUI. Я также видел, как традиционные программы уступили главную роль Web- приложениям. Могу предположить, что, когда вы будете читать эту книгу, будут бурно развиваться некоторые новые технологии, а Web-программирование в его современном (2004) виде начнет отходить на второй план. В соответствии с эти- ми технологическими циклами, или волнами, изменяются и методики програм- мирования. В зрелых технологических средах — таких как среда Web-программирования в середине 2000-х — нам доступны все достоинства богатой инфраструктуры раз- работки ПО. Такие среды предоставляют широкий выбор языков программиро- вания, мощные средства поиска ошибок, эффективные инструменты отладки и надежные автоматизированные средства оптимизации производительности при- ложений. Компиляторы почти не содержат ошибок. Инструменты хорошо опи- саны в документации производителей, в книгах и статьях сторонних фирм и на многочисленных Web-сайтах. Инструменты интегрированы, благодаря чему вы можете разрабатывать UI, модули работы с БД, составления отчетов и бизнес-ло- гики в одной среде. Решения проблем можно легко найти в ответах на «часто за- даваемые вопросы». Кроме того, доступны разнообразные услуги консультантов и программы тренинга. В ранних средах — таких как Web-программирование в середине 1990-х — ситу- ация противоположная. Языков программирования мало, при этом они часто полны ошибок и плохо документированы. Вместо написания нового кода программис- ты тратят массу времени только на то, чтобы разобраться в особенностях языка. Бесчисленные часы уходят на борьбу с ошибками в языках, ОС и других инстру- ментах. Инструменты программирования часто примитивны. Отладчиков может не быть вообще, а об оптимизаторах компиляторов программистам приходится ГЛАВА 4 Основные решения, которые приходится принимать при конструировании 65 лишь мечтать. Производители часто выпускают новые версии компиляторов, при этом каждая новая версия отказывается поддерживать значительные части ваше- го кода. Инструменты не интегрированы, из-за чего UI, модули работы с БД, со- ставления отчетов и бизнес-логики приходится разрабатывать при помощи раз- ных средств. Из-за плохой совместимости инструментов и частого появления новых компиляторов и библиотек программисты тратят много усилий только на под- держание работоспособности имеющейся инфраструктуры. При возникновении проблем в Интернете можно найти кое-какую документацию, но она не отлича- ется достоверностью и полнотой. Вам может показаться, что я рекомендую избегать программирования в ранних средах, но это не так. В ранних средах были разработаны программы, давшие начало некоторым из самых инновационных приложений, такие как Turbo Pascal, Lotus 123, Microsoft Word и браузер Mosaic. Я просто хочу сказать, что от стадии развития технологии зависит то, как будет протекать ваша работа. В зрелой сре- де вы можете посвящать большую часть дня постепенной реализации новой функ- циональности. Работая в ранней среде, исходите из того, что вам придется тра- тить много времени на выяснение недокументированных возможностей выбран- ного языка программирования, отладку ошибок, которые в итоге окажутся дефек- тами библиотек, проверку того, что написанный код будет работать с новой вер- сией библиотеки какого-нибудь производителя и т. д. При работе в примитивной среде методики программирования, описанные в этой книге, могут оказаться еще более полезными, чем в зрелых средах. Как сказал Дэвид Грайс (Gries, 1981), подход к программированию не должен определяться исполь- зуемыми инструментами. В связи с этим он проводит различие между програм- мированием на языке (programming in language) и программированием с исполь- зованием языка (programming into language). Разработчики, программирующие «на» языке, ограничивают свое мышление конструкциями, непосредственно поддер- живаемых языком. Если предоставляемые языком средства примитивны, мысли программистов будут столь же примитивными. Разработчики, программирующие «с использованием» языка, сначала решают, какие мысли они хотят выразить, после чего определяют, как выразить их при помощи конкретного языка. Пример программирования с использованием языка Разрабатывая программу на Visual Basic, который тогда находился на раннем эта- пе развития, я с огорчением обнаружил, что язык не поддерживает встроенных способов разделения бизнес-логики, кода GUI и кода работы с БД. Я знал, что, если буду невнимателен, со временем некоторые из моих «форм» Visual Basic включат в себя код бизнес-логики, другие — код доступа к БД, а остальные не будут содер- жать ни того, ни другого — в итоге я не смогу вспомнить, какая форма за что отвечает. Я только что завершил работу над проектом C++, в котором разделение кода было выполнено плохо, и не хотел еще раз наступать на те же грабли. Поэтому я принял конвенцию, в соответствии с которой файлам .frm (файлам формы) дозволялось только извлекать данные из БД и сохранять их обратно, но не передавать эти данные другим частям программы. Все формы поддерживали 6 6 ЧАСТЬ I Основы разработки ПО метод IsFormCompleted(), который сообщал вызвавшему его методу, сохранила ли активная форма свои данные. IsFormCompleted() был единственным открытым методом, который могли иметь формы. Код форм также не мог включать никакой бизнес-логики. Весь остальной код, в том числе проверяющий корректность вво- димых в форму данных, должен был содержаться в ассоциированном файле .bas. Visual Basic не поощрял такого подхода. Он поощрял программистов включать в файл .frm максимальный объем кода, и это отнюдь не облегчало реализацию вза- имодействия межу файлами .frm и .bas. Принятая мной конвенция была очень проста, но по мере развития проекта я обнаружил, что она помогла мне избежать многих случаев, в которых мне при- шлось бы писать неестественный код. Так, мне пришлось бы загружать формы, но держать их скрытыми, чтобы можно было вызвать реализованные в них методы проверки корректности данных, или мне пришлось бы копировать код форм в другие места программы и сопровождать этот параллельный код. Кроме того, конвенция IsFormCompleted() позволила все упростить. Все формы работали оди- наково, поэтому я мог не предполагать семантику IsFormCompleted() — вызовы этого метода всегда имели одинаковый смысл. Visual Basic не поддерживал такой подход непосредственно, но простая конвен- ция программирования — программирование с использованием языка — позво- лила мне реализовать отсутствующую в то время структуру языка и помогла упростить проект до приемлемого уровня. Понимание различия между программированием на языке и програм ми рованием с использованием языка — важнейшее условие понима- ния этой книги. Большинство важных принципов программирования зависит не от конкретных языков, а от способа их использования. Если язык не поддерживает нужные конструкции или имеет другие недостатки, попробуйте их компенсировать. Создайте свои конвенции кодирования, стандарты, библио- теки классов и другие средства. 4.4. Выбор основных методик конструирования При подготовке к конструированию следует решить, какие из доступных эффек- тивных методик вы будете использовать. Некоторые проекты предусматривают пар- ное программирование и предварительное создание тестов, тогда как другие — индивидуальное программирование и проведение формальных инспекций. Обе комбинации методик могут быть удачными, но при их выборе следует учитывать специфические особенности проекта. Специфические методики конструирования, которые должны быть осознанно приняты или отвергнуты, указаны ниже. В оставшейся части книги эти методики будут описаны подробнее. |