Частное против Защищенного - Видимость передовой практики [закрыто]


145

Я искал и знаю теоретическую разницу.

  • public - любой класс / функция может получить доступ к методу / свойству.
  • protected - только этот класс и любые подклассы могут иметь доступ к методу / свойству.
  • private - только этот класс может получить доступ к методу / свойству. Это даже не будет унаследовано.

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

До сих пор, чтобы сохранить концепцию наследования и полиморфизма, я использую publicвсе, к чему следует обращаться извне (например, конструкторы и функциональность основного класса), и protectedвнутренние методы (логика, вспомогательные методы и т. Д.). Я на правильном пути?

(Обратите внимание, что этот вопрос для меня, но также и для дальнейшего использования, так как я не видел такой вопрос, как этот)


33
Это имеет значение? Любой язык с поддержкой ООП имеет эту проблему. Мне довелось программировать на PHP, но я думаю, что этот вопрос относится к любому языку, поддерживающему ООП.
Призрак Мадары

2
Хорошо, честно, просто интересно, если ты забыл пометить. Теперь я вижу тег oop.
Пол Беллора

Я должен установить видимость (частный / публичный / защищенный) для каждого свойства класса? Или только некоторые из них должны иметь тип видимости? Если да, как решить, какое свойство должно иметь видимость в верхней части класса?
user4271704

1
случайно, разница между защищенным и частным кажется очевидной. Используйте защищенный, если подклассы будут использовать метод / переменную, в противном случае используйте private. В частности, если подклассы должны будут переопределить очень похожую частную переменную в родительской, просто сделайте ее защищенной.
iPherian

Существует другой спецификатор доступа internalв языке C #, который ограничивает уровень доступа класса или его членов в физической сборке. Хотя я не уверен насчет его поддержки или чего-то подобного в других языках.
RBT

Ответы:


128

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

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

Класс, который не предназначен для наследования, должен быть окончательным (в Java). Вы можете ослабить некоторые правила доступа (от частного к защищенному, от финального до не финального) ради модульного тестирования, но затем задокументируйте его и дайте понять, что, хотя метод защищен, он не должен быть переопределен.


1
Реальный вопрос здесь, о privateпротив protectedКогда я хочу это свойство наследуется, и когда не я? Я не могу точно сказать, хочет ли пользователь иногда в будущем взять мой класс и продлить его ...
Призрак Мадары

16
Ну, вопрос не в том, что пользователь хочет переопределить, а в том, что вы хотите разрешить переопределить. Обычно это помогает переключиться на другую сторону и попытаться подумать: если бы я использовал этот класс и создал подкласс, что бы я хотел переопределить? Иногда другие пользователи все еще будут скучать ...
Thorsten Dittmar

3
Защищенные поля - плохая практика наследования! , Пожалуйста, остерегайтесь этого. Вот почему
RBT

4
Частные поля плохие на практике при наследовании! (преувеличивая) Пожалуйста, прочитайте мой ответ.
Ларри

6
Типичное контрольно-уродливое мнение.
Бруно Desthuilliers

58

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

Старая мудрость

"отметьте это как частное, если у вас нет веских причин не делать этого"

Это имело смысл в те дни, когда оно было написано, до того, как открытый исходный код доминировал в пространстве библиотеки для разработчиков и в зависимости от VCS / mgmt. благодаря сотрудничеству с Github, Maven и т. д. стали сотрудничать друг с другом. Тогда еще можно было заработать, ограничивая способ (ы) использования библиотеки. Я провел, вероятно, первые 8 или 9 лет моей карьеры, строго придерживаясь этой «лучшей практики».

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

Вы когда-нибудь:

  • Были разочарованы, удивлены или обижены библиотекой и т. Д., В которой была ошибка, которая могла быть исправлена ​​с помощью наследования и нескольких строк кода, но из-за закрытых / финальных методов и классов пришлось ждать официального патча, который может никогда не появиться? У меня есть.
  • Хотели использовать библиотеку для немного другого варианта использования, чем предполагали авторы, но не смогли этого сделать из-за закрытых / финальных методов и классов? У меня есть.
  • Были разочарованы, удивлены или обижены библиотекой и т. Д., Которая была чрезмерно разрешительной в своей расширяемости? Я не.

Вот три самых больших объяснения, которые я слышал о пометке методов private по умолчанию:

Рационализация № 1: это небезопасно и нет причин переопределять определенный метод

Я не могу сосчитать, сколько раз я ошибался в том, будет ли когда-либо необходимость переопределять определенный метод, который я написал. Проработав несколько популярных библиотек с открытым исходным кодом, я с трудом узнал истинную стоимость маркировки вещей частными. Это часто исключает единственное практическое решение непредвиденных проблем или вариантов использования. И наоборот, я никогда за более чем 16 лет профессионального развития не сожалел о том, что помечал метод, защищенный вместо частного, по причинам, связанным с безопасностью API. Когда разработчик решает расширить класс и переопределить метод, он сознательно говорит: «Я знаю, что я делаю». и ради производительности этого должно быть достаточно. период. Если это опасно, обратите внимание на класс / метод Javadocs, а не просто слепо захлопните дверь.

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

Рационализация № 2: поддерживает чистоту публичного API / Javadocs

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

Рационализация № 3: Мое программное обеспечение является коммерческим, и мне нужно ограничить его использование.

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

Никогда не говори никогда

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

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


Wrt your Rationalization # 1- Когда я отмечаю что-то как защищенное, я решаю сделать это явно, поскольку я чувствую, что это поведение не является окончательным и может быть случаем, когда мои дочерние классы захотят переопределить это. Если я не уверен, тогда я бы хотел сделать его приватным по умолчанию. Специально для такого языка, как C #, который по умолчанию поддерживает метод, не являющийся виртуальным, добавление только защищенного спецификатора доступа не имеет смысла. Вы должны добавить оба слова virtualи protectedключевые слова, чтобы сделать ваше намерение очень ясным. Кроме того, люди предпочитают ассоциацию, а не наследование, так protectedкак дефолт трудно понять
RBT

4
Сочетание ключевых слов, необходимых для того, чтобы сделать метод переопределенным, является языковой деталью ИМХО. Встречный аргумент, который я привожу к рационализации № 1, прямо нацелен на случай, когда вы упоминаете «Если я не уверен ...». Практически говоря, если вы не можете придумать причину, по которой это было бы опасно, то есть больше, если вы выбираете расширяемость. Когда вы говорите «ассоциация», я воспринимаю это как синоним композиции, и в этом случае я не вижу, что это имеет значение в любом случае.
Ник

3
Я не помню, сколько раз мне приходилось «клонировать» базовые классы только потому, что я хотел переопределить 1 или 2 или методы. И, как вы сказали, с социальной тенденцией о том, что мы делимся большим количеством кода, чем когда-либо прежде, гораздо важнее использовать защищенный, чем приватный по умолчанию. т.е. быть более открытым для вашей собственной логики.
Синькоу

1
Согласен. Я просто хотел настроить Java BufferedReader для обработки пользовательских разделителей строк. Благодаря закрытым полям я должен клонировать весь класс, а не просто расширять его и переопределять readLine ().
Рон Данн

21

Хватит злоупотреблять приватными полями !!!

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

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

Есть два случая, когда вы хотите расширить класс:

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

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

Рассмотрим простую библиотеку, которая моделирует автомобили:

class Car {
    private screw;
    public assembleCar() {
       screw.install();
    };
    private putScrewsTogether() {
       ...
    };
}

Автор библиотеки подумал: нет причин, по которым пользователям моей библиотеки нужен доступ к деталям реализации, assembleCar()верно? Давайте отметим винт как частный.

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

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

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

Я очень поддерживаю ответ Ника.


2
Использование закрытых полей в основном говорит о том, что наследование является неправильной абстракцией для изменения определенного поведения класса / объекта (если используется сознательно). Изменение обычно молча нарушает LSP, так как поведение изменяется без изменения API. Отличный ответ, +1.
Призрак Мадары

Проповедовать! Приват является проклятием моего существования, когда я пытался писать моды для Minecraft. Ничего более раздражающего, чем использование Reflection или ASM, чтобы это исправить.
RecursiveExceptionException

Как я понял ответ Ника, он имел в виду в основном методы, а не свойства. Я полностью согласен с тем, что мы не должны каждый раз объявлять методы закрытыми, и их использование должно быть продуманным, но почему бы вам не посоветовать объявлять свойства, защищенные по частному, просто потому, что вы хотите легче "переписать" класс. Полностью поделитесь своим разочарованием, поскольку я работаю с 3-м днем, но давайте призываем людей создавать надежную архитектуру, а не просто говорить: «Код в конечном итоге окажется дерьмом, поэтому давайте защитимся с самого начала, чтобы мы могли легко переписать его». Хотя я разделяю ваше разочарование.
sean662

16

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

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

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

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

Отказ от ответственности: я не много программирую на Java :)


2
Чтобы уточнить: A final classв Java это класс, который не может быть унаследован от. A не final methodявляется виртуальным, то есть не может быть переопределено подклассом (методы Java по умолчанию являются виртуальными). A final field/parameter/variableявляется ссылкой на неизменяемую переменную (в том смысле, что ее нельзя изменить после инициализации).
М.Страмм

1
Как ни странно, я видел совершенно противоположный совет, что ничто не должно быть окончательным, если нет уверенности в том, что переопределение рассматриваемой вещи может сломать инвариант. Таким образом, каждый может свободно расширять свои классы по мере необходимости.
GordonM

Можете ли вы назвать один случай в вашей карьере программиста, когда пометка параметра метода "final" имела значение для вашего понимания? (кроме
случаев,

7

Когда бы вы использовали privateи когда вы бы использовали protected?

Частное наследование может рассматриваться как реализованное с точки зрения отношений, а не отношений IS-A . Проще говоря, внешний интерфейс наследующего класса не имеет (видимой) связи с наследуемым классом. Он используетprivate наследование только для реализации аналогичной функциональности, которую обеспечивает базовый класс.

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


2
Это «Реализовано с точки зрения отношений» является отношением HAS-A. Если все атрибуты являются частными, единственный способ получить к ним доступ через методы. Это то же самое, что если бы подкласс содержал объект суперкласса. Устранение всех защищенных атрибутов из ваших конкретных классов означает также устранение всех наследований от них.
Shawnhcorey

2
Интересный мыслительный процесс - частное наследование . @shawnhcorey благодарит за то, что он прямо указывает на то, что он указывает на связь HAS-A или ассоциацию (которая может быть агрегацией или композицией). Я чувствую, что этот пост также должен быть обновлен, чтобы прояснить то же самое.
RBT

1

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.