Подтвердить что ты не робот

Следует ли запрещать защищенные атрибуты?

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

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

4b9b3361

Ответ 1

В этом интервью по дизайну Билла Веннерса Джошуа Блох, автор Effective Java, говорит:

Доверенные подклассы

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

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

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

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

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

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

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

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

Bill Venners: И защищенные данные?

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

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

Ответ 2

С#:

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

Ответ 3

Вам могут понадобиться их для статического (или глобального) атрибута, для которого вы хотите, чтобы ваши подклассы или классы из одного пакета (если это о java) могли извлечь выгоду.

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

Ответ 4

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

Пункт 22: объявить члены данных частными.

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

Ответ 5

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

Ответ 6

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

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

Ответ 7

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

Ответ 8

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

Ответ 9

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

Ответ 10

Недавно я работал над проектом, так как "защищенный" член был очень хорошей идеей. Класс hiearchy был чем-то вроде:

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

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

Косвенность может иметь как минимум два варианта: std:: map и stdext:: hash_map. Обе карты будут вести себя одинаково, но для того факта, что hash_map нуждается в хэшировании ключа (в VC2003, castable to size_t).

Итак, BaseMap реализовал TMap как шаблонный тип, который был картографическим контейнером.

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

Итак:

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

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

  • Карта и HashMap нуждались в легком доступе чтения/записи к TMap, определенному в BaseMap.

Для меня единственным решением было использование защищенных для переменных std:: list и TMap. Невозможно было поместить эти "private", потому что я все равно выставлял бы все или почти все свои функции с помощью аксессуаров чтения/записи.

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

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

Ответ 11

Я использую их. Короче говоря, это хороший способ, если вы хотите, чтобы некоторые атрибуты были разделены. Конечно, вы можете написать для них функции set/get, но если нет проверки, то какая точка? Это также быстрее.

Рассмотрим это: у вас есть класс, который является вашим базовым классом. Он имеет немало атрибутов, которые вы не должны использовать в дочерних объектах. Вы можете написать функцию get/set для каждого, или вы можете просто установить их.

Мой типичный пример - обработчик файла/потока. Вы хотите получить доступ к обработчику (то есть файловому дескриптору), но вы хотите скрыть его от других классов. Это проще, чем писать для него функцию set/get.

Ответ 12

В общем, да. Обычно защищенный метод лучше.

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

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

Кроме того, я начал изредка подвергать публичные переменные-члены при их объявлении final и хорошо сформированные классы, которые не слишком гибкие (опять же, не примитивы или коллекции).

Это не какой-то тупой ярлык, я подумал об этом довольно серьезно и решил, что нет никакой разницы между публичной переменной final, отображающей объект и getter.

Ответ 13

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

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

(С#)

static Random rnd=new Random();
//...
if (rnd.Next()%1000==0) throw new Exception("My base class sucks! HAHAHAHA! xD");
//...

Вы не можете запечатать каждый класс, чтобы предотвратить это.

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

Но я лично не люблю придерживаться принципов oop любой ценой. Специально создавая свойства с единственной целью сделать частные члены данных.

(С#):

private _foo;
public foo
{
   get {return _foo;}
   set {_foo=value;}
}

Это было мое личное мнение.

Но делайте то, что требует ваш босс (если он хочет частные поля, чем это делает.)

Ответ 14

Я использую защищенные переменные/атрибуты в базовых классах, которые, как я знаю, я не планирую переходить на методы. Таким образом, подклассы имеют полный доступ к их унаследованным переменным и не имеют (искусственно созданных) накладных расходов, проходящих через getters/seters для доступа к ним. Примером может служить класс, использующий основной поток ввода-вывода; есть мало оснований не допускать прямого доступа подкласса к базовому потоку.

Это отлично подходит для переменных-членов, которые используются простым способом в базовом классе и во всех подклассах. Но для переменной, которая имеет более сложное использование (например, доступ к ней вызывает побочные эффекты у других членов класса), доступная переменная не подходит. В этом случае его можно сделать частным и общедоступным/защищенным getters/seters. Примером является внутренний механизм буферизации, предоставляемый базовым классом, при котором доступ к буферам непосредственно из подкласса может скомпрометировать целостность алгоритмов, используемых базовым классом для управления ими.

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

Инкапсуляция велика, но ее можно занять слишком далеко. Я видел классы, чьи собственные частные методы обращались к своим переменным-членам, используя только методы getter/setter. Это слишком много, потому что если класс не может доверять своим собственным частным методам своим личным данным, кому он может доверять?