Главная страница
Навигация по странице:

  • Инкапсулируйте детали реализации

  • ЧАСТЬ II Высококачественный кодИспользуйте наследование, если оно упрощает проектирование

  • ГЛАВА 5

  • Секреты и право на личную жизнь

  • ЧАСТЬ II Высококачественный код Рис. 59.

  • Пример сокрытия информации

  • Барьеры, препятствующие сокрытию информации

  • Избыточное распространение информации

  • Ошибочное представление о данных класса как о глобальных данных

  • Кажущееся снижение производительности

  • Перекрестная ссылка

  • Важность сокрытия информации

  • Совершенный код. Совершенный код. Мастер-класс. Стив Макконнелл. Руководство по стилю программирования и конструированию по


    Скачать 5.88 Mb.
    НазваниеРуководство по стилю программирования и конструированию по
    АнкорСовершенный код
    Дата31.03.2023
    Размер5.88 Mb.
    Формат файлаpdf
    Имя файлаСовершенный код. Мастер-класс. Стив Макконнелл.pdf
    ТипРуководство
    #1028502
    страница14 из 106
    1   ...   10   11   12   13   14   15   16   17   ...   106
    Рис. 5'7. Абстракция позволяет представить сложную концепцию
    в более простой форме
    Разработчики ПО иногда создают системы на уровне волокон древесины и молекул лака и стали, из#за чего такие системы становятся слишком сложными и плохо поддаются осмысле- нию. Если программисты не создают более общие абстракции, разработка системы может завершиться неудачей.
    Благоразумные программисты создают абстракции на уровне интерфейсов ме- тодов, интерфейсов классов и интерфейсов пакетов (иначе говоря, на уровне дверной ручки, уровне двери и на уровне дома), что способствует более быстрому и безопасному программированию.
    Инкапсулируйте детали реализации
    Когда абстракция нас покидает, на помощь приходит инкапсуляция. Абстракция говорит: «Вы можете рассмотреть объект с общей точки зрения». Инкапсуляция добавляет: «Более того, вы не можете рассмотреть объект с иной точки зрения».
    Продолжим нашу аналогию: инкапсуляция позволяет вам смотреть на дом, но не дает подойти достаточно близко, чтобы узнать, из чего сделана дверь. Инкапсуля- ция позволяет вам знать о существовании двери, о том, открыта она или заперта, но при этом вы не можете узнать, из чего она сделана (из дерева, стекловолокна, стали или другого материала), и уж никак не сможете рассмотреть отдельные волокна древесины.
    Инкапсуляция помогает управлять сложностью, блокируя доступ к ней (рис. 5#8).
    В подразделе «Хорошая инкапсуляция» раздела 6.2 инкапсуляция рассматривается подробнее в контексте проектирования классов.
    Рис. 5'8. Инкапсуляция не только представляет сложную концепцию
    в более простой форме, но и не позволяет взглянуть на какие бы то ни было
    детали сложной концепции. Что видите, то и получите — и не более того!
    Перекрестная ссылка Об абст- ракции в контексте проектирова- ния классов см. подраздел «Хоро- шая абстракция» раздела 6.2.
    CC2_Part2_ch5_2010.indd 87 22.06.2010 12:31:35

    88
    ЧАСТЬ
    II
    Высококачественный код
    Используйте наследование, если оно упрощает
    проектирование
    При проектировании ПО часто выясняется, что одни объекты аналогичны другим за исключением нескольких различий. Так, при создании системы расчета зарплаты нужно учесть, что одни сотрудники работают полный день, а другие — неполный.
    В этом случае наборы данных, ассоциированные с сотрудниками обеих категорий, будут различаться лишь несколькими аспектами. Объектно#ориентированный подход позволяет создать общий тип «сотрудник» и определить сотрудников, ра- ботающих полный день, как сотрудников общего типа за исключением нескольких различий. Если операция над объектом «сотрудник» не зависит от его категории, она выполняется так, как если бы объект был сотрудником общего типа. Если же операция зависит от типа сотрудника, она выполняется разными способами.
    Определение сходств и различий между такими объектами называется «наследова- нием», потому что отдельные типы сотрудников, работающих полный и неполный день, наследуют свойства общего типа «сотрудник».
    Польза наследования в том, что оно дополняет идею абстракции. Абстракция позволяет представить объекты с разным уровнем детальности. Если помните, на одном уровне мы рассматривали дверь как набор определенных типов моле- кул, на втором — как набор волокон древесины, а на третьем — как что#то, что защищает нас от воров. Древесина имеет определенные свойства — скажем, вы можете распилить ее пилой или склеить столярным клеем, — при этом и плинтусы, и подоконники имеют общие свойства древесины, но вместе с тем и некоторые специфические свойства.
    Наследование упрощает программирование, позволяя создать универсальные методы для выполнения всего, что основано на общих свойствах дверей, и затем написать специфические методы для выполнения специфических операций над конкретными типами дверей. Некоторые операции, такие как
    Open() или Close(), будут универсальными для всех дверей: внутренних, входных, стеклянных, сталь- ных — каких угодно. Поддержка языком операций вроде
    Open() или Close() при отсутствии информации о конкретном типе двери вплоть до периода выполнения называется
    полиморфизмом. Объектно#ориентированные языки, такие как C++,
    Java и более поздние версии Microsoft Visual Basic, поддерживают и наследование, и полиморфизм.
    Наследование — одно из самых мощных средств объектно#ориентированного программирования. При правильном применении оно может принести большую пользу, однако в обратном случае и ущерб будет немалым. Подробнее см. подраз- дел «Наследование (отношение «является»)» раздела 6.3.
    CC2_Part2_ch5_2010.indd 88 22.06.2010 12:31:36

    ГЛАВА
    5 Проектирование при конструировании
    89
    Скрывайте секреты (к вопросу о сокрытии информации)
    Сокрытие информации — один из основных принципов и структурного, и объек- тно#ориентированного проектирования. В первом случае сокрытие информации лежит в основе идеи «черных ящиков». Во втором оно дает начало концепциям инкапсуляции и модульности и связано с концепцией абстракции. Сокрытие ин- формации — одна из самых конструктивных идей в мире разработки ПО, и сейчас мы рассмотрим ее подробнее.
    Впервые сокрытие информации было представлено на суд общественности в 1972 г.
    Дэвидом Парнасом (David Parnas) в статье «On the Criteria to Be Used in Decomposing
    Systems Into Modules (О критериях, используемых при декомпозиции систем на модули)». С сокрытием информации тесно связана идея «секретов» — аспектов проектирования и реализации, которые разработчик ПО решает скрыть в каком#то месте от остальной части программы.
    В юбилейном 20#летнем издании книги «Мифический человеко#месяц» Фред Брукс пришел к выводу, что критика сокрытия информации была одной из ошибок, до- пущенных им в первом издании книги. «Парнас был прав в отношении сокрытия информации, а я ошибался», — признал он (Brooks, 1995). Барри Бом сообщил, что сокрытие информации — мощный метод избавления от повторной работы, и указал, что оно особенно эффективно в инкрементных средах с высоким уровнем изменений (Boehm, 1987).
    В контексте Главного Технического Императива Разработки ПО сокрытие инфор- мации оказывается особенно мощным эвристическим принципом, так как все его аспекты и даже само название подчеркивают
    сокрытие сложности.
    Секреты и право на личную жизнь
    При сокрытии информации каждый класс (пакет, метод) характеризуется аспектами проектирования или конструирования, которые он скрывает от остальных классов.
    Секретом может быть источник вероятных изменений, формат файла, реализация типа данных или область, изоляция которой требуется для сведения к минимуму вреда от возможных ошибок. Класс должен скрывать эту информацию и защищать свое право на «личную жизнь». Небольшие изменения системы могут влиять на не- сколько методов класса, но не должны распространяться за его интерфейс.
    Один из важнейших аспектов проектирования класса — при- нятие решения о том, какие свойства сделать доступными вне класса, а какие оставить секретными. Класс может включать
    25 методов, предоставляя доступ только к пяти из них и ис- пользуя остальные 20 внутренне. Класс может использовать несколько типов данных, не раскрывая сведений о них. Этот аспект проектирования классов называют «видимостью», так как он определяет, какие свойства класса «видимы» или «доступны» извне.
    Интерфейс класса должен сообщать как можно меньше о внутренней работе класса.
    В этом смысле класс во многом похож на айсберг, большая часть которого скрыта под водой (рис. 5#9).
    Интерфейсы классов должны быть полными и минималь- ными.
    Скотт Мейерс
    (Scott Meyers)
    CC2_Part2_ch5_2010.indd 89 22.06.2010 12:31:36

    90
    ЧАСТЬ
    II
    Высококачественный код
    Рис. 5'9. Хороший интерфейс класса похож на верхушку айсберга:
    большую часть класса он оставляет скрытой
    Как и любой другой аспект проектирования, разработка интерфейса класса — итеративный процесс. Если приемлемый интерфейс класса не удается создать с первого раза, сделайте еще несколько попыток, пока он не стабилизируется. Если интерфейс не стабилизируется, попробуйте другой подход.
    Пример сокрытия информации
    Допустим, вы пишете программу, каждый объект которой должен иметь уникаль- ный идентификатор, хранящийся в переменной#члене
    id. Один подход к проек- тированию может заключаться в применении целочисленных идентификаторов и хранении максимального на данный момент идентификатора в глобальной переменной
    g_maxId. При создании новых объектов вы можете — скажем, в кон- структоре каждого объекта — просто выполнять команду
    id = ++g_maxId, что гарантирует уникальность идентификаторов, и требует абсолютно минимального кода при создании каждого объекта. Разве это может привести к каким#нибудь неприятностям?
    Может. Что, если вы захотите зарезервировать диапазоны идентификаторов для определенных целей? Что, если для повышения защищенности программы вы за- хотите назначать идентификаторы в другом порядке? А если вы захотите повтор- но задействовать идентификаторы уничтоженных объектов? Или включить в про- грамму диагностический тест, проверяющий, не превысило ли число идентифи- каторов допустимый предел? Если, назначая идентификаторы, вы распространите команды
    id = ++g_maxId по всей программе, вам придется изменить каждую из них. Кроме того, этот подход небезопасен в многопоточной среде.
    Способ генерации новых идентификаторов является тем аспектом проектирования, который следует скрыть. Применив команду
    ++g_maxId, вы раскроете сведения о том, что новый идентификатор создается просто путем увеличения переменной
    g_maxId. Если же вместо этого вы используете команды id = NewId(), вы скроете
    CC2_Part2_ch5_2010.indd 90 22.06.2010 12:31:36

    ГЛАВА
    5 Проектирование при конструировании
    91
    информацию о способе создания новых идентификаторов. Сам метод
    NewId() может состоять из единственной строки
    return ( ++g_maxId ) или ее эквивален- та, однако, если вы позднее решите зарезервировать определенные диапазоны идентификаторов для специфических целей или повторно использовать старые идентификаторы, вам придется изменить только метод
    NewId(), но не десятки команд
    id = NewId(). Какими бы сложными ни были изменения метода NewId(), они не повлияют ни на какую другую часть программы.
    Допустим теперь, что вам понадобилось изменить тип идентификатора с цело- численного на строковый. Если по всей программе у вас разбросаны объявления вроде
    int id, метод NewId() не поможет. В этом случае вам тоже придется просмо- треть всю программу и внести десятки или сотни изменений.
    Итак, тип идентификатора — это тоже секрет, который следует скрыть. Показывая, что идентификаторы — целые числа, вы поощряете программистов выполнять над ними такие операции, как
    >, < и =. Программируя на C++, вы могли бы не объявлять идентификаторы как
    int, а назначить им при помощи директивы typedef пользо- вательский тип
    IdType соответствующий тому же int. Или же вы могли бы создать простой класс
    IdType. Повторю еще раз: сокрытие аспектов проектирования по- зволяет значительно уменьшить объем кода, затрагиваемого изменениями.
    Сокрытие информации полезно на всех уровнях проектирования: от при#
    менения именованных констант вместо литералов до создания типов данных и проектирования классов, методов и подсистем.
    Две категории секретов
    Связанные с сокрытием информации секреты относятся к двум общим категориям:
    
    секреты, которые скрывают сложность, позволяя программистам забыть о ней при работе над остальными частями программы;
    
    секреты, которые скрывают источники изменений с целью локализации ре- зультатов возможных изменений.
    В число источников сложности входят сложные типы данных, файловые струк- туры, булевы тесты, запутанные алгоритмы и т. д. Источники изменений будут описаны немного позднее.
    Барьеры, препятствующие сокрытию информации
    В некоторых случаях скрыть информацию невозможно, однако большинство барьеров, препятствующих сокрытию информации, является умственными и обусловлены привы- канием к другим методикам.
    Избыточное распространение информации Зачастую сокрытию информации препятствует избыточное распро- странение информации по системе. Так, жесткое кодирование литерала
    100 во многих местах программы децентрализует ссылки на него. Лучше скрыть эту информацию в одном месте — скажем, при помощи константы
    MAX_EMPLOYEES, для изменения значения которой придется изменить только одну строку кода.
    Дополнительные сведения От- дельные фрагменты этого раз- дела взяты из статьи «Designing
    Software for Ease of Extension and
    Contraction» (Parnas, 1979).
    CC2_Part2_ch5_2010.indd 91 22.06.2010 12:31:37

    92
    ЧАСТЬ
    II
    Высококачественный код
    Еще один пример избыточного распространения информации — распределение по системе кода взаимодействия с пользователями. При этом в случае изменения способа взаимодействия — например, при замене графического интерфейса на интерфейс командной строки — придется изменить почти весь код. Лучше сконцен- трировать взаимодействие с пользователями в одном классе (пакете, подсистеме), который можно было бы изменить, не влияя на всю систему.
    В качестве другого примера приведу повсеместное исполь- зование глобального элемента данных, такого как массив данных о сотрудниках, поддерживающий до 1000 элементов.
    Если программа будет обращаться к глобальным данным напрямую, информация о реализации элемента данных — скажем, то, что это массив, способный включать до 1000 элементов, — распространится по всей программе. Если программа будет обращаться к данным только через методы доступа, детали реа- лизации будут известны только этим методам.
    Круговая зависимость Более тонким барьером, мешающим сокрытию инфор- мации, является круговая зависимость, когда, например, метод класса A вызывает метод класса B, а метод класса B — метод класса A.
    Избегайте таких зависимостей: они осложняют тестирование системы, не по- зволяя протестировать ни один из классов, пока не будет реализована хотя бы часть второго класса.
    Ошибочное представление о данных класса как о глобальных данных
    Если вы добросовестный программист, возможна еще одна преграда на пути к эффективному сокрытию информации: вы можете рассматривать данные класса как глобальные данные, избегая их из#за соответствующих проблем. Всем известно, что дорога в ад программирования вымощена глобальными переменными, однако использовать данные класса гораздо безопаснее.
    Глобальные данные имеют два главных недостатка: методы, обращающиеся к гло- бальным данным, не знают о том, что другие методы тоже обращаются к этим данным, или же методы знают об этом, но не знают, что именно другие методы делают с глобальными данными. Данные класса этих недостатков не имеют. Не- посредственный доступ к данным класса ограничен несколькими методами этого же класса, которые знают и о том, что другие методы также работают с данными, и о том, что это за методы.
    Конечно, это предполагает, что система включает грамотно спроектированные не- большие классы. Если программа использует огромные классы, включающие десятки методов, различие между данными класса и глобальными данными стирается, и данные класса приобретают многие недостатки, характерные для глобальных данных.
    Кажущееся снижение производительности Наконец,
    отказ от сокрытия информации может объясняться стремле- нием избежать снижения производительности и на уровне архитектуры, и на уровне кода. В обоих случаях волноваться не о чем. При проектировании архитектуры сокрытие информации не конфликтует с производительностью. Помня и о сокрытии информации, и о производитель- ности, вы сможете достичь обеих целей.
    Перекрестная ссылка О доступе к глобальным данным при по- мощи интерфейсов классов см. подраздел «Используйте мето- ды доступа вместо глобальных данных» раздела 13.3.
    Перекрестная ссылка О повы- шении производительности на уровне кода см. главы 25 и 26.
    CC2_Part2_ch5_2010.indd 92 22.06.2010 12:31:37

    ГЛАВА
    5 Проектирование при конструировании
    93
    Производительность на уровне кода вызывает еще больше беспокойств. Разработ- чикам кажется, что опосредованный доступ к данным снизит производительность программы в период выполнения из#за дополнительных затрат на создание объ- ектов, вызовы методов и т. д. Эти волнения преждевременны. Пока вы не оцените производительность системы и не найдете узкие места, лучшим способом под- готовки к повышению производительности на уровне кода является модульное проектирование. Позже, определив в коде «горячие точки», вы оптимизируете отдельные классы и методы, не затрагивая остальную часть системы.
    Важность сокрытия информации
    Сокрытие информации относится к тем немногим теоретическим под#
    ходам, польза которых уже долгое время неоспоримо подтверждается на практике (Boehm, 1987a). Было обнаружено, что крупные программы, использующие сокрытие информации, вчетверо легче модифицировать, чем про- граммы, его не использующие (Korson and Vaishnavi, 1986). Более того, сокрытие информации — один из основных принципов и структурного, и объектно#ори- ентированного проектирования.
    Сокрытие информации обладает уникальной эвристической силой, уникальной способностью подталкивать разработчиков к эффективным проектным решениям.
    Традиционное объектно#ориентированное проектирование предоставляет мощные эвристические средства моделирования мира в терминах объектов, но объектный подход не помог бы вам догадаться, что идентификатор следует объявить как
    IdType, а не как
    int. Разработчик, использующий объектно#ориентированный подход, спросил бы: «Рассматривать ли идентификатор как объект?» В зависимости от принятых в проекте стандартов кодирования утвердительный ответ мог бы означать, что программист должен написать конструктор, деструктор, операторы копирования и присваивания, закомментировать все это и сохранить в системе управления конфигурацией. Но скорее всего программист решил бы: «Нет, не стоит создавать целый класс ради какого#то идентификатора. Использую просто
    int».
    Смотрите: эффективный вариант проектирования — простое сокрытие типа дан- ных идентификатора — даже не был рассмотрен! Если бы вместо этого разработ- чик спросил: «Не скрыть ли информацию об идентификаторе?» — он, возможно, решил бы объявить собственный тип
    IdType как синоним int. Различие между объек- тно#ориентированным проектированием и сокрытием информации в этом при- мере не сводится к простому несоответствию явных правил и предписаний. При- нятое в соответствии с принципом сокрытия информации решение прекрасно согласуется с объектно#ориентированным подходом. Вместо этого различие от- носится к области эвристики: размышление над сокрытием информации может указать на такие варианты проектирования, которые при использовании объект- но#ориентированного подхода остались бы незамеченными.
    Сокрытие информации может пригодиться при проектировании открытого ин- терфейса класса. Теория и практика проектирования классов во многом расхо- дятся, и многие разработчики, решая, чт у
    включить в открытый интерфейс клас- са, думают прежде всего об удобном интерфейсе, а это обычно приводит к рас- крытию почти всей информации об устройстве класса. Опыт подсказывает мне,
    CC2_Part2_ch5_2010.indd 93 22.06.2010 12:31:37

    94
    1   ...   10   11   12   13   14   15   16   17   ...   106


    написать администратору сайта