41. Делайте данные-члены закрытыми (кроме случая агрегатов в стиле структур С)
41. Делайте данные-члены закрытыми (кроме случая агрегатов в стиле структур С)
Резюме
Данные-члены должны быть закрыты. Только в случае простейших типов в стиле структур языка С, объединяющих в единое целое набор значений, не претендующих на инкапсуляцию и не обеспечивающих поведение, делайте все данные-члены открытыми. Избегайте смешивания открытых и закрытых данных, что практически всегда говорит о бестолковом дизайне.
Обсуждение
Сокрытие информации является ключом к качественной разработке программного обеспечения (см. рекомендацию 11). Желательно делать все данные-члены закрытыми; закрытые данные — лучшее средство для сохранения инварианта класса, в том числе при возможных вносимых изменениях.
Открытые данные — плохая идея, если класс моделирует некоторую абстракцию и, следовательно, должен поддерживать инварианты. Наличие открытых данных означает, что часть состояния вашего класса может изменяться неконтролируемо, непредсказуемо и асинхронно с остальной частью состояния. Это означает, что абстракция разделяет ответственность за поддержание одного или нескольких инвариантов с неограниченным множеством кода, который использует эту абстракцию, и совершенно очевидно, что такое положение дел просто недопустимо с точки зрения корректного проектирования.
Защищенные данные обладают всеми недостатками открытых данных, поскольку наличие защищенных данных означает, что абстракция разделяет ответственность за поддержание одного или нескольких инвариантов с неограниченным множеством кода — теперь это код существующих и будущих производных классов. Более того, любой код может читать и модифицировать защищенные данные так же легко, как и открытые — просто создав производный класс и используя его для доступа к данным.
Смешивание открытых и закрытых данных-членов в одном и том же классе является непоследовательным и попросту запутывает пользователей. Закрытые данные демонстрируют, что у вас есть некоторые инварианты и нечто, предназначенное для их поддержания. Смешивание их с открытыми данными-членами означает, что при проектировании так окончательно и не решено, должен ли класс представлять некоторую абстракцию или нет.
Не закрытые данные-члены почти всегда хуже даже простейших функций для получения и установки значений, поскольку последние обеспечивают устойчивость кода к возможным внесениям изменений.
Подумайте о сокрытии закрытых членов класса с использованием идиомы Pimpl (см. рекомендацию 43).
Примеры
Пример 1. Корректная инкапсуляция. Большинство классов (например, Matrix, File, Date, BankAccount, Security) должны закрывать все данные-члены и открывать соответствующие интерфейсы. Позволение вызывающему коду непосредственно работать с внутренними данными класса работает против представленной им абстракции и поддерживаемых им инвариантов.
Агрегат Node, широко используемый в реализации класса List, обычно содержит некоторые данные и два указателя на Node: next_ и prev_. Данные-члены Node не должны быть скрыты от List. Однако не забудьте рассмотреть еще пример 3.
Пример 2. TreeNode. Рассмотрим контейнер Tree<T>, реализованный с использованием TreeNode<T>, агрегата, используемого в Tree, который хранит указатели на предыдущий, следующий и родительский узлы и сам объект T. Все члены TreeNode могут быть открытыми, поскольку их не надо скрывать от класса Tree, который непосредственно манипулирует ими. Однако класс Tree должен полностью скрывать класс TreeNode (например, как вложенный закрытый класс или как определенный только в файле реализации класса Tree), поскольку это — детали внутренне реализации класса Tree, от которых не должен зависеть и с которыми не должен иметь дела вызывающий код. И наконец, Tree не скрывает содержащиеся в контейнере объекты T, поскольку за них отвечает вызывающий код; контейнеры используют абстракцию итераторов для предоставления доступа к содержащимся объектам, в то время как внутренняя структура контейнера остается скрытой.
Пример 3. Функции получения и установки значений. Если не имеется лучшей предметной абстракции, открытые и защищенные данные-члены (например, color) могут, как минимум, быть сделаны закрытыми и скрыты за функциями получения и установки значений (например, GetColor, SetColor). Тем самым обеспечивается минимальный уровень абстракции и устойчивость к изменениям.
Использование функций повышает уровень общения по поводу "цвета" от конкретного состояния до абстрактного, которое мы можем реализовать тем способом, который сочтем наиболее приемлемым. Мы можем изменить внутреннее представление цвета, добавить код для обновления дисплея при изменении цвета, добавить какие-то инструментальные средства или внести еще какие-то изменения — и все это без каких-либо изменений в вызывающем коде. В худшем случае вызывающий код потребуется перекомпилировать (т.е. мы сохраняем совместимость на уровне исходных текстов); в лучшем — не потребуется ни перекомпиляция, ни даже перекомпоновка (если изменения сохраняют бинарную совместимость). Ни совместимость на уровне исходных текстов, ни бинарная совместимость при внесении таких изменений невозможны, если исходный дизайн содержит открытый член color, с которым тесно связан вызывающий код.
Исключения
Функции получения и установки значений полезны, но дизайн класса, состоящего практически из одних таких функций, оставляет желать лучшего. Подумайте над тем, требуется ли в таком случае обеспечение абстракции или достаточно ограничиться простой структурой.
Агрегаты значений (известные как структуры в стиле С) просто хранят вместе набор различных данных, но при этом не обеспечивают ни их поведение, ни делают попыток моделирования абстракций или поддержания инвариантов. Такие агрегаты не предназначены для того, чтобы быть абстракциями. Все их данные-члены должны быть открытыми, поскольку эти данные-члены и представляют собой интерфейс. Например, шаблон класса std::pair<T, U> используется стандартными контейнерами для объединения двух несвязанных элементов типов T и U, и при этом pair сам по себе не привносит ни поведения, ни каких-либо инвариантов.
Ссылки
[Dewhurst03] §80 • [Henricson97] pg. 105 • [Koenig97] §4 • [Lakos96] §2.2 • [Meyers97] §20 • [Murray93] §2.3 • [Stroustrup00] §10.2.8, §15.3.1.1, §24.4.2-3 • [SuttHysl04a]
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКЧитайте также
4.11.4. Правила "все кроме"
4.11.4. Правила "все кроме" Очень часто приходится задавать правила в виде "все кроме". Например, нужно запретить доступ к порту telnet всем пользователям, кроме компьютера с адресом 192.168.77.10. Лучше поступить следующим образом: сначала разрешить доступ для компьютера 192.168.77.10, а
Принцип 7: Делайте упор на конверсию
Принцип 7: Делайте упор на конверсию Давайте совершим небольшое путешествие во времени. в март 2000 года. В журнале Fortune появляется статья, в которой Билл Керли, один из ведущих интернет-аналитиков, сколотивший состояние на вложениях в венчурный бизнес, пишет, что
§ 57. Делайте сайты проще
§ 57. Делайте сайты проще Простота — необходимое условие прекрасного. Л. Н. Толстой. Из письма к Л. Андрееву от 02.09.1902 2 августа 2000Почти сто лет прошло с момента написания эпиграфа к этому параграфу, а ценность высказывания не уменьшилась.Желание усложнить всегда
Влияние допуска резисторов на анализ наихудшего случая
Влияние допуска резисторов на анализ наихудшего случая В только что проведенном анализе мы изменяли только коэффициент hFE транзистора. Каким был бы анализ на наихудший случай, при выборе резистора в качестве изменяемого фактора допуска на сопротивление? Чтобы упростить
9.6. Анализ наихудшего случая
9.6. Анализ наихудшего случая Анализ наихудшего случая тесно связан с анализом Монте-Карло. Здесь также делается попытка определить поведение электронной схемы, когда ее компонентам предписаны допуски. Особенность анализа наихудшего случая (в программе PSPICE он называется
9.6.1. Общее представление об анализе наихудшего случая
9.6.1. Общее представление об анализе наихудшего случая Прежде чем приступить к проведению анализа наихудшего случая, вы, конечно же, должны начертить схему и указать ее параметры допуска (для сравнения см. раздел 9.5). Кроме того, вы должны определить, что, собственно, следует
9.6.2. Определение наихудшего случая активного фильтра
9.6.2. Определение наихудшего случая активного фильтра В качестве примера анализа наихудшего случая исследуем частотную характеристику активного фильтра, изображённого на рис. 9.32. Выясним, какими будут наихудшие случаи в направлениях снизу вверх (Hi) и сверху вниз (Lo). Вслед
23. Делайте заголовочные файлы самодостаточными
23. Делайте заголовочные файлы самодостаточными РезюмеУбедитесь, что каждый написанный вами заголовочный файл компилируем самостоятельно, т.е. что он включает все заголовочные файлы, от которых зависит его содержимое.ОбсуждениеЕсли один заголовочный файл не работает,
87. Делайте предикаты чистыми функциями
87. Делайте предикаты чистыми функциями РезюмеПредикат представляет собой функциональный объект, который возвращает ответ да/нет, обычно в виде значения типа bool. Функция является "чистой" в математическом смысле, если ее результат зависит только от ее аргументов
Правило 22: Объявляйте данные-члены закрытыми
Правило 22: Объявляйте данные-члены закрытыми В этом правиле мы поговорим о том, почему данные-члены не должны быть открытыми (public). Затем мы убедимся, что все аргументы против открытых данных-членов касаются также защищенных (protected). Это приведет нас к выводу, что
13.1.1. Данные-члены
13.1.1. Данные-члены Данные-члены класса объявляются так же, как переменные. Например, у класса Screen могут быть следующие данные-члены:#includeclass Screen {string _screen; // string( _height * _width )string::size_type _cursor; // текущее положение на экранеshort _height; // число строкshort _width; //
15.1.1. Члены и не члены класса
15.1.1. Члены и не члены класса Рассмотрим операторы равенства в нашем классе String более внимательно. Первый оператор позволяет устанавливать равенство двух объектов, а второй – объекта и C-строки:#include "String.h"int main() {String flower;// что-нибудь записать в переменную flowerif ( flower == "lily" ) //
Делайте меньше
Делайте меньше Меньше соревнуйтесь Народная мудрость гласит: чтобы подавить своих конкурентов, вы должны переиграть их. Если у них четыре возможности, у вас их должно быть пять, пятнадцать, двадцать… Если они тратят N, то вы должны потратить NN. Если у них есть 20, то у вас
Ничего кроме правды
Ничего кроме правды Сила спецификаций АТД проистекает из их способности отражать только существенные свойства структур данных без лишних деталей. Приведенная выше спецификация стеков выражает все, что нужно по существу знать о понятии стека, и не включает ничего, что
Sketchpad Айвена Сазерленда и сила случая Евгений Лебеденко
Sketchpad Айвена Сазерленда и сила случая Евгений Лебеденко Опубликовано 09 ноября 2012 года В конце пятидесятых годов прошлого века руководство Массачусетского Технологического Института (MIT) передало университетскому городку вычислительную машину