Сокрытие информации уменьшает стоимость проекта, сроки разработки и/или риск двумя основными путями.
• Оно локализует изменения. Сокрытие информации снижает область "эффекта волны" вносимого изменения и, соответственно, его стоимость.
• Оно усиливает инварианты. Сокрытие информации ограничивает код, ответственный за сохранение (и, в случае ошибки, за нарушение) инвариантов программы (см. рекомендацию 41).
Не позволяйте "засветиться" данным из любого объекта, который обеспечивает абстракцию (см. также рекомендацию 10). Данные — всего лишь одно из воплощений абстракции, концептуальное состояние. Если вы сконцентрируетесь на концепции, а не на ее представлениях, вы сможете предложить вдумчивый интерфейс, а "вылизать" реализацию можно и позже — например, путем применения кэширования вместо вычисления "на лету", или использования различных оптимизирующих представлений (например, полярных координат вместо декартовых).
Распространенный пример состоит в том, что члены-данные классов никогда не делаются доступными извне при помощи спецификатора public
(см. рекомендацию 41) или посредством указателей или дескрипторов (см. рекомендацию 42). Этот принцип в той же степени применим и к большим сущностям — например, таким, как библиотеки, которые также не должны разрешать доступ к внутренней информации извне. Модули и библиотеки должны предоставлять интерфейсы, которые определяют абстракции и работу с ними, и таким образом обеспечивают большую безопасность для вызывающего кода и меньшую связность, чем при применении совместно используемых данных.
Исключения
Тестирование кода зачастую требует возможности обращения ко "внутренностям" тестируемого класса или модуля.
Совокупности значений ( struct
в стиле С), которые представляют собой просто набор данных без предоставления каких-либо абстракций, не требуют сокрытия своих данных, которые в этом случае являются интерфейсом (см. рекомендацию 41).
Ссылки
[Brooks95] §19 • [McConnell93] §6.2 • [Parnas02] • [Stroustrup00] §24.4 • [SuttHysl04a]
12. Кодирование параллельных вычислений
Резюме
Если ваше приложение использует несколько потоков или процессов, следует минимизировать количество совместно используемых объектов, где это только можно (см. рекомендацию 10), и аккуратно работать с оставшимися.
Обсуждение
Работа с потоками — отдельная большая тема. Данная рекомендация оказалась в книге, потому что эта тема очень важна и требует рассмотрения. К сожалению, одна рекомендация не в силах сделать это полно и корректно, поэтому мы только резюмируем несколько наиболее важных положений и посоветуем обратиться к указанным ниже ссылкам за дополнительной информацией. Среди наиболее важных вопросов, касающихся параллельных вычислений, такие как избежание взаимоблокировок (deadlock), неустойчивых взаимоблокировок (livelock) и условий гонки (race conditions).
Стандарт С++ ничего не говорит о потоках. Тем не менее, С++ постоянно и широко применяется для написания кода с интенсивным использованием многопоточности. Если в вашем приложении потоки совместно используют данные, на это следует обратить особое внимание.
• Ознакомьтесь с документацией по целевой платформе на предмет локальных примитивов синхронизации. Обычно они охватывают диапазон от простых атомарных операций с целыми числами до межпроцессных взаимоисключений и семафоров.
• Предпочтительно "обернуть" примитивы платформы в собственные абстракции. Это хорошая мысль, в особенности если вам требуется межплатформенная переносимость. Вы можете также воспользоваться библиотекой (например, pthreads [Butenhof97]), которая сделает это за вас.
• Убедитесь, что используемые вами типы можно безопасно применять в многопоточных программах. В частности, каждый тип должен как минимум
• гарантировать независимость объектов, которые не используются совместно. Два потока могут свободно использовать различные объекты без каких-либо специальных действий со стороны вызывающего кода;
• документировать необходимые действия со стороны вызывающего кода при использовании одного объекта в разных потоках. Многие типы требуют сериализации доступа к таким совместно используемым объектам, но есть типы, для которых это условие не является обязательным. Обычно такие типы либо спроектированы таким образом, что избегают требований блокировки, либо выполняют блокировку самостоятельно. В любом случае, вы должны быть ознакомлены с ограничениями, накладываемыми используемыми вами типами.
Читать дальше