Файл: Руководство по стилю программирования и конструированию по.pdf

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 30.11.2023

Просмотров: 881

Скачиваний: 2

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

СОДЕРЖАНИЕ

ГЛАВА 6 Классы129쐽Вариант 3: использовать неявные экземпляры (с большой осторожностью). Вы должны создать новый сервис — скажем, SetCurrentFont ( fontId ), — при вызо- ве которого заданный экземпляр шрифта делается текущим. После этого все остальные сервисы используют текущий шрифт, благодаря чему в них не нуж- но передавать параметр fontId. При разработке простых приложений такой под- ход может облегчить использование нескольких экземпляров данных. В слож- ных приложениях подобная зависимость от состояния в масштабе всей сис- темы подразумевает, что вы должны следить за текущим экземпляром шрифта во всем коде, вызывающем методы Font; разумеется, сложность программы при этом повышается. Каким бы ни был размер приложения, всегда можно найти более удачные альтернативы данному подходу.Внутри АТД вы можете реализовать работу с несколькими экземплярами данных как угодно, но вне его при использовании языка, не являющегося объектно-ори- ентированным, возможны только три указанных варианта.АТД и классыАбстрактные типы данных лежат в основе концепции классов. В языках, поддержи- вающих классы, каждый АТД можно реализовать как отдельный класс. Однако обыч- но с классами связывают еще две концепции: наследование и полиморфизм. Може- те рассматривать класс как АТД, поддерживающий наследование и полиморфизм.6.2. Качественные интерфейсы классовПервый и, наверное, самый важный этап разработки высококачественного клас- са — создание адекватного интерфейса. Это подразумевает, что интерфейс дол- жен представлять хорошую абстракцию, скрывающую детали реализации класса.Хорошая абстракцияКак я говорил в подразделе «Определите согласованные абстракции» раздела 5.3,под абстракцией понимается представление сложной операции в упрощенной форме. Интерфейс класса — это абстракция реализации класса, скрытой за ин- терфейсом. Интерфейс класса должен предоставлять группу методов, четко согла- сующихся друг с другом.Рассмотрим для примера класс «сотрудник». Он может содержать такие данные,как фамилия сотрудника, адрес, номер телефона и т. д., и предлагать методы ини- циализации и использования этих данных. Вот как мог бы выглядеть такой класс:Пример интерфейса, формирующего хорошую абстракцию (C++)class Employee {public: // открытые конструкторы и деструкторы Employee(); Employee( FullName name, String address, String workPhone,Перекрестная ссылка Примеры кода в этой книге отформати- рованы с использованием кон- венции, поддерживающей сход- ство стилей между нескольки- ми языками. Об этой конвенции(и разных стилях кодирования)см. подраздел «Программирова- ние с использованием несколь- ких языков» раздела 11.4. 130ЧАСТЬ II Высококачественный код String homePhone, TaxId taxIdNumber, JobClassification jobClass ); virtual Employee(); // открытые методы FullName GetName() const; String GetAddress() const; String GetWorkPhone() const; String GetHomePhone() const; TaxId GetTaxIdNumber() const; JobClassification GetJobClassification() const;private:};Внутри этот класс может иметь дополнительные методы и данные, поддержива- ющие работу этих сервисов, но пользователям класса знать о них не нужно. Пред- ставляемая интерфейсом этого класса абстракция великолепна, потому что все методы интерфейса служат единой согласованной цели.Интерфейс, представляющий плохую абстракцию, содержал бы набор разнород- ных методов, например:Пример интерфейса, формирующегоплохую абстракцию (C++)class Program {public: // открытые методы void InitializeCommandStack(); void PushCommand( Command command ); Command PopCommand(); void ShutdownCommandStack(); void InitializeReportFormatting(); void FormatReport( Report report ); void PrintReport( Report report ); void InitializeGlobalData(); void ShutdownGlobalData();private:};Похоже, этот класс содержит методы работы со стеком команд, форматирования отчетов, печати отчетов и инициализации глобальных данных. Трудно увидеть связь между стеком команд, обработкой отчетов и глобальными данными. Интерфейс такого класса не формирует согласованную абстракцию, и класс обладает плохой ГЛАВА 6 Классы131связностью. В данном случае методы следует реорганизовать в более четкие классы,интерфейсы которых будут представлять более удачные абстракции.Если бы эти методы были частью класса Program, для формирования согласован- ной абстракции их можно было бы изменить так:Пример интерфейса, формирующего более удачную абстракцию (C++)class Program {public: // открытые методы void InitializeUserInterface(); void ShutDownUserInterface(); void InitializeReports(); void ShutDownReports();private:};В ходе очистки интерфейса одни его методы были перемещены в более подходя- щие классы, а другие были преобразованы в закрытые методы, используемые методом InitializeUserInterface() и другими методами.Данный способ оценки абстракции класса основан на изучении открытых методов класса, т. е. его интерфейса. Однако из того, что класс в целом формирует хорошую абстракцию, вовсе не следует, что его отдельные методы также представляют удач- ные абстракции. Рекомендации по проектированию методов см. в разделе 7.2.Чтобы ваши классы имели высококачественные абстрактные интерфейсы, соблю- дайте при их проектировании следующие принципы.Выражайте в интерфейсе класса согласованный уровень абстракцииКлассы полезно рассматривать как механизмы реализации абстрактных типов дан- ных, описанных в разделе 6.1. В идеале каждый класс должен быть реализацией только одного АТД. Если класс реализует более одного АТД или если вам не уда- ется определить, реализацией какого АТД класс является, самое время реоргани- зовать класс в один или несколько хорошо определенных АТД.Так, следующий класс имеет несогласованный интерфейс, потому что формируе- мый им уровень абстракции непостоянен:Пример интерфейса, включающего разныеуровни абстракции (C++)class EmployeeCensus: public ListContainer {public: // открытые методы 132ЧАСТЬ II Высококачественный кодАбстракция, формируемая этими методами, относится к уровню «employee» (сотрудник). void AddEmployee( Employee employee ); void RemoveEmployee( Employee employee );Абстракция, формируемая этими методами, относится к уровню «list» (список). Employee NextItemInList(); Employee FirstItem(); Employee LastItem();private:};Этот класс представляет два АТД: Employee и ListContainer (список-контейнер).Подобные смешанные абстракции часто возникают, когда программист реализу- ет класс при помощи класса-контейнера или других библиотечных классов и не скрывает этот факт. Спросите себя, должна ли информация об использовании класса-контейнера быть частью абстракции. Обычно это является деталью реали- зации, которую следует скрыть от остальных частей программы, например так:Пример интерфейса, формирующего согласованную абстракцию (C++)class EmployeeCensus {public: // открытые методыАбстракция, формируемая всеми этими методами, теперь относится к уровню «employee». void AddEmployee( Employee employee ); void RemoveEmployee( Employee employee ); Employee NextEmployee(); Employee FirstEmployee(); Employee LastEmployee();private:Тот факт, что класс использует библиотеку ListContainer, теперь скрыт. ListContainer m_EmployeeList;};Программисты могут утверждать, что наследование от ListContainer удобно, потому что оно поддерживает полиморфизм, позволяя создать внешний метод поиска или сортировки, принимающий объект ListContainer. Но этот аргумент не проходит главный тест на уместность наследования: «Используется ли наследование толь- ко для моделирования отношения „является“?» Наследование класса EmployeeCensus(каталог личных дел сотрудников) от класса ListContainer означало бы, что Employee-Census «является» ListContainer, что, очевидно, неверно. Если абстракция объектаEmployeeCensus заключается в том, что он поддерживает поиск или сортировку,>>>> ГЛАВА 6 Классы133эти возможности должны быть явными согласованными частями интерфейса класса.Если представить открытые методы класса как люк, предотвращающий попадание воды в подводную лодку, несогласованные открытые методы — это щели. Вода не будет протекать через них так быстро, как через открытый люк, но позже лодка все же потонет. На практике при смешении уровней абстракции именно это и проис- ходит. По мере изменений программы смешанные уровни абстракции делают ее все менее и менее понятной, пока в итоге код не станет совсем загадочным.Убедитесь, что вы понимаете, реализацией какой абстракцииявляется класс Некоторые классы очень похожи, поэтому при разра- ботке класса нужно понимать, какую абстракцию должен представлять его интерфейс. Однажды я работал над программой, которая должна была под- держивать редактирование информации в табличном формате. Сначала мы хо- тели использовать простой элемент управления «grid» (сетка), но доступные эле- менты управления этого типа не позволяли закрашивать ячейки ввода данных в другой цвет, поэтому мы выбрали элемент управления «spreadsheet» (электронная таблица), который такую возможность поддерживал.Элемент управления «электронная таблица» был гораздо сложнее «сетки» и пре- доставлял около 150 методов в сравнении с 15 методами «сетки». Так как наша цель заключалась в использовании «сетки», а не «электронной таблицы», мы поручили одному программисту написать класс-оболочку, который скрывал бы тот факт, что мы подменили один элемент управления другим. Он поворчал по поводу ненуж- ных затрат и бюрократии, ушел и вернулся через пару дней с классом-оболочкой,который честно предоставлял все 150 методов «электронной таблицы».Но нам было нужно не это — нам требовался интерфейс «сетки», инкапсулирую- щий тот факт, что за кулисами мы использовали гораздо более сложную «элект- ронную таблицу». Программисту следовало предоставить доступ только к 15 ме- тодам «сетки» и еще одному, шестнадцатому методу, поддерживающему закраши- вание ячеек. Открыв доступ ко всем 150 методам, программист подверг нас риску того, что после нескольких изменений реализации класса нам в итоге придется поддерживать все 150 открытых методов. Он не смог обеспечить нужную нам инкапсуляцию и проделал гораздо больше работы, чем стоило.В зависимости от конкретных обстоятельств оптимальной абстракцией может оказаться как «сетка», так и «электронная таблица». Если приходится выбирать между двумя похожими абстракциями, убедитесь, что выбор правилен.Предоставляйте методы вместе с противоположными им методамиБольшинство операций имеет соответствующие противоположные операции. Если одна из операций включает свет, вам, вероятно, понадобится и операция, его вы- ключающая. Если одна операция добавляет элемент в список, элементы скорее всего нужно будет и удалять. Если одна операция активизирует элемент меню, вторая,наверное, должна будет его деактивизировать. При проектировании класса про- верьте каждый открытый метод на предмет того, требуется ли вам его противо- положность. Создавать противоположные методы, не имея на то причин, не сле- дует, но проверить их целесообразность нужно. 134ЧАСТЬ II Высококачественный кодУбирайте постороннюю информацию в другие классы Иногда вы будете обнаруживать, что одни методы класса работают с одной половиной данных, а другие — с другой. Это значит, что вы имеете дело с двумя классами, скрывающи- мися под маской одного. Разделите их!По мере возможности делайте интерфейсы программными, а не семан-тическими Каждый интерфейс состоит из программной и семантической ча- стей. Первая включает типы данных и другие атрибуты интерфейса, которые могут быть проверены компилятором. Вторая складывается из предположений об ис- пользовании интерфейса, которые компилятор проверить не может. Семантический интерфейс может включать такие соображения, как «Метод А должен быть выз- ван перед Методом B» или «Метод А вызовет ошибку, если переданный в него Эле- мент Данных 1 не будет перед этим инициализирован». Семантический интерфейс следует документировать в комментариях, но вообще интерфейсы должны как можно меньше зависеть от документации. Любой аспект интерфейса, который не может быть проверен компилятором, является потенциальным источником оши- бок. Старайтесь преобразовывать семантические элементы интерфейса в программ- ные, используя утверждения (assertions) или иными способами.Опасайтесь нарушения целостности интерфейса приизменении класса При модификации и расширении клас- са часто обнаруживается дополнительная нужная функци- ональность, которая не совсем хорошо соответствует интер- фейсу первоначального класса, но плохо поддается реализации иным образом. Так,класс Employee может превратиться во что-нибудь вроде:Пример интерфейса, изуродованного при сопровождениипрограммы (C++)class Employee {public: // открытые методы FullName GetName() const; Address GetAddress() const; PhoneNumber GetWorkPhone() const; bool IsJobClassificationValid( JobClassification jobClass ); bool IsZipCodeValid( Address address ); bool IsPhoneNumberValid( PhoneNumber phoneNumber ); SqlQuery GetQueryToCreateNewEmployee() const; SqlQuery GetQueryToModifyEmployee() const; SqlQuery GetQueryToRetrieveEmployee() const;private:};То, что начиналось как ясная абстракция, превратилось в смесь почти несогласо- ванных методов. Между сотрудниками и методами, проверяющими корректностьПерекрестная ссылка О поддер- жании качества кода при его изменении см. главу 24. ГЛАВА 6 Классы135почтового индекса, номера телефона или ставки зарплаты (job classification), нет логической связи. Методы, предоставляющие доступ к деталям SQL-запросов, от- носятся к гораздо более низкому уровню абстракции, чем класс Employee, нару- шая общую абстракцию класса.Не включайте в класс открытые члены, плохо согласующиеся с абстрак-цией интерфейса Добавляя новый метод в интерфейс класса, всегда спраши- вайте себя: «Согласуется ли этот метод с абстракцией, формируемой существую- щим интерфейсом?» Если нет, найдите другой способ внесения изменения, позво- ляющий сохранить согласованность абстракции.Рассматривайте абстракцию и связность вместе Понятия абстракции и связности (cohesion) тесно связаны: интерфейс класса, представляющий хорошую абстракцию, обычно отличается высокой связностью. И наоборот: классы, имею- щие высокую связность, обычно представляют хорошие абстракции, хотя эта связь выражена слабее.Я обнаружил, что при повышенном внимании к абстракции, формируемой ин- терфейсом класса, проект класса получается более удачным, чем при концентра- ции на связности класса. Если вы видите, что класс имеет низкую связность и не знаете, как это исправить, спросите себя, представляет ли он согласованную аб- стракцию.Хорошая инкапсуляцияКак я уже говорил в разделе 5.3, инкапсуляция является бо- лее строгой концепцией, чем абстракция. Абстракция по- могает управлять сложностью, предоставляя модели, позво- ляющие игнорировать детали реализации. Инкапсуляция не позволяет узнать детали реализации, даже если вы этого захотите.Две этих концепции связаны: без инкапсуляции абстракция обычно разрушается.По своему опыту могу сказать, что вы или имеете и абстракцию, и инкапсуляцию,или не имеете ни того, ни другого. Промежуточных вариантов нет.Минимизируйте доступность классов и их членов Ми- нимизация доступности — одно из нескольких правил, под- держивающих инкапсуляцию. Если вы не можете понять,каким делать конкретный метод: открытым, закрытым или защищенным, — некоторые авторы советуют выбирать са- мый строгий уровень защиты, который работает (Meyers,1998; Bloch, 2001). По-моему, это прекрасное правило, но мне кажется, что еще важнее спросить себя: «Какой вари- ант лучше всего сохраняет целостность абстракции интер- фейса?» Если предоставление доступа к методу согласуется с абстракцией, сделайте его открытым. Если вы не уверены, скрыть больше обычно предпочтительнее, чем скрыть меньше.Не делайте данные-члены открытыми Предоставление доступа к данным- членам нарушает инкапсуляцию и ограничивает контроль над абстракцией. КакПерекрестная ссылка Об инкап- суляции см. подраздел «Инкап- сулируйте детали реализации»раздела 5.3.Самым важным отличием хоро- шо спроектированного модуля от плохо спроектированного яв- ляется степень, в которой мо- дуль скрывает свои внутренние данные и другие детали реали- зации от других модулей.Джошуа Блох (Joshua Bloch) 136ЧАСТЬ II Высококачественный код указывает Артур Риэль, класс Point (точка), который предоставляет доступ к дан- ным:float x;float y;float z;нарушает инкапсуляцию, потому что клиентский код может свободно делать с данными Point что угодно, при этом сам класс может даже не узнать об их изме- нении (Riel, 1996). В то же время класс Point, включающий члены:float GetX();float GetY();float GetZ();void SetX( float x );void SetY( float y );void SetZ( float z );поддерживает прекрасную инкапсуляцию. Вы не имеете понятия о том, реализо- ваны ли данные как float x, y и z, хранит ли класс Point эти элементы как double,преобразуя их в float, или же он хранит их на Луне и получает через спутник.Не включайте в интерфейс класса закрытые детали реализации Истинная инкапсуляция не позволяла бы узнать детали реализации вообще. Они были бы скрыты и в прямом, и в переносном смыслах. Однако популярные языки — в том числе C++ — требуют, чтобы программисты раскрывали детали реализации в интерфейсе класса, например:Пример обнародования деталей реализации класса (C++)class Employee {public: Employee( FullName name, String address, String workPhone, String homePhone, TaxId taxIdNumber, JobClassification jobClass ); FullName GetName() const; String GetAddress() const;private:Обнародованные детали реализации. String m_Name; String m_Address; int m_jobClass;};> 1   ...   14   15   16   17   18   19   20   21   ...   104

ГЛАВА 20 Качество ПО463Табл. 20-2. Эффективность нахождения дефектовпри использовании разных методикМетодикаМинимальнаяТипичнаяМаксимальнаяустранения дефектовэффективностьэффективностьэффективностьНеформальные25%35%40%обзоры проектаФормальные45%55%65%инспекции проектаНеформальные20%25%35%обзоры кодаФормальные45%60%70%инспекции кодаМоделирование35%65%80%или прототипированиеСамостоятельная20%40%60%проверка кодаБлочное тестирование15%30%50%Тестирование новых20%30%35%функций (компонентов)Интеграционное25%35%40%тестированиеРегрессивное15%25%30%тестированиеТестирование системы25%40%55%Ограниченное бета-тес- 25%35%40%тирование (менее чем в 10 организациях)Крупномасштабное бета- 60%75%85%тестирование (более чем в 1000 организаций)Источники: «Programming Productivity» (Jones, 1986a), «Software Defect-RemovalEfficiency» (Jones, 1996) и «What We Have Learned About Fighting Defects» (Shull et al., 2002).Самое интересное в этих данных то, что типичная эффективность об- наружения дефектов при использовании любой методики не превышает75% и что в среднем она равна примерно 40%. Более того, самые попу- лярные методики — блочное тестирование и интеграционное тестирование — по- зволяют найти обычно только около 30–35% дефектов. Как правило, использует- ся подход, основанный на интенсивном тестировании, что позволяет устранить лишь около 85% дефектов. Ведущие организации используют более широкий ди- апазон методик, достигая при этом 95%-ой или более высокой эффективности ус- транения дефектов (Jones, 2000).Итак, если разработчики хотят достигнуть более высокой эффективности обна- ружения дефектов, они должны полагаться на комбинацию методик. Одно из подтверждений этого вывода было получено в классическом исследовании Глен- форда Майерса (Myers, 1978b). Участниками исследования были программисты,обладавшие минимум 7-, а в среднем — 11-летним опытом. Исследуемая программа 464ЧАСТЬ V Усовершенствование кода содержала 15 известных ошибок. Майерс попросил каждого программиста най- ти эти ошибки, используя одну из следующих методик:쐽тестирование выполнения программы по спецификации;쐽тестирование выполнения программы по спецификации с возможностью изу- чения исходного кода;쐽анализ/инспекция с использованием и спецификации, и исходного кода.Различия эффективности обнаружения дефектов оказались очень боль- шими: программисты нашли от 1 до 9 дефектов. Средний показатель был равен 5,1, или 1/3 от общего числа известных дефектов.При использовании одной методики никакая из них не имела статистически зна- чимого преимущества над любой другой. В то же время любая комбинация двух методик — в том числе использование одной методики двумя независимыми груп- пами — приводила к увеличению общего числа найденных дефектов почти вдвое.В исследованиях, проведенных в Лаборатории проектирования ПО NASA, компа- нии Boeing и других компаниях, было обнаружено, что разные программисты находят разные дефекты. Только примерно каждую пятую ошибку, обнаруженную в ходе инспекций, находят двое или более разработчиков (Kouchakdjian, Green,and Basili, 1989; Tripp, Struck, and Pflug, 1991; Schneider, Martin, and Tsai, 1992).Майерс обращает внимание на то, что поиск одних видов ошибок оказывается более эффективным при непосредственном участии людей (например, при инспекции или анализе кода), а других видов — при компьютерном тестировании (Myers, 1979).Этот вывод подтвердился в более позднем исследовании, показавшем, что чтение кода способствует нахождению дефектов интерфейса, а функциональное тести- рование — нахождению дефектов управляющих структур (Basili, Selby, and Hutchens,1986). Гуру тестирования Борис Бейзер (Boris Beizer) сообщает, что неформаль- ные подходы к тестированию обычно позволяют достигнуть покрытия кода тес- тами лишь на 50–60%, если только вы не используете анализатор покрытия(Johnson, 1994).Таким образом, методики поиска дефектов лучше применять в комбина- ции. Джонс (Jones) также подтверждает этот вывод. Используя исключи- тельно тестирование, высоких результатов добиться невозможно. Джонс сообщает, что комбинация блочного тестирования, функционального тестирования и тестирования системы часто приводит к обнаружению менее 60% дефектов, что обычно неприемлемо для конечного продукта.Эти данные также помогают понять, почему программисты, начинающие приме- нять дисциплинированную методику устранения дефектов, такую как экстремаль- ное программирование, добиваются более высокой степени устранения дефектов.Как показывает табл. 20-3, набор методик устранения дефектов, применяемых в экстремальном программировании, позволяет устранить около 90% дефектов в обычной ситуации и 97% в лучшем случае, что гораздо выше среднего для отрас- ли показателя, равного 85%. Некоторые программисты связывают этот факт с синергичными отношениями между методиками экстремального программиро- вания, но на самом деле это просто предсказуемый результат использования кон- кретного набора методик устранения дефектов. Эффективность других комбинаций методик может оказаться такой же или даже более высокой, поэтому выбор кон- ГЛАВА 20 Качество ПО465кретных методик устранения дефектов, позволяющих достичь желаемого уровня качества, является одним из аспектов эффективного планирования проекта.Табл. 20-3. Эффективность обнаружения дефектов, характернаядля экстремального программированияМетодика устраненияМинимальнаяТипичнаяМаксимальнаядефектовэффективностьэффективностьэффективностьНеформальные обзоры25%35%40%проекта (парное программи- рование)Неформальные обзоры кода20%25%35%(парное программирование)Самостоятельная20%40%60%проверка кодаБлочное тестирование15%30%50%Интеграционное25%35%40%тестированиеРегрессивное тестирование15%25%30%Общая эффективность74%90%97%устранения дефектовСтоимость нахождения дефектовНекоторые методики обнаружения дефектов дороже других. Наиболее экономич- ные при прочих равных условиях имеют наименьшую стоимость в расчете на один обнаруженный дефект. Равенством прочих условий пренебрегать нельзя, поскольку стоимость методики в расчете на один дефект зависит от общего числа обнару- женных дефектов, этапа обнаружения каждого дефекта и других факторов, не связанных с экономическими аспектами конкретной методики.Как правило, эксперименты показывают, что инспекции обходятся дешев- ле, чем тестирование. В исследовании, проведенном в Лаборатории про- ектирования ПО, было обнаружено, что при чтении кода число дефек- тов, находимых в час, было примерно на 80% более высоким, чем при тестирова- нии (Basili and Selby, 1987). В другой организации поиск дефектов проектирова- ния с использованием блочного тестирования был вшестеро дороже, чем при ис- пользовании инспекций (Ackerman, Buchwald, and Lewski, 1989). Более позднее ис- следование, проведенное в IBM, показало, что на обнаружение каждой ошибки раз- работчики тратили 3,5 человеко-часа в случае инспекций кода и 15–25 в случае тестирования (Kaplan, 1995).Стоимость исправления дефектовСтоимость нахождения дефектов — только одна часть уравнения. Другой частью является стоимость их исправления. На первый взгляд, методика обнаружения дефектов не играет роли: стоимость их исправления всегда будет одинаковой.Это неверно, потому что чем дольше дефект остается в системе, тем больше средств придется потратить на его устранение. Следовательно, методика, способствующая раннему обнаружению ошибок, снижает стоимость их исправления. Еще важнее 466ЧАСТЬ V Усовершенствование кода то, что одни методики — такие как инспекции — позволя- ют определить и симптомы, и причины дефектов за один этап; другие — например, тестирование — указывают на симптомы дефекта, но требуют выполнения дополнитель- ной работы для диагностики и устранения его причины. Витоге одноэтапные методики оказываются гораздо более дешевыми, чем двухэтапные.В одном из подразделений Microsoft обнаружили, что при использова- нии инспекции кода — одноэтапной методики — на нахождение и ис- правление дефекта уходит 3 часа, тогда как при использовании тестиро- вания — двухэтапной методики — на это требуется 12 часов (Moore, 1992). Кол- лофелло и Вудфилд сообщили, что при разработке программы из 700 000 строк,над которой работало более 400 программистов, обзоры кода имели гораздо бо- лее высокую экономическую эффективность, чем тестирование: прибыль на ин- вестированный капитал была равной 1,38 и 0,17 соответственно (Collofello andWoodfield, 1989).Суть сказанного в том, что эффективная программа контроля качества должна включать комбинацию методик, применяемых на всех стадиях разработки. Для достижения высокого качества ПО можно использовать следующую комбинацию:쐽формальные инспекции всех требований, всех аспектов архитектуры и всех проектов критических частей системы;쐽моделирование или прототипирование;쐽чтение или инспекции кода;쐽тестирование выполнения программы.20.4. Когда выполнять контроль качества ПО?Как было отмечено в главе 3, чем раньше ошибка внедряет- ся в приложение, тем сильнее она переплетается с другими частями приложения и тем больше средств придется потра- тить на ее устранение. Дефект в требованиях может вылить- ся в один или несколько дефектов в проекте, которые могут привести к появлению множества дефектов в коде. Ошибка в требованиях может привести к разработке дополнительных компонентов архитектуры или подтолкнуть к неудачным ар- хитектурным решениям. Дополнительные архитектурные компоненты требуют написания дополнительного кода, те- стов и документации. С другой стороны, ошибка в требованиях может привести к выбрасыванию частей архитектуры, кода и тестов. Если идея устранения ошибок из чертежей дома перед заливкой фундамента бетоном кажется вам разумной, то вы согласитесь и с тем, что дефекты требований и архитектуры также следует уст- ранять до того, как они повлияют на более поздние этапы разработки.Кроме того, ошибки в требованиях или архитектуре обычно имеют более широ- кие следствия, чем ошибки конструирования. Одна ошибка в архитектуре может затронуть несколько классов и десятки методов, тогда как одна ошибка констру-Перекрестная ссылка О зависи- мости стоимости исправления дефектов от срока их присут- ствия в системе см. раздел «Об- ращение к данным» раздела 3.1.Сами ошибки более подробно обсуждаются в разделе 22.4.Перекрестная ссылка Контроль качества предварительных дей- ствий — например, определения требований и разработки архи- тектуры — в этой книге не рас- сматривается. Информацию по этим темам можно найти в кни- гах, указанных в разделе «До- полнительные ресурсы» в кон- це этой главы. ГЛАВА 20 Качество ПО467ирования скорее всего повлияет только на один метод или класс. Это еще одно убедительное обоснование как можно более раннего нахождения ошибок.Дефекты проникают в ПО на всех стадиях разработки, поэтому контро- лю качества следует уделять должное внимание на всех этапах проекта,начиная с самых ранних. Контроль качества нужно внести в планы в начале работы над программой; его следует выполнять по мере прогресса; нако- нец, он должен подчеркивать удачное завершение работы над проектом.20.5. Главный Закон Контроля Качества ПОНи в одном ресторане посетителей не кормят бесплатно, и даже если б кормили, никто не смог бы поручиться за качество блюд. Однако разра- ботка ПО — совсем не кулинарное искусство, и качество ПО имеет одну важную необычную особенность. Главный Закон Контроля Качества ПО заключа- ется в том, что повышение качества системы снижает расходы на ее разработку.В основе этого закона лежит одно важное наблюдение: лучшим способом повы- шения производительности труда программистов и качества ПО является мини- мизация времени, затрачиваемого на исправление кода, чем бы оно ни объясня- лось: изменениями требований, изменениями проекта или отладкой. Средняя для отрасли производительность труда программистов эквивалентна примерно 10–50 строкам кода на одного человека в день (с учетом всех затрат, не связанных с кодированием). Написание 10–50 строк кода требует нескольких минут, — на что же уходит остальное время?Такая, казалось бы, низкая производительность труда час- тично объясняется тем, что в подобных средних показате- лях учитывается время, не связанное непосредственно с программированием. Время тестировщиков, руководителей,секретарей — все эти факторы включены в данный показа- тель. Определение требований, разработка архитектуры и другие действия, не относящиеся к кодированию, также отра- жены в «строках кода в день». Однако основные временные затраты объясняются не этим.Самый длительный этап в большинстве проектов — отладка и исправление не- правильного кода. При традиционном цикле разработки ПО эти действия зани- мают около 50% времени (см. раздел 3.1). Сокращение потребности в отладке,достигаемое благодаря предотвращению ошибок, повышает производительность труда. Следовательно, наиболее очевидный метод сокращения графика разработ- ки — повышение качества ПО и снижение объема времени, уходящего на его отладку и исправление.Этот анализ подтверждается реальными данными. В обзоре 50 проектов,потребовавших более 400 человеколет и включивших почти 3 000 000строк кода, проведенном в Лаборатории проектирования ПО NASA, было обнаружено, что повышенное внимание к контролю качества позволяло снизить уровень ошибок, но не повышало общие расходы на разработку (Card, 1987).Перекрестная ссылка О разли- чиях между написанием отдель- ной программы и созданием программного продукта см. под- раздел «Программы, продукты,системы и системные продукты»раздела 27.5. 468ЧАСТЬ V Усовершенствование кодаВ исследовании, проведенном в IBM, были получены аналогичные результаты:Программным проектам с наименьшими уровнями дефектов соответствовали самые короткие графики разработки и максимальные показатели производитель- ности труда… устранение дефектов на самом деле — самый дорогой и длитель- ный этап разработки ПО (Jones, 2000).Это верно и для противоположного края шкалы. В одном исследовании1985 года ученые попросили 166 профессиональных программистов написать программы по одной и той же спецификации. Итоговые про- граммы содержали в среднем 220 строк, а на их написание ушло в среднем чуть меньше 5 часов. Результаты оказались поистине удивительными: программисты,работавшие над своими программами средний объем времени, допустили наиболь- шее число ошибок. Программисты, которым потребовалось больше или меньше времени, допустили значительно меньше ошибок (DeMarco and Lister, 1985). Ре- зультаты показаны на рисунке 20-2.Рис. 20-2. Ни при самом быстром, ни при самом медленном подходе к разработкеПО не наблюдается наибольший уровень дефектовВ сравнении с самой быстрой группой двум самым медленным группам понадо- билось примерно в 5 раз больше времени для достижения результата с примерно тем же уровнем дефектов. Таким образом, на создание ПО без дефектов не всегда уходит больше времени, чем на написание ПО с дефектами.Вероятно, в некоторых случаях контроль качества требует значительных затрат.Если вы пишете приложение управления космическим кораблем или медицин- ской системой жизнеобеспечения, высокие требования к надежности ПО делают проект более дорогим.В сравнении с традиционным циклом «кодирование — тестирование — отладка»улучшенная программа контроля качества ПО оказывается более экономичной.Она перенаправляет ресурсы от отладки и рефакторинга к предварительным этапам контроля качества. Предварительные этапы влияют на качество системы больше,чем последующие, поэтому время, потраченное на предварительных этапах, по- зволяет сэкономить больше времени потом. Результатом является снижение уровня ГЛАВА 20 Качество ПО469дефектов, сокращение сроков разработки и снижение затрат. В трех следующих главах вы найдете еще несколько примеров, иллюстрирующих Главный ЗаконКонтроля Качества ПО.Контрольный список: план контроля качества Идентифицировали ли вы специфические характеристики качества, имеющие особую важность в вашем проекте? Сообщили ли вы другим программистам целевые характеристики качества? Провели ли вы различие между внешними и внутренними характеристика- ми качества? Обдумали ли вы, как некоторые характеристики могут усиливать или ос- лаблять другие? Призывает ли ваш проект к использованию нескольких методик обнаруже- ния ошибок, ориентированных на поиск разных видов ошибок? Составили ли вы план контроля качества, охватывающий все этапы разра- ботки ПО? Оцениваете ли вы качество системы каким-нибудь образом, чтобы можно было определить, повышается оно или понижается? Понимают ли руководители, что контроль качества требует дополнительных расходов в начале проекта, но зато позволяет добиться общей экономии средств?Дополнительные ресурсыСоставить список книг для этой главы несложно, потому что методики повышения качества ПО и производительности труда описываются почти во всех трудах, посвященных эф- фективным методологиям разработки ПО. Сложность в том, чтобы выделить книги,касающиеся непосредственно качества ПО. Ниже я указал две такие работы.Ginac, Frank P. Customer Oriented Software Quality Assurance. Englewood Cliffs, NJ: PrenticeHall, 1998. В этой очень краткой книге описаны атрибуты качества, метрики каче- ства, программы контроля качества, роль тестирования в контроле качества, а так- же известные программы повышения качества, в том числе модель CMM, разрабо- танная в институте Software Engineering Institute, и стандарты ISO серии 9000.Lewis, William E. Software Testing and Continuous Quality Improvement, 2d ed. Auer- bach Publishing, 2000. В этой книге можно найти подробное обсуждение цикла контроля качества, а также методик тестирования. Кроме того, в ней вы найдете много контрольных форм и списков.Соответствующие стандартыIEEE Std 730-2002 — стандарт IEEE планирования контроля качества ПО.IEEE Std 1061-1998 — стандарт IEEE методологии метрик качества ПО.IEEE Std 1028-1997 — стандарт обзоров ПО.http://cc2e.com/2043http://cc2e.com/2050http://cc2e.com/2057 470ЧАСТЬ V Усовершенствование кодаIEEE Std 1008-1987 (R1993) — стандарт блочного тестирования ПО.IEEE Std 829-1998 — стандарт документирования тестов ПО.Ключевые моменты쐽Высокого качества можно достичь без дополнительных затрат, но для этого вы должны перераспределить ресурсы и предотвращать дефекты вместо того,чтобы их исправлять.쐽Стремление к одним характеристикам качества препятствует достижению дру- гих. Четко определите цели, имеющие для вас первостепенную важность, и сообщите об этом всем членам группы.쐽Никакая методика обнаружения дефектов не является достаточно эффектив- ной. Тестирование само по себе — не самый лучший способ устранения оши- бок. Составляя программу контроля качества, предусмотрите применение не- скольких методик, позволяющих обнаружить разные виды ошибок.쐽Существуют многие эффективные методики контроля качества, применяемые как во время конструирования, так и до его начала. Чем раньше вы обнаружи- те дефект, тем слабее он переплетется с остальным кодом и тем меньше вреда он успеет принести.쐽В мире программирования контроль качества ориентирован на процесс.В отличие от промышленного производства разработка ПО не включает по- вторяющегося этапа, влияющего на конечный продукт, поэтому качество ре- зультата определяется процессом, используемым для разработки ПО. 1   ...   53   54   55   56   57   58   59   60   ...   104

ГЛАВА 32 Самодокументирующийся код775граммой, и после тщательного изучения документации он обнаружил только та- кой комментарий:MOV AX, 723h ; R. I. P. L. V. B.Поломав над ним голову всю ночь, программист в итоге все исправил и пошел домой спать. Несколько месяцев спустя он встретился с автором программы на конференции и узнал, что комментарий означал «Rest in peace, Ludwig van Beet- hoven» (Покойся в мире, Людвиг ван Бетховен). Бетховен умер в 1827 году, кото- рому соответствует шестнадцатеричное значение 723. Необходимость использо- вания значения 723h не имела никакого отношения к комментарию. &%@*?#%

БиблиографияNewcomer, Joseph M. 2000. «Optimization: Your Worst Enemy,» May 2000, www.flounder. com/optimization.htm.Norcio, A. F. 1982. «Indentation, Documentation and Programmer Comprehension.» Proceedings:Human Factors in Computer Systems, March 15–17, 1982, Gaithersburg, MD: 118–20.Norman, Donald A. 1988. The Psychology of Everyday Things. New York, NY: Basic Books. (Also published in paperback as The Design of Everyday Things. New York, NY: Doubleday, 1990.)Oman, Paul and Shari Lawrence Pfleeger, eds. 1996. Applying Software Metrics. Los Alamitos, CA:IEEE Computer Society Press.Oman, Paul W., and Curtis R. Cook. 1990a. «The Book Paradigm for Improved Maintenance.» IEEESoftware, January, 39–45.Oman, Paul W., and Curtis R. Cook. 1990b. «Typographic Style Is More Than Cosmetic.» Commu-nications of the ACM 33, no. 5 (May): 506–20.Ostrand, Thomas J., and Elaine J. Weyuker. 1984. «Collecting and Categorizing Software Error Data in an Industrial Environment.» Journal of Systems and Software 4, no. 4 (November): 289–300.Page-Jones, Meilir. 2000. Fundamentals of Object-Oriented Design in UML. Boston, MA: Addison-Wesley.Page-Jones, Meilir. 1988. The Practical Guide to Structured Systems Design. Englewood Cliffs, NJ:Yourdon Press.Parikh, G., and N. Zvegintzov, eds. 1983. Tutorial on Software Maintenance. Los Alamitos, CA: IEEEComputer Society Press.Parikh, Girish. 1986. Handbook of Software Maintenance. New York, NY: John Wiley & Sons.Parnas, David L. 1972. «On the Criteria to Be Used in Decomposing Systems into Modules.»Communications of the ACM 5, no. 12 (December): 1053–58.Parnas, David L. 1976. «On the Design and Development of Program Families.» IEEE Transactionson Software Engineering SE-2, 1 (March): 1–9.Parnas, David L. 1979. «Designing Software for Ease of Extension and Contraction.» IEEE Trans-actions on Software Engineering SE-5, no. 2 (March): 128–38.Parnas, David L. 1999. ACM Fellow Profile: David Lorge Parnas,» ACM Software Engineering Notes,May 1999, 10–14.Parnas, David L., and Paul C. Clements. 1986. «A Rational Design Process: How and Why to FakeIt.» IEEE Transactions on Software Engineering SE-12, no. 2 (February): 251–57.Parnas, David L., Paul C. Clements, and D. M. Weiss. 1985. «The Modular Structure of ComplexSystems.» IEEE Transactions on Software Engineering SE-11, no. 3 (March): 259–66.Perrott, Pamela. 2004. Private communication.Peters, L. J., and L. L. Tripp. 1976. «Is Software Design Wicked» Datamation, Vol. 22, No. 5 (May1976), 127–136.Peters, Lawrence J. 1981. Handbook of Software Design: Methods and Techniques. New York, NY:Yourdon Press.Peters, Lawrence J., and Leonard L. Tripp. 1977. «Comparing Software Design Methodologies.»Datamation, November, 89– 94.Peters, Tom. 1987. Thriving on Chaos: Handbook for a Management Revolution. New York, NY:Knopf.Petroski, Henry. 1994. Design Paradigms: Case Histories of Error and Judgment in Engineering.Cambridge, U.K.: Cambridge University Press.Pietrasanta, Alfred M. 1990. «Alfred M. Pietrasanta on Improving the Software Process.» SoftwareEngineering: Tools, Techniques, Practices 1, no. 1 (May/ June): 29–34. Библиография857Pietrasanta, Alfred M. 1991a. «A Strategy for Software Process Improvement.» Ninth Annual PacificNorthwest Software Quality Conference, October 7–8, 1991. Oregon Convention Center, Portland, ORPietrasanta, Alfred M. 1991b. «Implementing Software Engineering in IBM.» Keynote address. NinthAnnual Pacific Northwest Software Quality Conference, October 7– 8, 1991. Oregon ConventionCenter, Portland, OR.Pigoski, Thomas M. 1997. Practical Software Maintenance. New York, NY: John Wiley & Sons.Pirsig, Robert M. 1974. Zen and the Art of Motorcycle Maintenance: An Inquiry into Values. WilliamMorrow.Plauger, P. J. 1988. «A Designer’s Bibliography.» Computer Language, July, 17–22.Plauger, P. J. 1993. Programming on Purpose: Essays on Software Design. New York, NY: PrenticeHall.Plum, Thomas. 1984. C Programming Guidelines. Cardiff, NJ: Plum Hall.Polya, G. 1957. How to Solve It: A New Aspect of Mathematical Method, 2d ed. Princeton, NJ: PrincetonUniversity Press.Post, Ed. 1983. «Real Programmers Don’t Use Pascal,» Datamation, July 1983, 263– 265.Prechelt, Lutz. 2000. «An Empirical Comparison of Seven Programming Languages,» IEEE Computer,October 2000, 23–29.Pressman, Roger S. 1987. Software Engineering: A Practitioner’s Approach. New York, NY: McGraw-Hill.Pressman, Roger S. 1988. Making Software Engineering Happen: A Guide for Instituting theTechnology. Englewood Cliffs, NJ: Prentice Hall.Putnam, Lawrence H. 2000. «Familiar Metric Management – Effort, Development Time, and DefectsInteract.» Downloadable from www.qsm.com.Putnam, Lawrence H., and Ware Myers. 1992. Measures for Excellence: Reliable Software On Time,Within Budget. Englewood Cliffs, NJ: Yourdon Press, 1992.Putnam, Lawrence H., and Ware Myers. 1997. Industrial Strength Software: Effective ManagementUsing Measurement. Washington, DC: IEEE Computer Society Press.Putnam, Lawrence H., and Ware Myers. 2000. «What We Have Learned.» Downloadable fromwww.qsm.com, June 2000.Raghavan, Sridhar A., and Donald R. Chand. 1989. «Diffusing Software-Engineering Methods.»IEEE Software, July, 81–90.Ramsey, H. Rudy, Michael E. Atwood, and James R. Van Doren. 1983. «Flowcharts Versus ProgramDesign Languages: An Experimental Comparison.» Communications of the ACM 26, no. 6 (June):445–49.Ratliff, Wayne. 1987. Interview in Solution System.Raymond, E. S. 2000. «The Cathedral and the Bazaar,» www.catb.org/esr/writings/cathedral-bazaar.Raymond, Eric S. 2004. The Art of Unix Programming. Boston, MA: Addison-Wesley.Rees, Michael J. 1982. «Automatic Assessment Aids for Pascal Programs.» ACM Sigplan Notices 17,no. 10 (October): 33–42.Reifer, Donald. 2002. «How to Get the Most Out of Extreme Programming/Agile Methods,»Proceedings, XP/Agile Universe 2002. New York, NY: Springer; 185–196.Reingold, Edward M., and Wilfred J. Hansen. 1983. Data Structures. Boston, MA: Little, Brown.Rettig, Marc. 1991. «Testing Made Palatable.» Communications of the ACM 34, no. 5 (May): 25–29.Riel, Arthur J. 1996. Object-Oriented Design Heuristics. Reading, MA: Addison-Wesley.Rittel, Horst, and Melvin Webber. 1973. «Dilemmas in a General Theory of Planning.» Policy Sciences4: 155–69. 858БиблиографияRobertson, Suzanne, and James Robertson, 1999. Mastering the Requirements Process. Reading,MA: Addison-Wesley.Rogers, Everett M. 1995. Diffusion of Innovations, 4th ed. New York, NY: The Free Press.Rombach, H. Dieter. 1990. «Design Measurements: Some Lessons Learned.» IEEE Software, March,17–25.Rubin, Frank. 1987. «‘GOTO Considered Harmful’ Considered Harmful.» Letter to the editor.Communications of the ACM 30, no. 3 (March): 195–96. Follow-up letters in 30, no. 5 (May 1987):351–55; 30, no. 6 (June 1987): 475–78; 30, no. 7 (July 1987): 632–34; 30, no. 8 (August 1987):659–62; 30, no. 12 (December 1987): 997, 1085.Sackman, H., W. J. Erikson, and E. E. Grant. 1968. «Exploratory Experimental Studies Comparing Online and Offline Programming Performance.» Communications of the ACM 11, no. 1 (January): 3–11.Schneider, G. Michael, Johnny Martin, and W. T. Tsai. 1992. «An Experimental Study of FaultDetection in User Requirements Documents,» ACM Transactions on Software Engineering andMethodology, vol 1, no. 2, 188–204.Schulmeyer, G. Gordon. 1990. Zero Defect Software. New York, NY: McGraw-Hill.Sedgewick, Robert. 1997. Algorithms in C, Parts 1-4, 3d ed. Boston, MA: Addison-Wesley.Sedgewick, Robert. 2001. Algorithms in C, Part 5, 3d ed. Boston, MA: Addison-Wesley.Sedgewick, Robert. 1998. Algorithms in C++, Parts 1-4, 3d ed. Boston, MA: Addison-Wesley.Sedgewick, Robert. 2002. Algorithms in C++, Part 5, 3d ed. Boston, MA: Addison-Wesley.Sedgewick, Robert. 2002. Algorithms in Java, Parts 1-4, 3d ed. Boston, MA: Addison-Wesley.Sedgewick, Robert. 2003. Algorithms in Java, Part 5, 3d ed. Boston, MA: Addison-Wesley.SEI 1995. The Capability Maturity Model: Guidelines for Improving the Software Process, SoftwareEngineering Institute, Reading, MA: Addison-Wesley, 1995.SEI, 2003. «Process Maturity Profile: Software CMM®, CBA IPI and SPA Appraisal Results: 2002Year End Update,» Software Engineering Institute, April 2003.Selby, Richard W., and Victor R. Basili. 1991. «Analyzing Error-Prone System Structure.» IEEETransactions on Software Engineering SE-17, no. 2 (February): 141–52.SEN 1990. «Subsection on Telephone Systems,» Software Engineering Notes, April 1990, 11–14.Shalloway, Alan, and James R. Trott. 2002. Design Patterns Explained. Boston, MA: Addison-Wesley.Sheil, B. A. 1981. «The Psychological Study of Programming.» Computing Surveys 13, no. 1 (March):101–20.Shen, Vincent Y., et al. 1985. «Identifying Error-Prone Software—An Empirical Study.» IEEETransactions on Software Engineering SE-11, no. 4 (April): 317–24.Sheppard, S. B., et al. 1978. «Predicting Programmers’ Ability to Modify Software.» TR 78-388100-3, General Electric Company, May.Sheppard, S. B., et al. 1979. «Modern Coding Practices and Programmer Performance.» IEEEComputer 12, no. 12 (December): 41–49.Shepperd, M., and D. Ince. 1989. «Metrics, Outlier Analysis and the Software Design Process.»Information and Software Technology 31, no. 2 (March): 91–98.Shirazi, Jack. 2000. Java Performance Tuning. Sebastopol, CA: O’Reilly & Associates.Shlaer, Sally, and Stephen J. Mellor. 1988. Object Oriented Systems Analysis—Modeling the Worldin Data. Englewood Cliffs, NJ: Prentice Hall.Shneiderman, Ben, and Richard Mayer. 1979. «Syntactic/Semantic Interactions in ProgrammerBehavior: A Model and Experimental Results.» International Journal of Computer and InformationSciences 8, no. 3: 219–38. Библиография859Shneiderman, Ben. 1976. «Exploratory Experiments in Programmer Behavior.» International Journalof Computing and Information Science 5: 123–43.Shneiderman, Ben. 1980. Software Psychology: Human Factors in Computer and InformationSystems. Cambridge, MA: Winthrop.Shneiderman, Ben. 1987. Designing the User Interface: Strategies for Effective Human-ComputerInteraction. Reading, MA: Addison-Wesley.Shull, et al. 2002. «What We Have Learned About Fighting Defects,» Proceedings, Metrics 2002.IEEE; 249–258.Simon, Herbert. 1996. The Sciences of the Artificial, 3d ed. Cambridge, MA: MIT Press.Simon, Herbert. The Shape of Automation for Men and Management. Harper and Row, 1965.Simonyi, Charles, and Martin Heller. 1991. «The Hungarian Revolution.» BYTE, August, 131–38.Smith, Connie U., and Lloyd G. Williams. 2002. Performance Solutions: A Practical Guide to CreatingResponsive, Scalable Software. Boston, MA: Addison-Wesley.Software Productivity Consortium. 1989. Ada Quality and Style: Guidelines for Professional Progra-mmers. New York, NY: Van Nostrand Reinhold.Soloway, Elliot, and Kate Ehrlich. 1984. «Empirical Studies of Programming Knowledge.» IEEETransactions on Software Engineering SE-10, no. 5 (September): 595–609.Soloway, Elliot, and Sitharama Iyengar, eds. 1986. Empirical Studies of Programmers. Norwood,NJ: Ablex.Soloway, Elliot, Jeffrey Bonar, and Kate Ehrlich. 1983. «Cognitive Strategies and Looping Constructs:An Empirical Study.» Communications of the ACM 26, no. 11 (November): 853–60.Solution Systems. 1987. World-Class Programmers’ Editing Techniques: Interviews with SevenProgrammers. South Weymouth, MA: Solution Systems.Sommerville, Ian. 1989. Software Engineering, 3d ed. Reading, MA: Addison-Wesley.Spier, Michael J. 1976. «Software Malpractice—A Distasteful Experience.» Software—Practice andExperience 6: 293–99.Spinellis, Diomidis. 2003. Code Reading: The Open Source Perspective. Boston, MA: Addison-Wesley.SPMN. 1998. Little Book of Configuration Management. Arlington, VA; Software Program ManagersNetwork.Starr, Daniel. 2003. «What Supports the Roof?» Software Development. July 2003, 38–41.Stephens, Matt. 2003. «Emergent Design vs. Early Prototyping,» May 26, 2003, www.softwarereality.com/design/early_prototyping.jsp.Stevens, Scott M. 1989. «Intelligent Interactive Video Simulation of a Code Inspection.» Communi-cations of the ACM 32, no. 7 (July): 832–43.Stevens, W., G. Myers, and L. Constantine. 1974. «Structured Design.» IBM Systems Journal 13, no.2 (May): 115–39.Stevens, Wayne. 1981. Using Structured Design. New York, NY: John Wiley & Sons.Stroustrup, Bjarne. 1997. The C++ Programming Language, 3d ed. Reading, MA: Addison-Wesley.Strunk, William, and E. B. White. 2000. Elements of Style, 4th ed. Pearson.Sun Microsystems, Inc. 2000. «How to Write Doc Comments for the Javadoc Tool,» 2000. Available from http://java.sun.com/j2se/javadoc/writingdoccomments/.Sutter, Herb. 2000. Exceptional C++: 47 Engineering Puzzles, Programming Problems, and Solutions.Boston, MA: Addison-Wesley.Tackett, Buford D., III, and Buddy Van Doren. 1999. «Process Control for Error Free Software: ASoftware Success Story,» IEEE Software, May 1999. 860БиблиографияTenner, Edward. 1997. Why Things Bite Back: Technology and the Revenge of UnintendedConsequences. Vintage Books.Tenny, Ted. 1988. «Program Readability: Procedures versus Comments.» IEEE Transactions onSoftware Engineering SE-14, no. 9 (September): 1271–79.Thayer, Richard H., ed. 1990. Tutorial: Software Engineering Project Management. Los Alamitos,CA: IEEE Computer Society Press.Thimbleby, Harold. 1988. «Delaying Commitment.» IEEE Software, May, 78–86.Thomas, Dave, and Andy Hunt. 2002. «Mock Objects,» IEEE Software, May/June 2002.Thomas, Edward J., and Paul W. Oman. 1990. «A Bibliography of Programming Style.» ACM SigplanNotices 25, no. 2 (February): 7–16.Thomas, Richard A. 1984. «Using Comments to Aid Program Maintenance.» BYTE, May, 415–22.Tripp, Leonard L., William F. Struck, and Bryan K. Pflug. 1991. «The Application of Multiple TeamInspections on a Safety-Critical Software Standard,» Proceedings of the 4th Software EngineeringStandards Application Workshop, Los Alamitos, CA: IEEE Computer Society Press.U.S. Department of Labor. 1990. «The 1990– 91 Job Outlook in Brief.» Occupational OutlookQuarterly, Spring. U.S. Government Printing Office. Document 1990-282-086/20007.Valett, J., and F. E. McGarry. 1989. «A Summary of Software Measurement Experiences in the SoftwareEngineering Laboratory.» Journal of Systems and Software 9, no. 2 (February): 137–48.Van Genuchten, Michiel. 1991. «Why Is Software Late? An Empirical Study of Reasons for Delay inSoftware Development.» IEEE Transactions on Software Engineering SE-17, no. 6 (June): 582–90.Van Tassel, Dennie. 1978. Program Style, Design, Efficiency, Debugging, and Testing, 2d ed. EnglewoodCliffs, NJ: Prentice Hall.Vaughn-Nichols, Steven. 2003. «Building Better Software with Better Tools,» IEEE Computer,September 2003, 12–14.Vermeulen, Allan, et al. 2000. The Elements of Java Style. Cambridge University Press.Vessey, Iris, Sirkka L. Jarvenpaa, and Noam Tractinsky. 1992. «Evaluation of Vendor Products: CASETools as Methodological Companions.» Communications of the ACM 35, no. 4 (April): 91–105.Vessey, Iris. 1986. «Expertise in Debugging Computer Programs: An Analysis of the Content ofVerbal Protocols.» IEEE Transactions on Systems, Man, and Cybernetics SMC-16, no. 5 (September/October): 621–37.Votta, Lawrence G., et al. 1991. «Investigating the Application of Capture-Recapture Techniques to Requirements and Design Reviews.» Proceedings of the Sixteenth Annual Software EngineeringWorkshop, December 4–5, 1991. Greenbelt, MD: Goddard Space Flight Center. Document SEL-91-006.Walston, C. E., and C. P. Felix. 1977. «A Method of Programming Measurement and Estimation.»IBM Systems Journal 16, no. 1: 54–73.Ward, Robert. 1989. A Programmer’s Introduction to Debugging C. Lawrence, KS: R & D Publications.Ward, William T. 1989. «Software Defect Prevention Using McCabe’s Complexity Metric.» Hewlett-Packard Journal, April, 64–68.Webster, Dallas E. 1988. «Mapping the Design Information Representation Terrain.» IEEE Computer,December, 8–23.Weeks, Kevin. 1992. «Is Your Code Done Yet?» Computer Language, April, 63–72.Weiland, Richard J. 1983. The Programmer’s Craft: Program Construction, Computer Architecture,and Data Management. Reston, VA: Reston Publishing.Weinberg, Gerald M. 1983. «Kill That Code!» Infosystems, August, 48–49. 1   ...   96   97   98   99   100   101   102   103   104


1   ...   33   34   35   36   37   38   39   40   ...   104

ГЛАВА 12 Основные типы данных
293
Пример логического условия, чье назначение понятно (Java)
finished = ( ( elementIndex < 0 ) || ( MAX_ELEMENTS < elementIndex ) );
repeatedEntry = ( elementIndex == lastElementIndex );
if ( finished || repeatedEntry ) {
}
Используйте логические переменные для упрощения сложных условий
Чтобы правильно закодировать сложное условие, часто приходится делать несколько попыток. Когда через некоторое время нужно модифицировать это условие, быва- ет сложно разобраться, что же оно проверяет. Логические переменные могут уп- ростить проверку. В предыдущем примере программа на самом деле проверяет два условия: завершено ли выполнение метода и выполняется ли этот метод повторно.
Создав логические переменные
finished и repeatedEntry, вы упрощаете if-проверку:
теперь ее легче читать, легче изменять, и она меньше подвержена ошибкам.
Вот другой пример сложного условия:
Пример сложного условия (Visual Basic)
If ( ( document.AtEndOfStream() ) And ( Not inputError ) ) And _
( ( MIN_LINES <= lineCount ) And ( lineCount <= MAX_LINES ) ) And _
( Not ErrorProcessing() ) Then
‘ делаем что-то
End If
Условие в примере достаточно запутанное, но все же часто встречающееся. Оно налагает на читателя тяжелую умственную нагрузку. Могу предположить, что вы даже не попытаетесь разобраться в этой
if-проверке, а посмотрите и скажете: «Раз- берусь с этим позже, если и впрямь понадобится». Обратите внимание на эту мысль,
ведь это абсолютно то же самое, что сделают другие люди, читая ваш код, содер- жащий подобные условия.
А вот как переписать этот код, используя логические переменные, добавленные для упрощения условия:
Пример упрощенного условия (Visual Basic)
allDataRead = ( document.AtEndOfStream() ) And ( Not inputError )
legalLineCount = ( MIN_LINES <= lineCount ) And ( lineCount <= MAX_LINES )
Вот упрощенная проверка.
If ( allDataRead ) And ( legalLineCount ) And ( Not ErrorProcessing() ) Then
‘ делаем что-то
End If
Вторая версия проще. Думаю, вы легко прочитаете логическое выражение в
if- проверке.
>

294
ЧАСТЬ III Переменные
Создайте свой логический тип в случае необходимости Некоторые язы- ки, такие как C++, Java и Visual Basic, имеют предопределенный логический тип,
другие — скажем, C — не имеют. В языках, подобных C, вы можете определить свой логический тип. В C это можно сделать так:
Пример определения типа BOOLEAN
с помощью обычного typedef на языке C
typedef int BOOLEAN;
Или можно это сделать, определив дополнительно значения
true и false:
Пример определения типа Boolean с помощью Enum на языке C
enum Boolean {
True=1,
False=(!True)
};
Объявление переменных как
BOOLEAN, а не int делает их последующее использо- вание более очевидным, а вашу программу — самодокументируемой.
12.6. Перечислимые типы
Перечислимым называется тип данных, который позволяет описать на естествен- ном языке каждый элемент класса или объекта. Перечислимые типы реализова- ны в C++ и Visual Basic и обычно используются, когда вы знаете все возможные значения переменной и хотите выразить их словами. Вот примеры перечисли- мых типов на Visual Basic:
Примеры перечислимых типов (Visual Basic)
Public Enum Color
Color_Red
Color_Green
Color_Blue
End Enum
Public Enum Country
Country_China
Country_England
Country_France
Country_Germany
Country_India
Country_Japan
Country_Usa
End Enum
Public Enum Output
Output_Screen
Output_Printer


ГЛАВА 12 Основные типы данных
295
Output_File
End Enum
Перечислимые типы — мощная альтернатива устаревшим схемам, в которых вы подробно расписываете: «1 — это красный, 2 — зеленый, 3 — голубой…». В связи с этим можно предложить следующие принципы применения таких типов.
Используйте перечислимые типы для читабельности Вместо выражений вроде:
if chosenColor = 1
вы можете написать более читабельную фразу, например:
if chosenColor = Color_Red
Каждый раз, когда вы видите числовую константу, подумайте: не заменить ли ее перечислимым типом.
Перечислимые типы особенно полезны для определения параметров методов. Кто поймет, что означают параметры этой функции?
Пример вызова функции, в котором стоило бы
использовать перечислимые типы (C++)
int result = RetrievePayrollData( data, true, false, false, true );
А параметры в этом вызове функции гораздо понятнее:
Пример вызова функции, использующей
перечислимые типы для читабельности (C++)
int result = RetrievePayrollData(
data,
EmploymentStatus_CurrentEmployee,
PayrollType_Salaried,
SavingsPlan_NoDeduction,
MedicalCoverage_IncludeDependents
);
Используйте перечислимые типы для надежности В некоторых языках (осо- бенно в Ada) перечислимые типы заставляют компилятор выполнять более тщатель- ную проверку типов, чем при работе с целыми значениями и константами. При использовании именованных констант компилятору неоткуда узнать, что единствен- ные возможные значения переменной — это
Color_Red, Color_Green и Color_Blue.
Компилятор не будет возражать, встретив выражения вроде
color = Country_England
или
country = Output_Printer. Если вы используете перечислимые типы, то при объяв- лении переменной типа
Color компилятор позволит присвоить ей только значе- ния
Color_Red, Color_Green или Color_Blue.
Используйте перечислимые типы для модифицируемости Перечислимые типы позволяют вам легко модифицировать код. Обнаружив пробел в своей схе- ме «1 — это красный, 2 — зеленый, 3 — голубой», придется пройти по коду и из- менить все вхождения
1, 2, 3 и т. д. А используя перечислимые типы, вы можете

296
ЧАСТЬ III Переменные продолжить добавление элементов в список, просто поместив их в определение типа и перекомпилировав программу.
Используйте перечислимые типы как альтернативу логическим пере-
менным Очень часто логической переменной недостаточно для представления необходимых значений. Представьте, что у вас есть метод, возвращающий
True,
если он успешно выполнил свою задачу, и
False в противном случае. Позднее вы можете обнаружить, что на самом деле у вас есть два варианта
False. Первый оз- начает, что задание не выполнено, но это повлияло только на сам метод; второй —
что невыполнение задания привело к фатальной ошибке, которую надо передать в остальную часть программы. В этом случае перечислимый тип со значениями
Status_Success, Status_Warning и Status_FatalError подходит больше, чем логический тип со значениями
true и false. Эту схему очень легко расширять новыми вариан- тами успешного или неудачного выполнения.
Проверяйте некорректные значения Когда вы используете перечислимый тип в условиях
if или case, проверяйте появление недопустимых значений. В переклю- чателе
case для перехвата неправильных значений применяется оператор else:
Хороший пример проверки некорректных значений
в перечислимом типе (Visual Basic)
Select Case screenColor
Case Color_Red
Case Color_Blue
Case Color_Green
Здесь выполняется проверка неправильного значения.
Case Else
DisplayInternalError( False, “Internal Error 752: Invalid color.” )
End Select
Настройте первый и последний элемент перечислимого типа для исполь-
зования в качестве границ циклов Определение первого и последнего эле- мента в перечислении в виде
Color_First, Color_Last, Country_First, Country_Last и т. д., позволяет писать циклы, проходящие по всем элементам типа. Вы настраи- ваете свой перечислимый тип, используя явные значения:
Пример установки значений First и Last перечислимого типа (Visual Basic)
Public Enum Country
Country_First = 0
Country_China = 0
Country_England = 1
Country_France = 2
Country_Germany = 3
Country_India = 4
Country_Japan = 5
>


ГЛАВА 12 Основные типы данных
297
Country_Usa = 6
Country_Last = 6
End Enum
Теперь элементы
Country_First и Country_Last могут служить как границы цикла:
Хороший пример циклического вызова элементов перечисления (Visual Basic)
‘ Вычисляем курсы валют по отношению к долларам США.
Dim usaCurrencyConversionRate( Country_Last ) As Single
Dim iCountry As Country
For iCountry = Country_First To Country_Last usaCurrencyConversionRate( iCountry ) = ConversionRate( Country_Usa, iCountry )
Next
Зарезервируйте первый элемент перечислимого типа как недопустимый
При объявлении перечислимого типа зарезервируйте его первый элемент в ка- честве недопустимого значения. Большинство компиляторов присваивает перво- му элементу перечисления значение
0. Объявление некорректным элемента, при- равненного
0, поможет выявить неправильно проинициализированные перемен- ные, так как они вероятнее всего будут равны
0, чем любому другому неправиль- ному значению.
Вот как при этом подходе будет выглядеть перечислимый тип
Country:
Пример объявления первого элемента перечисления недопустимым (Visual Basic)
Public Enum Country
Country_InvalidFirst = 0
Country_First = 1
Country_China = 1
Country_England = 2
Country_France = 3
Country_Germany = 4
Country_India = 5
Country_Japan = 6
Country_Usa = 7
Country_Last = 7
End Enum
Точно укажите в стандартах кодирования для проекта, как должны ис-
пользоваться первый и последний элементы, и неукоснительно придержи-
вайтесь этого Использование в перечислениях элементов InvalidFirst, First и
Last может сделать объявления массивов и операторы циклов читабельнее. Но есть вероятность неразберихи в таких вопросах: с чего начинаются значимые элементы перечисления — с
0 или 1 и является ли допустимыми первый и последний эле- менты? Если используется эта технология, стандарты кодирования для проекта в целях уменьшения количества ошибок должны требовать применения элементов
InvalidFirst, First и Last для всех без исключения перечислений.

298
ЧАСТЬ III Переменные
Помните о подводных камнях в присваивании явных значений элементам
перечисления Некоторые языки позволяют присваивать конкретные значения элементам перечисления:
Пример явного присваивания значений элементам перечисления (C++)
enum Color {
Color_InvalidFirst = 0,
Color_First = 1,
Color_Red = 1,
Color_Green = 2,
Color_Blue = 4,
Color_Black = 8,
Color_Last = 8
};
Если в этом примере вы объявите переменную цикла типа
Color и попробуете пройти по всем элементам
Color, то этой переменной наряду с допустимыми зна- чениями 1, 2, 4 и 8 будут присваиваться и недопустимые — 3, 5, 6 и 7.
Если ваш язык не поддерживает
перечислимые типы
Если в вашем языке нет перечислимых типов, их можно имитировать, используя глобальные переменные или клас- сы. Например, такие объявления можно использовать в языке
Java:
Пример имитации перечислимых типов (Java)
// Создание перечислимого типа Country.
class Country {
private Country() {}
public static final Country China = new Country();
public static final Country England = new Country();
public static final Country France = new Country();
public static final Country Germany = new Country();
public static final Country India = new Country();
public static final Country Japan = new Country();
}
// Создание перечислимого типа Output.
class Output {
private Output() {}
public static final Output Screen = new Output();
public static final Output Printer = new Output();
public static final Output File = new Output();
}
Перекрестная ссылка На момент написания этой книги Java не поддерживает перечислимых ти- пов. Но, возможно, уже делает это к моменту, когда вы это чи- таете.


ГЛАВА 12 Основные типы данных
299
Такие перечислимые типы делают вашу программу читабельнее, поскольку вы можете использовать открытые члены класса, например,
Country. England и Out-
put. Screen, вместо именованных констант. Именно такой способ создания пере- числимых типов еще и обеспечивает безопасность типов, так как каждый тип объяв- лен как класс и компилятор будет проверять некорректные присваивания вроде
Output output = Country. England (Bloch, 2001).
В языках, не поддерживающих классы, такого же эффекта можно достичь, акку- ратно используя глобальные переменные для каждого элемента перечисления.
12.7. Именованные константы
Именованные константы аналогичны переменным за исключением того, что вы не можете изменить значение константы после ее инициализации. Именованные кон- станты позволяют ссылаться на постоянные величины, например, максимальное количество работников, используя имя, а не число —
MAXIMUM_EMPLOYEES, а не 1000.
Применение именованных констант — это способ «параметризации» программы,
т. е. размещение некоторой характеристики, которая может измениться, в пара- метре. В результате вам потребуется изменить его только в одном месте, а не по всей программе. Если вы когда-нибудь объявляли массив такой длины, какой, по- вашему, должно хватить для любых ситуаций, а потом выяснялось, что массив слишком мал, вы оцените значение именованных констант. Когда размер масси- ва изменяется, вы меняете только определение константы, используемой для объяв- ления массива. Такое «централизованное управление» имеет большое значение для того, чтобы сделать работу с ПО действительно приятной: упростить его поддер- жку и модификацию.
Используйте именованные константы в объявлениях данных Именован- ные константы повышают читабельность и удобство сопровождения объявлений данных и выражений, которым необходимо знать размеры обрабатываемых дан- ных. В следующем примере для описания длины телефонных номеров работни- ков лучше использовать
LOCAL_NUMBER_LENGTH, а не число 7.
Хороший пример применения именованных констант
в объявлениях данных (Visual Basic)
Const AREA_CODE_LENGTH = 3
LOCAL_NUMBER_LEN GTH здесь объявляется как константа.
Const LOCAL_NUMBER_LENGTH = 7
Type PHONE_NUMBER
areaCode( AREA_CODE_LENGTH ) As String
А здесь используется.
localNumber( LOCAL_NUMBER_LENGTH ) As String
End Type
>
>

300
ЧАСТЬ III Переменные
’ Убедимся, что все символы в телефонном номере — это цифры.
И здесь тоже используется.
For iDigit = 1 To LOCAL_NUMBER_LENGTH
If ( phoneNumber.localNumber( iDigit ) < “0” ) Or _
( “9” < phoneNumber.localNumber( iDigit ) ) Then
‘ Выполняем обработку ошибок.
Это простой пример, но вы вполне можете представить программу, в которой сведения о длине телефонных номеров требуются во многих местах.
На момент создания программы все работники живут в одной стране, поэтому вам нужно только семь цифр для их телефонных номеров. По мере расширения ком- пании ее филиалы открываются в разных странах, и вам понадобятся более длин- ные телефонные номера. Если вы параметризовали эту длину, вам надо сделать изменение только в одном месте — в определении именованной константы
LOCAL_
NUMBER_LENGTH.
Как вы, наверное, поняли, именованные константы делают сопровождение программы удобнее. Как правило, любая технология, централизующая управление объектами, подвер- женными изменениям, — это хороший способ уменьшить затраты на сопровождение (Glass, 1991).
Избегайте литеральных значений, даже «безопасных» Как вы думаете, что в следующем цикле означает число
12?
Пример непонятного кода (Visual Basic)
For i = 1 To 12
profit( i ) = revenue( i ) – expense( i )
Next
Исходя из специфического содержимого кода, можно предположить, что выпол- няется цикл по 12 месяцам в году. Но вы
уверены? Вы поставите на это свое со- брание «Монти Пайтон»?
В этом случае вам не нужно использовать именованные константы для поддерж- ки расширяемости: вряд ли число месяцев в году изменится в ближайшем буду- щем. Но если при написании кода остается хотя бы тень сомнения в его предназ- начении, развейте ее с помощью хорошо названной именованной константы,
например, так:
Пример более понятного кода (Visual Basic)
For i = 1 To NUM_MONTHS_IN_YEAR
profit( i ) = revenue( i ) – expense( i )
Next
Это уже лучше, но для завершения примера индекс цикла тоже нужно назвать более информативно:
Дополнительные сведения О зна- чении централизованного управ- ления см. стр. 57–60 в книге
«Software Conflict» (Glass, 1991).
>