Б. Страуструп - Язык программирования С++. Специальное издание, 3-изд. Бином. 2004 (1160791), страница 183
Текст из файла (страница 183)
В каждом случае следует задуматься, правильно ли выбран язык программирования, правильно ли выбран метод проектирования, и освоил ли проектировщик имеющие- ся средства проектирования. В таком несоответствии нет ничего необычного или постыдного. Просто это несоот- ветствие приводит к неоптимальному проекту и приносит программистам трудности, которых можно было бы избежать.
То же самое можно сказать и о проектировщиках, когда концептуально метод проектирования оказывается значительно беднее, чем иде- ология С++. Поэтому по мере возможности следует избегать таких несоответствий. Следующие рассуждения построены в виде ответов на возражения, поскольку они часто возникают в реальной жизни. 24.2.1. Отказ от классов Рассмотрим проектирование без применения классов. Конечная программа на С++ будет в основном совпадать с программой на С, которая получилась бы в том же процессе проектирования — а эта программа будет опять же примерно соответствовать программе на СОВОГО, которая получилась бы при следовании этому процессу проектирования.
По сути проект получается «независимым от языка программирования» 800 Глава 24. Проектирование и программирование ценой того, что программист вынужден пользоваться общим подмножеством С и СОВОЕ. Этот подход действительно имеет свои преимущества. Например, строгое разделение данных и кода, что позволяет легче пользоваться традиционными базами данных, которые спроектированы для таких программ. Поскольку используется минимальный язык программирования, от программистов требуется меныцая квалификация — или, по крайней мере, меньше разных навыков. Для многих приложений— скажем, для традиционных последовательных программ обновления баз данных — такой образ мыслей вполне оправдан, и для ннх вполне годятся традиционные приемы, развивавшиеся десятилетиями.
Однако предположим, что прикладная программа существенно отлнчается от традиционной последовательной обработки записей (или символов), или сложность программы выше, чем обычно, — скажем, в интерактивной САЯЕ-системе. Отсутствие языковой поддержки абстракции дыш~|х, вызванное решением не применять классы, принесет вред. Внутренняя сложность проявится где-нибудь в прилозкепии, а если программа реализована на достаточно бедном языке, то код не будет напряму1о отражать проект.
Код будет слишком объемным, будет отсутствовать проверка типов, н вообше программа не будет способна к взаимодействию со средствами программирования. Л это верный рецепт превращения сопровождения в кошмар, Обычное временное решение этой проблемы заключается в построении специальных средств, которые поддерживали бы основные понятия проектирования. Эти средства предоставляют конструкции высокого уровня н проверку, чтобы компенсировать недостатки (преднамеренно обедненного) языка реализации. Таким образом, метод проектирования становится специализированным н, как правило, внутрифирменным языком программирования.
Такие языки в большинстве случаев плохо заменяют широко распространенные универсальные языки, поддерживаемые разнообразными средствами проектирования. Самая распространенная причина неиспользования классов в проекте — простая инерпня. Традиционные языки программирования не поддерживают понятия класса, а традиционные методы проектирования вынуждены отражать это отсутствие. Проектирование, как правило, сводилось к тому, чтобы разоить проблему на множество процедур, выполняющих нужные действия.
Такой подход, названный в ~лаве 2 процедурным программированием, в контексте проектирования часто называют функциональной декомпозицией. Обычно задают вопрос: «А можно использовать С++ вместе с методом проектирования, основанном на функциональной декомпозиции? ь Можно, но скорее всего, вы придете к тому, что будете пользоваться С++ как просто улучшенным С, и перед вами встанут вышеупомянутые проблемы. Это может быть приемлемо в переходный период для уже завершенных проектов и для подсистем, где классы вроде бы не принесут больших выгод (учитывая опыт конкретных исполнйтелей). Однако в более долгосрочном плане политика сдерживания широкомасштабного применения классов, вызванная привязанностью к функциональной декомпозиции, це совместима с эффективным использованием С++ и других языков, поддерживающих абстракцию.
Процедурно-ориентированный н объектно-ориентированный взгляды на программирование фундаментально различаются и, как правило, ведут к радикалыю различным решениям одной и той же проблемы. Это наблюдение одннаконо верно как для фазы проектирования, так и для фазы реализации: вы можете сфокусировать проект 801 24.2. Проектирование и язык программирования на выполняемых действиях или на представляемых сушностях, но не на том и другом одновременно. Так почему лсе следует предпочесть «объектно-ориентированное проектирование традиционным методам, основанным на функциональной декомпозиции? В первую очередь потому, что функциональная декомпозиция ведет к недостаточной абстракции данных. Из этого следует, что по.лучившийся в результате проект будет: менее податлив к изменениям; ° менее совместим с инструментальными средствами; ° менее приспособлен к параллельной разработке; менее приспособлен для совместного выполнения.
Проблема тут в том, что функциональная декомпозиция заставляет нас делать интересные данные глобальными, поскольку, когда система структурирована в виде дерева функций, любые данные, с которыми работают хотя бы две функции, должны быть глобальными по отношению к обеим этим функциям, г)то приводит к тому, что «интересные» данные всплывают все выше и выше к корню дерева по мере того, как все новым и новым функциям требуется доступ к ним (как обычно, считается, что дерево растет от корня вниз). Точно такой же процесс можно наблюдать в однокорневой иерархии классов, где «интересные данные и функции имеют тенденцию всплывать к корневому классу Я 24А).
Фокусирование на описании классов и инкапсуляции данных призвано решить эту проблему, делая зависимости между разными частямп программы явнылги и понятными. И что более важно — снижается число взаимозависимостей в системе за счет того, что улучшается локализация обращений к данным. Однако некоторые проблемы лучше решать написанием набора процедур. Суть объектно-ориентированного подхода к проектированию не в том, чтобы в программе не было функций-не-членов, или чтобы ни одна из частей программы не была процедурно-ориентированной. Нет, суть в том, чтобы разделить разные части программы так, чтобы они лучше отражали понятия прикладной области.
Как правило, это достигается, когда проект фокусируется на классах, а не на функциях. Применение процедурного стиля должно быть сознательным решением, а не просто использоваться по умолчанию. И классы, и пропедуры нужно применять в зависимости от приложения, а не просто как следствия жесткого метода проектирования. 24.2.2. Отказ от наследования Рассмотрим проект, не применяющий наследования.
Тогда программы просто не смогут воспользоваться основным преимуществом С++, в то же время некие выгоды от С++ по сравнению с С, Разса1, Еогггап, СОВОВ и т. д. будут получены. Распространенные причины отказа от наследования — не считая инерции — состоят в заявлениях, что «наследование — это деталь реализации», «наследование нарушает сокрытие информации» и «наследование затрудняет взаимодействие с другими программами». Взгляд на наследование просто как на деталь реализации не учитывает, что иерархия классов может непосредственно моделировать ключевые взаимоотношения между понятиями в данной прикладной области. Такие взаимоотношения должны быть выраженгя в проекте явно. Можно оправдать исключение наследования из части программ на С++, которые должны взаимодействовать с программами, написанными на других языках. Однако 802 Глава 24.
Проектирование и программирование это не достаточная причина для отказа от наследования во всей системе; это просто причина для тщательной спецификации и инкапсуляции интерфейса программы с «внешним миромм Аналогично, беспокойства, связанные с наругпением принципа сокрытия информации при наследовании Ц 24.3.2.1), должны просто заставить нас аккуратнее пользоваться виртуальными функциями и защищепныгии членами Я 15.3). Это не причина, чтобы отказываться от наследования вообще. Во многих случаях никакого реального выигрыша от наследования нет.