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

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

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

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

Добавлен: 30.11.2023

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

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


Почему правил наследования так много?
В этом разделе были описаны многие правила избавления от проблем,
связанных с наследованием. Все эти правила подразумевают, что
насле-
дование часто противоречит главному техническому императиву програм-
С множественным наследовани- ем в C++ связан один неоспори- мый факт: оно открывает ящик
Пандоры, полный проблем, ко- торые просто невозможны при единичном наследовании.
Скотт Мейерс
(Scott Meyers)

146
ЧАСТЬ II Высококачественный код
мирования — управлению сложностью. Ради управления сложностью относитесь к наследованию с подозрением. Вот как использовать наследование и включение:

если несколько классов имеют общие данные, но не фор- мы поведения, создайте общий объект, который можно было бы включить во все эти классы;

если несколько классов имеют общие формы поведения,
но не данные, сделайте эти классы производными от общего базового класса, определяющего общие методы;

если несколько классов имеют общие данные и формы поведения, сделайте эти классы производными от общего базового класса, определяющего общие дан- ные и методы;

используйте наследование, если хотите, чтобы интерфейс определялся базо- вым классом, и включение, если хотите сами контролировать интерфейс.
Методы-члены и данные-члены
Ниже я даю несколько советов по эффективной реализации методов-членов и данных-членов.
Включайте в класс как можно меньше методов
В одном исследовании программ на C++ было обнаружено, что большему числу методов в расчете на один класс соответствует большее число изъянов (Basili, Briand,
and Melo, 1996). Однако важнее оказались другие конкурирующие факторы, в том числе многоуровневые иерархии наследования, большое число методов, вызыва- емых из класса, и сильное сопряжение между классами. Разрабатывая класс, стре- митесь к оптимальному соответствию между этими факторами и минимальным числом методов.
Блокируйте неявно сгенерированные методы и операторы, которые вам
не нужны Иногда некоторые возможности, такие как создание объекта или при- сваивание, целесообразно блокировать. Вам может показаться, что сделать это невозможно, потому что компилятор генерирует эти операции автоматически.
Однако вы можете запретить их использование в клиентском коде, объявив кон- структор, оператор присваивания или другой метод или оператор как
private.
(Создание закрытого конструктора — стандартный способ определения класса- одиночки, о чем см. ниже.)
Минимизируйте число разных методов, вызываемых классом Одно иссле- дование показало, что число дефектов в коде класса статистически коррелирует с общим числом методов, вызываемых классом (Basili, Briand, and Melo, 1996). То же исследование показало, что число дефектов в коде класса повышается и при увеличении числа используемых в нем классов. Эти концепции иногда называют
«коэффициентом разветвления по выходу (fan out)».
Избегайте опосредованных вызовов методов других классов Непосред- ственные связи довольно опасны. Опосредованные связи, такие как
account.Con-
tactPerson().DaytimeContactInfo().PhoneNumber(), опасны еще больше. В связи с этим ученые сформулировали «Правило Деметры (Law of Demeter)» (Lieberherr and
Holland, 1989), которое гласит, что Объект A может вызывать любые из собствен- ных методов. Если он создает Объект B, он может вызывать любые методы Объекта
Перекрестная ссылка О слож- ности см. подраздел «Главный
Технический Императив Разра- ботки ПО: управление сложно- стью» раздела 5.2.
Перекрестная ссылка О методах в общем см. главу 7.


ГЛАВА 6 Классы
147
B, но ему не следует вызывать методы объектов, возвраща- емых Объектом B. В нашем случае это означает, что вызов
account . ContactPerson() приемлем, однако вызова account.-
ContactPerson() .DaytimeContactInfo() следовало бы избежать.
Это упрощенное объяснение — подробнее см. в книгах, ука- занных в конце главы.
Вообще минимизируйте сотрудничество класса с дру-
гими классами Старайтесь свести к минимуму все следу- ющие показатели:

число видов создаваемых объектов;

число непосредственно вызываемых методов созданных объектов;

число вызовов методов, принадлежащих объектам, возвращенным другими созданными объектами.
Конструкторы
Советы по использованию конструкторов почти не зависят от языка (по крайней мере это касается C++, Java и Visual Basic). С деструкторами связано больше раз- личий — см. материалы, указанные в разделе «Дополнительные ресурсы».
Инициализируйте по мере возможности все данные-члены во всех кон-
структорах Инициализация всех данных-членов во всех конструкторах — про- стой прием защитного программирования.
Создавайте классы-одиночки с помощью закрытого
конструктора Если вы хотите определить класс, позво- ляющий создать только один объект, скройте все конструк- торы класса и создайте статический метод
GetInstance(), пре- доставляющий доступ к единственному экземпляру класса:
Пример создания класса-одиночки с помощью
закрытого конструктора (Java)
public class MaxId {
// конструкторы и деструкторы
Закрытый конструктор.
private MaxId() {
}
// открытые методы
Открытый метод, предоставляющий доступ к единственному экземпляру класса.
public static MaxId GetInstance() {
return m_instance;
}
// закрытые члены
Дополнительные сведения Хо- рошее обсуждение «Правила
Деметры» см. в книгах «Pragma- tic Programmer» (Hunt and Tho- mas, 2000), «Applying UML and
Patterns» (Larman, 2001) и «Fun- damentals of Object-Oriented De- sign in UML» (Page-Jones, 2000).
Дополнительные сведения Ана- логичный код, написанный на
C++, был бы очень похож. Под- робнее см. раздел 26 книги «More
Effective C++» (Meyers, 1998).
>
>

148
ЧАСТЬ II Высококачественный код
Единственный экземпляр класса.
private static final MaxId m_instance = new MaxId();
}
Закрытый конструктор вызывается только при инициализации статического объек- та
m_instance. Для обращения к классу-одиночке MaxId нужно просто вызвать метод
MaxId. GetInstance().
Если сомневаетесь, выполняйте полное копирование, а не ограниченное
Одним из главных аспектов работы со сложными объектами является выбор типа их копирования: полного или ограниченного. Полная копия (deep copy) — это почленная копия данных-членов объекта; ограниченная копия (shallow copy)
обычно просто указывает или ссылается на исходный объект, хотя конкретные значения «полного» и «ограниченного» копирования могут различаться.
Мотивом создания ограниченных копий обычно бывает повышение быстродей- ствия программы. Однако создание нескольких копий крупных объектов редко приводит к заметному снижению быстродействия, хотя и выглядит эстетически непривлекательно. Полное копирование некоторых объектов действительно мо- жет снижать быстродействие, но программисты обычно очень плохо определя- ют, какой код вызывает проблемы (см. главу 25). Повышение сложности едва ли можно оправдать сомнительным улучшением быстродействия кода, поэтому, если не доказано обратное, лучше выполнять полное копирование.
Полные копии легче в реализации и сопровождении, чем ограниченные. При ограниченном копировании нужно написать не только специфический для объекта код, но и код подсчета ссылок, безопасного сравнения объектов, их безопасного уничтожения и т. д. Такой код может быть источником ошибок, поэтому без вес- кой причины создавать его не следует.
Если вы находите, что вам все-таки нужно ограниченное копирование, прекрас- ное обсуждение этого подхода в контексте C++ см. в разделе 29 книги Скотта
Мейерса «More Effective C++» (Meyers, 1996). В книге Мартина Фаулера «Refactoring»
(Fowler, 1999) описываются специфические действия, нужные для преобразова- ния ограниченных копий в полные и наоборот [Фаулер называет их объектами- ссылками (reference objects) и объектами-значениями (value objects)].
6.4. Разумные причины создания классов
Если вы верите всему, что читаете, у вас могло сложиться впечатление, что единственная причина создания класса —
моделирование объектов реального мира. На самом деле это весьма далеко от истины. Список разумных причин созда- ния класса приведен ниже.
Моделирование объектов реального мира Пусть моде- лирование объектов реального мира — не единственная причина создания класса, но от этого она не становится менее хорошей! Создайте класс для каждого объекта реаль-
Перекрестная ссылка Причины создания классов и методов во многом перекрываются (см.
раздел 7.1).
Перекрестная ссылка Об иден- тификации объектов реального мира см. подраздел «Определи- те объекты реального мира»
раздела 5.3.
>


ГЛАВА 6 Классы
149
ного мира, моделируемого вашей программой. Поместите нужные объекту дан- ные в класс и создайте сервисные методы, моделирующие поведение объекта.
Примеры подобного моделирования см. в разделе 6.1.
Моделирование абстрактных объектов Другой разумной причиной созда- ния класса является моделирование
абстрактного объекта — объекта, который не существует в реальном мире, но является абстракцией других конкретных объек- тов. Прекрасный пример — классический объект
Shape (фигура). Объекты Circle
(окружность) и
Square (прямоугольник) существуют на самом деле, тогда как класс
Shape — это абстракция конкретных фигур.
В мире программирования редко встречаются готовые абстракции вроде
Shape, из- за чего поиск ясных абстракций усложняется. Процесс извлечения абстракций из разнообразия сущностей реального мира недетерминирован, и формирование абстракций может быть основано на разных принципах. Если бы нам не были из- вестны окружности, прямоугольники, треугольники и другие геометрические фи- гуры, мы могли бы прийти в итоге к более необычным фигурам, таким как «каба- чок», «брюква» и «Понтиак Ацтек». Нахождение адекватных абстрактных объектов
— одна из главных проблем объектно-ориентированного проектирования.
Снижение сложности Снижение сложности — самая важная причи- на создания класса. Создайте класс для сокрытия информации, чтобы о ней можно было не думать. Конечно, вам придется думать о ней при написании класса, но после этого вы сможете забыть о деталях и использовать класс, не зная о его внутренней работе. Другие причины создания классов —
минимизация объема кода, облегчение сопровождения программы и снижение числа ошибок — тоже хороши, но без абстрагирующей силы классов сложные программы было бы невозможно охватить умом.
Изоляция сложности Как бы ни проявлялась сложность — в форме запутан- ных алгоритмов, крупных наборов данных, замысловатых протоколов коммуни- кации, — она является источником ошибок. При возникновении ошибки ее будет проще найти, если она будет локализована в классе, а не распределена по всему коду. Изменения, обусловленные исправлением ошибки, не повлияют на осталь- ной код, потому что вам придется исправить только один класс. Если вы найдете более эффективный, простой или надежный алгоритм, им будет легче заменить старый алгоритм, изолированный в классе. Во время разработки вам будет про- ще попробовать несколько вариантов проектирования и выбрать наилучший.
Сокрытие деталей реализации Еще одна прекрасная причина создания класса
— сокрытие деталей реализации, и таких сложных, как мудреный способ доступа к БД, и столь банальных, как отдельный элемент данных, хранимый в форме чис- ла или строки.
Ограничение влияния изменений Изолируйте области вероятных изменений,
чтобы влияние изменений ограничивалось пределами одного или нескольких классов. Проектируйте приложение так, чтобы области вероятных изменений можно было изменить с максимальной легкостью. В число областей вероятных изменений входят зависимости от оборудования, подсистема ввода/вывода, слож- ные типы данных и бизнес-правила. Некоторые частые источники изменений


150
ЧАСТЬ II Высококачественный код описаны в подразделе «Скрывайте секреты (к вопросу о сокрытии информации)»
раздела 5.3.
Сокрытие глобальных данных Используя глобальные данные, вы можете скрыть детали их реализации за интер- фейсом класса. Обращение к глобальным данным через ме- тоды доступа имеет ряд преимуществ в сравнении с их не- посредственным использованием. Вы можете менять структуру данных, не изме- няя программу. Вы можете следить за доступом к данным. Кроме того, использо- вание методов доступа подталкивает к размышлениям о том, на самом ли деле данные глобальны; часто оказывается, что «глобальные данные» на самом деле являются просто данными какого-то объекта.
Упрощение передачи параметров в методы Если вы передаете один пара- метр в несколько методов, это может указывать на необходимость объединения этих методов в класс, чтобы они могли использовать параметр как данные объекта.
Упрощение передачи параметров в методы само по себе не цель, но передача крупных объемов данных наводит на мысль, что другая организация классов могла бы быть более эффективной.
Создание центральных точек управления Управлять каждой задачей в одном месте — разумная идея. Управле- ние может принимать разные формы. Знание числа элемен- тов таблицы — одна форма. Управление файлами, соедине- ниями с БД, принтерами и другими устройствами — другая.
Использование одного класса для чтения и записи БД явля- ется формой централизованного управления. Если БД нужно будет преобразовать в однородный файл или данные «в памяти», изменения придется внести только в один класс.
Идея централизованного управления похожа на сокрытие информации, но она имеет уникальную эвристическую силу, так что не забудьте добавить ее в свой инструментарий.
Облегчение повторного использования кода Код, разбитый на грамотно орга- низованные классы, легче повторно использовать в других программах, чем тот же код, помещенный в один более крупный класс. Даже если фрагмент вызывает- ся только из одного места программы и вполне понятен в составе более крупно- го класса, подумайте, может ли он понадобиться в другой программе. Если да, стоит поместить его в отдельный класс.
В Лаборатории проектирования ПО NASA были изучены десять проек- тов, в которых энергично преследовалось повторное использование кода
(McGarry, Waligora, and McDermott, 1989). И при объектно-, и при функ- ционально-ориентированном подходах разработчикам первоначально не удалось достичь этой цели, потому что в предыдущих проектах не была создана достаточная база кода. Впоследствии при работе над функциональными проектами около 35%
кода удалось взять из предыдущих проектов. В проектах, основанных на объект- но-ориентированном подходе, этот показатель составил 70%. Если благодаря заб- лаговременному планированию можно избежать написания 70% кода, грех этим не воспользоваться!
Перекрестная ссылка О сокры- тии информации см. подраздел
«Скрывайте секреты (к вопро- су о сокрытии информации)»
раздела 5.3.
Перекрестная ссылка О пробле- мах, связанных с глобальными данными, см. раздел 13.3.


ГЛАВА 6 Классы
151
Заметьте, что ядро подхода NASA к созданию повторно ис- пользуемых классов не включает «проектирование для по- вторного использования». Классы, претендующие на повтор- ное использование, определяют в NASA в конце проектов.
Все действия по упрощению повторного использования классов выполняются как специальный проект в конце ос- новного проекта или как первый этап нового проекта. Этот подход помогает предотвращать «позолоту» — создание не- нужной функциональности, только повышающей сложность.
Планирование создания семейства программ Если вы ожидаете, что програм- му придется изменять, разумно изолировать области предполагаемых изменений в отдельных классах. После этого вы можете изменять классы, не влияя на остальную часть программы, или вообще заменить их на абсолютно новые классы. Размыш- ление о том, как может выглядеть целое семейство программ, а не просто одна программа, — эффективный эвристический принцип предвосхищения целых категорий изменений (Parnas, 1976).
Как-то я руководил группой, работавшей над рядом программ, упрощавших заклю- чение договоров страхования. Мы должны были адаптировать каждую программу к отдельным тарифам, формату отчетов конкретного клиента и т. д. Однако мно- гие части программ были похожи: например, классы ввода данных о потенциаль- ных заказчиках, классы, сохранявшие информацию в БД, классы просмотра тари- фов и т. д. Мы организовали программу так, чтобы каждая «изменчивая» часть на- ходилась в отдельном классе. Создание первоначальной программы заняло три месяца или около того, но зато когда к нам обращался новый клиент, мы просто переписывали несколько классов и включали их в остальной код. Несколько дней работы и — вуаля! — специализированное приложение!
Упаковка родственных операций Если создание класса не удается обосновать сокрытием информации, совместным доступом к данным или обеспечением гиб- кости программы, вы все же можете упаковать наборы операций в более осмыс- ленные группы, такие как группы тригонометрических функций, статистических функций, методов работы со строками, методов манипулирования битами, гра- фических методов и т. д. Класс — не единственное средство объединения родствен- ных операций. В зависимости от конкретного языка для этого также можно ис- пользовать пакеты, пространства имен или заголовочные файлы.
Выполнение специфического вида рефакторинга Создание новых классов предусматривают многие специфические виды рефакторинга (см. главу 24), та- кие как разделение одного класса на два, сокрытие делегата, удаление класса-по- средника и формирование класса-расширения. Создание этих новых классов может быть мотивировано стремлением к лучшему выполнению какой-либо задачи из описанных в данном разделе.
Классы, которых следует избегать
Хотя в целом классы очень полезны, работая с ними, вы можете столкнуться с проблемами. Ниже описаны классы, создавать которые не следует.
Перекрестная ссылка О реали- зации минимального объема необходимой функциональности см. подраздел «Программа со- держит код, который может когда-нибудь понадобиться»
раздела 24.2.