Должны ли защищенные атрибуты всегда быть запрещены? - PullRequest
14 голосов
/ 16 сентября 2008

Я редко использую наследование, но когда я использую, я никогда не использую защищенные атрибуты, потому что я думаю, что это нарушает инкапсуляцию наследуемых классов.

Используете ли вы защищенные атрибуты? для чего вы их используете?

Ответы [ 14 ]

19 голосов
/ 10 октября 2009

В этом интервью по проекту Билла Веннерса, Джошуа Блох, автор Эффективная Java говорит:

Доверяющие подклассы

Билл Веннерс: Должен ли я доверять подклассам более глубоко, чем не-подклассы? Например, я делаю это проще для подкласса реализация сломать меня, чем я будет для не подкласса? В в частности, как вы относитесь к защищенные данные?

Джош Блох: Чтобы написать что-то подклассовое и надежное против вредоносного подкласса на самом деле довольно сложная вещь, при условии, что вы даете доступ к подклассу к вашим внутренним структурам данных. Если у подкласса нет доступа к все, что обычный пользователь нет, тогда это сложнее для подкласс для нанесения ущерба. Но если вы сделайте все ваши методы окончательными, подкласс все еще может сломать ваш контракты, просто делая неправильно вещи в ответ на метод призывание. Именно поэтому критические классы безопасности, такие как String являются окончательными. В противном случае кто-то мог написать подкласс, который делает строки казаться изменчивым, что было бы достаточно, чтобы сломать безопасность. Так что вы должен доверять своим подклассам. если ты не верь им, тогда ты не сможешь их, потому что подклассы могут так легко заставить класс нарушать его контракты.

Что касается защищенных данных в целом, это неизбежное зло. Так должно быть сведено к минимуму. Наиболее защищенные данные и защищенные методы составляют обязательство к реализации подробно. Защищенное поле является детали реализации, что вы делая видимым для подклассов. Даже Защищенный метод является частью внутренняя структура, которую вы делаете видимый для подклассов.

Причина, по которой вы делаете это видимым, заключается в том, что это часто необходимо для того, чтобы подклассы, чтобы сделать свою работу, или сделать это эффективно. Но как только вы сделали это, вы привержены этому. Сейчас то, что вам не разрешено изменить, даже если вы позже найдете более эффективная реализация, что нет дольше предполагает использование конкретное поле или метод.

Итак, при прочих равных условиях вы не должно быть никаких защищенных членов совсем. Но это сказал, если у вас слишком мало, тогда ваш класс не может быть использован как супер класс, или, по крайней мере, не так эффективный супер класс. Часто ты узнай по факту. Моя философия должен иметь как можно меньше защищенных членов возможно, когда вы впервые напишите учебный класс. Тогда попробуйте сделать его подклассом. Вы может обнаружить, что без определенного защищенный метод, все подклассы будут должен сделать что-то плохое.

Например, если вы посмотрите на AbstractList, вы обнаружите, что там это защищенный метод для удаления диапазон списка в один выстрел (removeRange). Почему это там? Потому что нормальная идиома для удаления диапазон, основанный на общедоступном API, позвоните subList, чтобы получить суб- List, а затем позвоните clear на этом суб- List. Без этого конкретного защищенный метод, однако, единственный вещь, которую clear мог бы сделать, это неоднократно удаляйте отдельные элементы.

Подумай об этом. Если у вас есть массив представление, что это будет делать? Это будет многократно свернуть массив, выполняю заказ N работаю N раз. Так будет взять квадратичное количество работы, вместо линейного объема работы что должно. Предоставляя это защищенный метод, мы разрешаем любой реализация, которая может эффективно удалить весь диапазон, чтобы сделать это. А также любая разумная List реализация может удалить диапазон более эффективно все сразу.

что нам нужно это защищено метод это то, что вам нужно быть умнее меня, чтобы узнать фронт. В основном я реализовал вещь. Затем, как мы начали подкласс это, мы поняли, что удаление диапазона было квадратичная. Мы не могли себе этого позволить, поэтому Я положил в защищенном способе. Я думаюэто лучший подход с защищенные методы. Вставьте всего лишь возможно, а затем добавьте больше по мере необходимости. Защищенные методы представляют обязательства по проектам, которые вы можете хочу изменить. Вы всегда можете добавить защищенные методы, но вы не можете взять их.

Билл Веннерс: А защищенные данные?

Джош Блох: То же самое, но даже больше. Защищенные данные еще больше опасно с точки зрения испортить ваш инварианты данных. Если вы даете кому-то иначе доступ к некоторым внутренним данным, у них есть свобода правления над ним.

Короткая версия: нарушает инкапсуляцию, но это необходимое зло, которое должно быть сведено к минимуму.

7 голосов
/ 16 сентября 2008

C #:

Я использую защищенные для абстрактных или виртуальных методов, которые я хочу переопределить базовыми классами. Я также делаю метод защищенным, если он может вызываться базовыми классами, но я не хочу, чтобы он вызывался вне иерархии классов.

6 голосов
/ 16 сентября 2008

Они могут вам понадобиться для статического (или «глобального») атрибута, который вы хотите использовать для своих подклассов или классов из одного пакета (если речь идет о Java).

Эти статические финальные атрибуты, представляющие некое «постоянное значение», редко имеют функцию получения, поэтому в этом случае может иметь смысл защищенный статический атрибут финала.

3 голосов
/ 16 сентября 2008

Скотт Мейерс говорит, не используйте защищенные атрибуты в Effective C ++ (3-е изд.):

Элемент 22: Объявить элементы данных личными.

Причина та же, что и вы: она нарушает инкапсуляцию. Следствием этого является то, что в противном случае локальные изменения в макете класса могут нарушить зависимые типы и привести к изменениям во многих других местах.

2 голосов
/ 16 апреля 2016

Ключевое слово protected является концептуальной ошибкой и языковым дизайном, а также несколькими современными языками, такими как Nim и Цейлон (см. http://ceylon -lang.org / Documentation / faq / language-design / # no_protected_modifier ), которые были тщательно спроектированы, а не просто копировали типичные ошибки, такого ключевого слова нет.

Это не защищенные элементы, которые нарушают инкапсуляцию, это разоблачающие элементы, которые не должны быть открыты, которые нарушают инкапсуляцию ... не имеет значения, являются ли они защищенными или общедоступными. Проблема с protected заключается в том, что он ошибочен и вводит в заблуждение ... объявление членов protected (а не private) не защищает их, а наоборот, точно так же, как public. Защищенный член, доступный вне класса, открыт миру, поэтому его семантика должна сохраняться вечно, как в случае public. Сама идея «защищенного» - это бессмыслица ... инкапсуляция - это не безопасность, а ключевое слово только способствует путанице между ними. Вы можете немного помочь, избегая любого использования protected в ваших собственных классах - если что-то является внутренней частью реализации, не является частью семантики класса и может измениться в будущем, тогда сделайте его частным или внутренними для вашего пакета, модуля, сборки и т. д. Если это неизменяемая часть семантики класса, сделайте ее общедоступной, и тогда вы не будете раздражать пользователей вашего класса, которые могут видеть, что в документации есть полезный член, но не может использовать его, если они не создают свои собственные экземпляры и не могут получить его путем создания подклассов.

2 голосов
/ 16 сентября 2008

Нет веских причин иметь защищенные атрибуты. Базовый класс должен иметь возможность зависеть от состояния, что означает ограничение доступа к данным с помощью методов доступа. Вы не можете никому дать доступ к вашим личным данным, даже детям.

1 голос
/ 18 сентября 2008

Я недавно работал над проектом, где «защищенный» участник был очень хорошей идеей. Класс hiearchy был что-то вроде:

[+] Base
 |
 +--[+] BaseMap
 |   |
 |   +--[+] Map
 |   |
 |   +--[+] HashMap
 |
 +--[+] // something else ?

В Base реализован std :: list, но больше ничего. Прямой доступ к списку был запрещен для пользователя, но поскольку базовый класс был неполным, он все равно полагался на производные классы для реализации косвенного обращения к списку.

Переадресация может происходить как минимум из двух разновидностей: std :: map и stdext :: hash_map. Обе карты будут вести себя одинаково, но для факта, что hash_map требует, чтобы ключ был хешируемым (в VC2003, преобразуемым в size_t).

Таким образом, BaseMap реализовал TMap как шаблонный тип, который был контейнероподобным контейнером.

Map и HashMap были двумя производными классами BaseMap, один из которых специализировал BaseMap для std :: map, а другой - для stdext :: hash_map.

Итак:

  • База не была пригодна для использования как таковая (нет общедоступных средств доступа!) И предоставляла только общие функции и код

  • BaseMap требовалось легкое чтение / запись в std :: list

  • Для Map и HashMap требовался простой доступ для чтения / записи к TMap, определенному в BaseMap.

Для меня единственным решением было использование protected для std :: list и переменных-членов TMap. Не было никакого способа, которым я мог бы поместить эти «частные», потому что я все равно раскрыл бы все или почти все их функции через средства доступа для чтения / записи.

В конце концов, я предполагаю, что если вы все-таки разделите свой класс на несколько объектов, каждый производный добавляет необходимые функции к своему родительскому классу, и только самый производный класс действительно пригоден для использования, тогда защищенный способ - это путь. Помог тот факт, что «защищенный член» был классом, и поэтому «сломаться» было почти невозможно.

Но в противном случае следует избегать максимально защищенного доступа (т. Е. По умолчанию использовать private и public, когда необходимо предоставить метод).

1 голос
/ 16 сентября 2008

Я думаю, что защищенные атрибуты - плохая идея. Я использую CheckStyle для применения этого правила в моих командах разработчиков Java.

1 голос
/ 16 сентября 2008

В общем, нет, вы действительно не хотите использовать защищенные элементы данных. Это вдвойне верно, если вы пишете API. Как только кто-то унаследует от вашего класса, вы никогда не сможете по-настоящему выполнять техническое обслуживание и не каким-то образом сломать его странным, а иногда и диким способом.

1 голос
/ 16 сентября 2008

Я не использую защищенные атрибуты в Java, потому что они защищены только пакетами. Но в C ++ я буду использовать их в абстрактных классах, что позволит наследующему классу наследовать их напрямую.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...