В двух ситуациях имеет значение, является ли участник protected
или private
:
- Если производный класс мог бы извлечь выгоду из использования члена, то создание члена `protected` позволило бы ему сделать это, в то время как превращение его в` private 'лишило бы его этой выгоды.
- Если будущая версия базового класса может извлечь выгоду из-за того, что член не будет вести себя так, как это делает в настоящей версии, если сделать член `private ', то эта будущая версия позволит изменить поведение (или полностью исключить член), в то время как создание его «защищенного» потребовало бы от всех будущих версий класса того же поведения, что лишало бы его выгоды, которую можно было бы получить от его изменения.
Если можно представить реалистичный сценарий, в котором производный класс мог бы извлечь выгоду из возможности доступа к члену, и не может представить сценарий, в котором базовый класс мог бы извлечь выгоду из изменения его поведения, тогда член должен иметь значение protected
[при условии Конечно, это не должно быть публично. Если нельзя представить сценарий, в котором производный класс получит большую выгоду от непосредственного доступа к члену, но можно представить сценарии, в которых будущая версия базового класса может извлечь выгоду, изменив ее, тогда это должно быть private
. Эти случаи довольно ясны и понятны.
Если нет какого-либо правдоподобного сценария, когда базовый класс выиграл бы от смены члена, я бы посоветовал сделать это protected
. Кто-то скажет, что принцип «ЯГНИ» («Вам это не нужно») благоприятствует private
, но я не согласен. Если вы ожидаете, что другие унаследуют класс, то закрытие члена не предполагает «YAGNI», а скорее «HAGNI» (он не нужен). Если «вам» не понадобится менять поведение элемента в будущей версии класса, «вам» не нужно, чтобы оно было private
. Напротив, во многих случаях у вас не будет возможности предсказать, что может потребоваться потребителям вашего класса. Это не означает, что нужно сделать членов protected
без предварительной попытки определить, какие выгоды можно получить, изменив их, поскольку YAGNI
на самом деле не применимо ни к одному из решений. YAGNI применяется в тех случаях, когда можно будет справиться с будущей потребностью, если и когда она возникнет, поэтому нет необходимости иметь дело с ней сейчас. Решение сделать членом класса, который предоставляется другим программистам private
или protected
, подразумевает решение о том, какой тип потенциальной будущей потребности будет обеспечен, и затруднит обеспечение другого.
Иногда оба сценария могут быть правдоподобными, и в этом случае может быть полезно предложить два класса - один из которых выставляет рассматриваемые члены, а класс является производным от того, чего нет (стандартного идиоматического не было для производного класса) скрыть элементы, унаследованные от его родителя, хотя объявление новых членов, которые имеют те же имена, но не имеют компилируемой функциональности и помечены атрибутом Obsolete
, будет иметь такой эффект). В качестве примера компромиссов рассмотрим List<T>
. Если бы этот тип предоставил вспомогательный массив как защищенный член, можно было бы определить производный тип CompareExchangeableList<T> where T:Class
, который включал бы член T CompareExchangeItem(index, T T newValue, T oldvalue)
, который возвращал бы Interlocked.CompareExchange(_backingArray[index], newValue, oldValue)
; такой тип мог использоваться любым кодом, который ожидал List<T>
, но код, который знал, что экземпляр был CompareExchangeableList<T>
, мог использовать CompareExchangeItem
на нем. К сожалению, поскольку List<T>
не предоставляет массив поддержки производным классам, невозможно определить тип, который допускает CompareExchange
для элементов списка, но который по-прежнему будет использоваться кодом, ожидающим List<T>
.
Тем не менее, это не значит, что раскрытие массива поддержки было бы совершенно бесплатно; даже если во всех существующих реализациях List<T>
используется один резервный массив, Microsoft может реализовать будущие версии для использования нескольких массивов, когда в противном случае список будет превышать 84 КБ, чтобы избежать неэффективности, связанной с кучей больших объектов. Если резервный массив был представлен как защищенный элемент, было бы невозможно реализовать такое изменение, не нарушая какой-либо код, который полагался на этот элемент.
На самом деле, идеальным вариантом было бы сбалансировать эти интересы путем предоставления защищенного члена, который при наличии индекса элемента списка вернет сегмент массива, который содержит указанный элемент. Если существует только один массив, метод всегда возвращает ссылку на этот массив со смещением, равным нулю, начальным индексом, равным нулю, и длиной, равной длине списка. Если в будущей версии List<T>
массив будет разбит на несколько частей, метод может позволить производным классам эффективно обращаться к сегментам массива способами, которые были бы невозможны без такого доступа [например, использование Array.Copy
], но List<T>
может изменить способ управления резервным хранилищем, не нарушая правильно написанные производные классы. Неправильно написанные производные классы могут сломаться, если базовая реализация изменится, но это ошибка производного класса, а не базового.