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

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

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

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

Добавлен: 30.11.2023

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

Скачиваний: 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


ГЛАВА 8 Защитное программирование
195
Решив передать исключение, удостоверьтесь, что уровень абстракции исключения и метода совпадают. Вот как не надо делать:
Плохой пример Java-класса, который генерирует исключение
на неверном уровне абстракции
class Employee {
Объявление исключения с неправильным уровнем абстракции.
public TaxId GetTaxId() throws EOFException {
}
}
Функция
GetTaxId() передает низкоуровневое исключение EOFException вызыва- ющей стороне. Она не обрабатывает исключение сама, а раскрывает некоторые детали своей реализации, генерируя низкоуровневое исключение. Это привязы- вает клиентский код не к классу
Employee, а к коду внутри класса Employee, гене- рирующему исключение
EOFException. Инкапсуляция нарушена, управляемость кода ухудшается.
Вместо этого код
GetTaxId() должен передавать исключение, соответствующее интерфейсу класса, частью которого он является, например, так:
Хороший пример Java-класса, который генерирует исключение
на правильном уровне абстракции
class Employee {
Объявление исключения, соответствующего уровню абстракции.
public TaxId GetTaxId() throws EmployeeDataNotAvailable {
}
}
Код обработки исключений внутри
GetTaxId(), возможно, просто устанавливает соответствие между исключениями
io_disk_not_ready и EmployeeDataNotAvailable,
что гораздо лучше, так как сохраняется абстракция интерфейса.
Вносите в описание исключения всю информацию о его причинах Каж- дое исключение возникает при определенных обстоятельствах, обнаруженных ко- дом в момент генерации этого исключения. Эти сведения недоступны тому, кто читает сообщение об исключении. Убедитесь, что это сообщение содержит до- статочно информации для понимания причины генерации исключения. Напри- мер, если причиной был неправильный индекс элемента массива, включите в опи- сание верхнюю и нижнюю границы массива и некорректное значение индекса.
>
>

196
ЧАСТЬ II Высококачественный код
Избегайте пустых блоков catch Иногда возникает искушение оставить без внимания исключение, которое вы не знаете, как обработать. Например:
Плохой пример игнорирования исключения (Java)
try {
// много кода
} catch ( AnException exception ) {
}
Такой подход говорит о том, что либо код внутри блока
try генерирует исключе- ние без причины, либо код в блоке
catch не обрабатывает возможную исключи- тельную ситуацию. Выясните, в чем суть проблемы, и исправьте блоки
try или catch.
Изредка можно столкнуться с ситуацией, когда исключение более низкого уровня не соответствует уровню абстракции вызывающего метода. В этом случае хотя бы задокументируйте, почему блок
catch должен быть пустым. Это можно сделать в комментариях или записав сообщение в файл журнала, например:
Хороший пример игнорирования исключения на Java
try {
// много кода
} catch ( AnException exception ) {
LogError( ”Unexpected exception” );
}
Выясните, какие исключения генерирует используемая библиотека Ес- ли вы работаете с языком, не требующим, чтобы метод или класс объявляли воз- можные исключения, убедитесь, что вам известно, какие исключения могут воз- никнуть в коде используемых библиотек. Неперехваченное исключение из биб- лиотеки приведет к аварийному завершению программы так же легко, как и исключение, сгенерированное в вашем коде. Если библиотечные исключения не документированы, создайте код-прототип и протестируйте библиотеки, чтобы их выявить.
Рассмотрите вопрос о централизованном выводе информации об исклю-
чениях Поддержание целостности в обработке исключений обеспечивает цен- трализованный генератор сообщений об исключениях. Он содержит базу знаний о том, какие это исключения, как каждое из них должно быть обработано, каков формат их сообщений и т. п.
Вот пример упрощенного обработчика исключений. Он просто печатает диагно- стическое сообщение:


ГЛАВА 8 Защитное программирование
197
Пример централизованного генератора сообщений
об исключениях, часть 1 (Visual Basic)
Sub ReportException( _
ByVal className, _
ByVal thisException As Exception _
)
Dim message As String
Dim caption As String message = ”Exception: ” & thisException.Message & ”.” & ControlChars.CrLf & _
”Class:
” & className & ControlChars.CrLf & _
”Routine: ” & thisException.TargetSite.Name & ControlChars.CrLf caption = ”Exception”
MessageBox.Show( message, caption, MessageBoxButtons.OK, _
MessageBoxIcon.Exclamation )
End Sub
Этот обработчик можно использовать следующим образом:
Пример централизованного генератора сообщений
об исключениях, часть 2 (Visual Basic)
Try
Catch exceptionObject As Exception
ReportException( CLASS_NAME, exceptionObject )
End Try
Код этой версии
ReportException() несложен. В реальных приложениях вы може- те сделать отчет настолько кратким или подробным, насколько это необходимо в вашем обработчике исключений.
Если вы решили создать централизованный генератор сообщений, примите во вни- мание основные проблемы с централизованной обработкой ошибок, обсуждаемые в подразделе «Вызвать процедуру или объект — обработчик ошибок» раздела 8.3.
Стандартизуйте использование исключений в вашем проекте Чтобы со- хранить процедуру обработки исключений максимально интеллектуально управ- ляемой, вы можете стандартизовать использование исключений несколькими спо- собами.

Если вы работаете с языком, таким как C++, который позволяет генерировать исключения разных типов, стандартизуйте, что конкретно будет создаваться.
В целях совместимости с другими языками подумайте об использовании только объектов, порожденных от базового класса
Exception.

Подумайте о создании собственного класса исключений, который может слу- жить базовым классом для всех исключений, возникающих в вашем проекте.
Это поможет централизовать и стандартизовать регистрацию, обработку и другие действия с ошибками.
Дополнительные сведения Об этой технологии см. «Practical
Standards for Microsoft Visual
Basic .NET» (Foxall, 2003).

198
ЧАСТЬ II Высококачественный код

Определите конкретные случаи, в которых код может использовать синтаксис
throw-catch для локальной обработки ошибок.

Определите конкретные случаи, в которых код может сгенерировать исклю- чение, не перехватываемое локально.

Решите, будет ли использоваться централизованный генератор сообщений об исключениях.

Определите, допускаются ли исключения в конструкторах и деструкторах.
Рассмотрите альтернативы исключениям Некоторые языки поддерживают исключения 5–10 лет и более. Одна- ко до сих пор нет общепринятых правил их безопасного использования.
Некоторые программисты применяют исключения для обработки ошибок толь- ко потому, что их язык программирования предоставляет такой механизм. Вам все- гда следует принимать во внимание все возможные методы обработки ошибок:
локальную обработку ошибок, возврат кода ошибки, запись отладочной инфор- мации в файл, прекращение работы системы и др. Обрабатывать ошибки с помо- щью исключений только потому, что это позволяет язык, — классический пример программирования
на языке, а не с использованием языка (см. разделы 4.3 и 34.4).
И напоследок подумайте, действительно ли вашей программе необходимо обра- батывать исключения. Точка. Как заметил Бьерн Страуструп, иногда лучшей реак- цией на серьезную ошибку периода выполнения будет освобождение всех ресур- сов и прекращение работы. Пусть пользователь перезапустит программу с надле- жащими входными данными (Stroustrup, 1997).
8.5. Изоляция повреждений,
вызванных ошибками
Изоляция повреждений, или баррикада, — это стратегия, сходная с тем, как изо- лируются отсеки в трюме корабля. Если корабль налетает на айсберг и в днище появляется пробоина, отсеки задраиваются, и остальная часть корабля не страда- ет. Баррикады также аналогичны брандмауэрам, предотвращающим распростра- нение огня из одной части здания в другую. (Ранее баррикады и назывались бран- дмауэрами, но сейчас термин «брандмауэр» обычно относится к блокировке не- желательного сетевого трафика.)
Один из способов изоляции в целях защитного программирования состоит в разработке набора интерфейсов в качестве оболочки для «безопасных» частей кода.
Проверяйте корректность данных, пересекающих границу безопасной области,
и реагируйте соответственно, если данные неправильные (рис. 8-2).
Перекрестная ссылка Об альтер- нативных подходах к обработ- ке ошибок см. раздел 8.3.


ГЛАВА 8 Защитное программирование
199
Рис. 8-2. Выделение части кода для работы с непроверенными данными, а части —
для работы с только корректными данными, может быть эффективным способом
освободить большую часть программы от ответственности за проверку
допустимости данных
Тот же подход применим и на уровне класса. Открытые методы класса предпола- гают, что данные небезопасны и отвечают за их проверку и исправление. Если данные были проверены открытыми методами класса, закрытые методы могут считать, что данные безопасны.
Этот подход можно представить и такой аналогией: данные стерилизуются, прежде чем войти в операционную. Все, что находится в ней, считается безопасным. Клю- чевой вопрос проектирования — решить, что должно быть в операционной, что
— остаться снаружи и где быть дверям. Иначе говоря, какие методы поместить внутри безопасной зоны, какие — снаружи, а какие будут проверять данные. Про- стейший способ — проверка внешних данных по мере их поступления. Но ин- формацию часто необходимо проверять неоднократно, на нескольких уровнях,
поэтому иногда требуется многоуровневая стерилизация.
Преобразовывайте входные данные к нужному типу в момент ввода
Данные на входе обычно представлены в форме строки или числа. Иногда значе- ние соответствует булевому типу, например, «да» или «нет». Иногда — перечисли- мому, скажем,
Color_Red, Color_Green, и Color_Blue. Сохранение данных неопреде- ленного типа в течение неизвестного периода времени усложняет программу и увеличивает шансы, что кто-нибудь может вывести программу из строя, указав «Да»
в качестве цвета. Преобразуйте входные данные в надлежащую форму как можно раньше.

200
ЧАСТЬ II Высококачественный код
Связь между баррикадами и утверждениями
Применение баррикад делает отчетливым различие между утверждениями и об- работкой ошибок. Методы с внешней стороны баррикады должны использовать обработчики ошибок, поскольку небезопасно делать любые предположения о данных. Методы внутри баррикад должны использовать утверждения, так как дан- ные, переданные им, считаются проверенными при прохождении баррикады. Если один из методов внутри баррикады обнаруживает некорректные данные, это сле- дует считать ошибкой в программе, а не в данных.
Использование баррикад также иллюстрирует значимость принятия решения об обработке ошибок на уровне архитектуры. Решение, какой код находится внут- ри, а какой — снаружи баррикады, принимается на уровне архитектуры.
8.6. Отладочные средства
Еще один ключевой аспект защитного программирования — отладка, способная стать могучим союзником в быстром обнаружении ошибок.
Не применяйте ограничения промышленной версии
к отладочной версии автоматически
Известным заблуждением программистов является предпо- ложение, что ограничения промышленной версии относятся и ко времени разработки. Промышленная версия должна работать быстро. Отладочная — может себе позволить ра- ботать медленно. Промышленная версия должна быть эко- номна с ресурсами. Отладочная — может быть расточитель- ной. Промышленная версия не должна позволять пользователю делать опасные действия. Отладочная — может предоставлять дополнительные возможности без риска нарушить безопасность.
Одна моя программа интенсивно использовала четырехсвязный список. Код это- го списка содержал ошибки, и иногда список повреждался. Я добавил пункт меню для проверки целостности этого связного списка.
В отладочном режиме Microsoft Word содержит код, который в момент простоя каждые несколько секунд проверяет целостность объекта
Document. Это помогает быстро обнаруживать повреждение данных, что упрощает диагностику ошибок.
Будьте готовы поступиться скоростью и ресурсоемкостью во время раз- работки в обмен на встроенные средства, позволяющие процессу разра- ботки двигаться более гладко.
Внедрите поддержку отладки как можно раньше
Чем раньше вы добавите отладочные средства, тем больше они помогут. Обычно вы не добавляете отладочную информацию, пока несколько раз не столкнетесь с про- блемой. Если же вы внедрите поддержку отладки после первого раза или перенесете ее из предыдущего проекта, она будет помогать вам на протяжении всей работы.
Дополнительные сведения Об использовании отладочного ко- да в защитном программирова- нии см. «Writing Solid Code»
(Maguire, 1993).


1   ...   22   23   24   25   26   27   28   29   ...   104

ГЛАВА 8 Защитное программирование
201
Используйте наступательное программирование
Исключительные случаи должны обрабатываться так, что- бы во время разработки они были очевидны, а в промыш- ленном коде — позволяли продолжить работу. Майкл Ховард и Дэвид Леблан назвали этот подход «наступательным про- граммированием» (Howard and LeBlanc, 2003).
Допустим, у вас есть оператор
case, который, как вы ожида- ете, будет обрабатывать только 5 видов событий. Во время разработки вариант по умолчанию нужно использовать для генерации предупреждения «Эй! Здесь еще один вариант! Исправьте программу!». Однако в промышленной версии реакция в этом случае должна быть более вежливой. Можно, например, делать запись в журнал ошибок.
Вот некоторые приемы наступательного программирования.

Убедитесь, что утверждения завершают работу програм- мы. Нельзя, чтобы у программистов вошло в привычку просто нажимать клавишу Enter для пропуска уже изве- стной проблемы. Сделайте проблему достаточно мучи- тельной, чтобы ее исправили.

Заполняйте любую выделенную память, чтобы можно было обнаружить ошибки выделения памяти.

Заполняйте все файлы и потоки, чтобы выявить ошибки файловых форматов.

Убедитесь, что при попадании в ветвь
default или else всех операторов case
программа прекращает работу или еще как-то заставляет обратить на это вни- мание.

Заполняйте объекты мусором перед самым их удалением.

Настройте программу на отправку вам журналов ошибок по электронной по- чте, чтобы видеть, какие ошибки происходят в рабочем ПО, если, конечно, это можно сделать в ваших программах.
Иногда нападение — лучшая защита. Чем жестче требования во время разработ- ки, тем проще эксплуатация программы.
Запланируйте удаление отладочных средств
Если вы пишете код для себя, возможно, было бы хорошо оставить всю отладоч- ную информацию в программе. Но в коде для коммерческого использования тре- бования к скорости и размеру скорее всего этого не допустят. Решите заранее, как избежать постоянного перетаскивания отладочного кода в программу и из нее.
Вот несколько способов сделать это:
Для контроля версий и сборки программы используй-
те инструменты ant и make Средства контроля версий позволяют создавать варианты программы из одних и тех же исходных файлов. А инструменты для сборки позволяют вам настроить вклю- чение отладочного кода в режиме разработки и его исключение в коммерческой версии.