Причины использовать приватные вместо защищенных для полей и методов - PullRequest
35 голосов
/ 06 февраля 2011

Это довольно простой вопрос ОО, но тот, который беспокоил меня в течение некоторого времени.

Я стараюсь не использовать модификатор видимости private для моих полей и методов в пользу protected.

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

Но для большинства людей модификатор private обычно используется по умолчанию при определении непубличного поля / метода.

Итак, вы можете перечислить варианты использования для private? Есть ли главная причина всегда использовать личное? Или ты тоже считаешь, что он чрезмерно употреблен?

Ответы [ 7 ]

30 голосов
/ 06 февраля 2011

Есть некоторый консенсус, что следует отдавать предпочтение композиции вместо наследования в ООП. Есть несколько причин для этого (Google, если вы заинтересованы), но основная часть такова:

  • наследование редко является лучшим инструментом и не так гибко, как другие решения
  • защищенные элементы / поля образуют интерфейс для ваших подклассов
  • интерфейсы (и предположения об их будущем использовании) сложно получить правильно и правильно документировать

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

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

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

Альтернатива

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

Таким образом, вместо базового класса Vehicle с функцией виртуального привода () (наряду со всем остальным, таким как целое число для цены и т. Д.), У вас будет класс Vehicle, принимающий объект интерфейса Motor, и этот интерфейс двигателя предоставляет только функцию drive (). Теперь вы можете добавлять и повторно использовать любой тип двигателя в любом месте (более или менее.:).

16 голосов
/ 07 января 2013

В двух ситуациях имеет значение, является ли участник protected или private:

  1. Если производный класс мог бы извлечь выгоду из использования члена, то создание члена `protected` позволило бы ему сделать это, в то время как превращение его в` private 'лишило бы его этой выгоды.
  2. Если будущая версия базового класса может извлечь выгоду из-за того, что член не будет вести себя так, как это делает в настоящей версии, если сделать член `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> может изменить способ управления резервным хранилищем, не нарушая правильно написанные производные классы. Неправильно написанные производные классы могут сломаться, если базовая реализация изменится, но это ошибка производного класса, а не базового.

4 голосов
/ 06 февраля 2011

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

2 голосов
/ 06 февраля 2011

Я достигаю здесь. Тем не менее, я думаю, что использование переменных-членов Protected должно быть осознанным, потому что вы не только планируете наследовать, но и потому, что есть веская причина, по которой классифицированные производные не должны использовать свойства Setters / Getters, определенные в базовом классе.

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

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

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

Конечно, во многих случаях это "не имеет значения", потому что мы не реализуем ничего внутри метода получения / установки, кроме доступа к переменной. Но опять же, если это так, к производному классу можно так же легко получить доступ через getter / setter. Это также защищает от труднодоступных ошибок позже, если используется постоянно. Если начальное значение метода get / setter для поля-члена в базовом классе каким-либо образом изменяется, а производный класс напрямую ссылается на защищенное поле, существует вероятность возникновения проблем.

1 голос
/ 26 апреля 2017

Я программировал ООП с C ++ в 1993 году и с Java в 1995 году. Снова и снова я видел необходимость дополнения или пересмотра класса, обычно добавляя дополнительную функциональность, тесно интегрированную с классом.ООП способ сделать это состоит в том, чтобы создать подкласс базового класса и внести изменения в подкласс.Например, поле базового класса, первоначально упоминаемое только в другом месте базового класса, необходимо для какого-либо другого действия, или какое-то другое действие должно изменить значение поля (или одного из содержащихся в нем членов).Если это поле является частным в базовом классе, то подкласс не может получить к нему доступ, не может расширить функциональность.Если поле защищено, оно может сделать это.

Подклассы имеют особое отношение к базовому классу, которого нет в других классах в иерархии классов: они наследуют члены базового класса.Целью наследования является доступ к членам базового класса;частное препятствует наследованию.Как разработчик базового класса должен знать, что подклассам никогда не понадобится доступ к члену?В некоторых случаях это может быть ясно, но частное должно быть исключением, а не правилом.Разработчики, создающие подклассы базового класса, имеют исходный код базового класса, поэтому их альтернативой является непосредственный пересмотр базового класса (возможно, просто изменение частного статуса на защищенный перед созданием подклассов).Это не чистая, хорошая практика, но это то, что заставляет вас делать личное.

1 голос
/ 07 января 2013

Вы на правильном пути.Вы делаете что-то приватное, потому что ваша реализация зависит от того, не было ли оно изменено ни пользователем, ни потомком.

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

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

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

0 голосов
/ 14 октября 2016

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

За пределами моделирования чего-либо, программирование - это больше решение проблемы.Проблема «частных, защищенных и общедоступных» элементов является проблемой только тогда, когда она связана с принятием надежного решения.Как решатель проблем, я бы не стал ошибаться, пытаясь понять, как другие используют МОЕ решение для решения своих собственных проблем.Теперь имейте в виду, что основной причиной проблемы ...., было предоставление места для проверки данных (т. Е. Проверка того, что данные находятся в допустимом диапазоне и структуре перед использованием их в вашем объекте).

Имея это в виду, что если ваш код решает проблему, для которой он был разработан, вы сделали свою работу.Если другим нужно ваше решение для решения той же или подобной проблемы - ну, вам действительно нужно контролировать, как они это делают.Я бы сказал: «Только если вы получаете какую-то выгоду от этого или знаете недостатки своего дизайна, поэтому вам нужно защищать некоторые вещи».

...