Стандарты программирования на С++. 101 правило и рекомендация
Шрифт:
Следует избегать данных со внешним связыванием в области видимости пространства имен или представляющих собой статические члены классов. Их применение усложняет логику программы и приводит к тесной связи между различными (и, что еще хуже, отдаленными) частями программы. Совместно используемые данные делают менее эффективным тестирование модулей, поскольку корректность фрагмента кода, использующего общие данные, обусловлена историей изменения этих данных, а кроме того, обуславливает функционирование некоторого, пока неизвестного, кода, который будет использовать эти данные позже.
Имена объектов в глобальном пространстве имен приводят к его дополнительному засорению.
Если вам никак не обойтись без глобальных объектов, объектов
Объекты, находящиеся в области видимости пространств имен, статические члены или совместно используемые разными потоками или процессами, снижают уровень распараллеливания в многопоточных и многопроцессорных средах, и часто являются узким местом с точки зрения производительности и масштабируемости (см. рекомендацию 7). Старайтесь избавиться от совместного использования данных; используйте вместо него средства коммуникации (например, очередь сообщений).
Предпочтительно обеспечить низкую связность и минимизировать взаимодействие классов (см. [Cargill92]).
Такие средства уровня программы, как
Код, в котором объект совместно используется разными потоками, должен обеспечить сериализацию обращений к такому объекту (см. рекомендацию 12 и [Sutter04c]).
[Cargill92] pp. 126, 136, 169-173 • [Dewhurst03] §3 • [Lakos96] §2.3.1 • [McConnell93] §5.1-4 • [Stroustrup00] §C.10.1 • [Sutter00] §47 • [Sutter02] §16, Appendix A • [Sutter04c] • [SuttHysl03]
11. Сокрытие информации
Не выпускайте внутреннюю информацию за пределы объекта, обеспечивающего абстракцию.
Для минимизации зависимостей между вызывающим кодом, который работает с абстракцией, и реализацией абстракции, внутренние данные такой реализации должны быть скрыты. В противном случае вызывающий код может обратиться к этой информации (или, что еще хуже, изменить ее), и такая утечка предназначенной сугубо для внутреннего использования информация делает вызывающий код зависимым от внутреннего представления абстракции. Открывать следует абстракции (предпочтительно из соответствующей предметной области, но, как минимум, абстракцию
Сокрытие информации уменьшает стоимость проекта, сроки разработки и/или риск двумя основными путями.
• Оно локализует изменения. Сокрытие информации снижает область "эффекта волны" вносимого изменения и, соответственно, его стоимость.
• Оно усиливает инварианты. Сокрытие информации ограничивает код, ответственный за сохранение (и, в случае ошибки, за нарушение) инвариантов программы (см. рекомендацию 41).
Не позволяйте "засветиться" данным из любого объекта, который обеспечивает
абстракцию (см. также рекомендацию 10). Данные — всего лишь одно из воплощений абстракции, концептуальное состояние. Если вы сконцентрируетесь на концепции, а не на ее представлениях, вы сможете предложить вдумчивый интерфейс, а "вылизать" реализацию можно и позже — например, путем применения кэширования вместо вычисления "на лету", или использования различных оптимизирующих представлений (например, полярных координат вместо декартовых).Распространенный пример состоит в том, что члены-данные классов никогда не делаются доступными извне при помощи спецификатора
Тестирование кода зачастую требует возможности обращения ко "внутренностям" тестируемого класса или модуля.
Совокупности значений (
[Brooks95] §19 • [McConnell93] §6.2 • [Parnas02] • [Stroustrup00] §24.4 • [SuttHysl04a]
12. Кодирование параллельных вычислений
Если ваше приложение использует несколько потоков или процессов, следует минимизировать количество совместно используемых объектов, где это только можно (см. рекомендацию 10), и аккуратно работать с оставшимися.
Работа с потоками — отдельная большая тема. Данная рекомендация оказалась в книге, потому что эта тема очень важна и требует рассмотрения. К сожалению, одна рекомендация не в силах сделать это полно и корректно, поэтому мы только резюмируем несколько наиболее важных положений и посоветуем обратиться к указанным ниже ссылкам за дополнительной информацией. Среди наиболее важных вопросов, касающихся параллельных вычислений, такие как избежание взаимоблокировок (deadlock), неустойчивых взаимоблокировок (livelock) и условий гонки (race conditions).
Стандарт С++ ничего не говорит о потоках. Тем не менее, С++ постоянно и широко применяется для написания кода с интенсивным использованием многопоточности. Если в вашем приложении потоки совместно используют данные, на это следует обратить особое внимание.
• Ознакомьтесь с документацией по целевой платформе на предмет локальных примитивов синхронизации. Обычно они охватывают диапазон от простых атомарных операций с целыми числами до межпроцессных взаимоисключений и семафоров.
• Предпочтительно "обернуть" примитивы платформы в собственные абстракции. Это хорошая мысль, в особенности если вам требуется межплатформенная переносимость. Вы можете также воспользоваться библиотекой (например, pthreads [Butenhof97]), которая сделает это за вас.
• Убедитесь, что используемые вами типы можно безопасно применять в многопоточных программах. В частности, каждый тип должен как минимум
• гарантировать независимость объектов, которые не используются совместно. Два потока могут свободно использовать различные объекты без каких-либо специальных действий со стороны вызывающего кода;