Java защищенные поля против общедоступных получателей - PullRequest
58 голосов
/ 17 февраля 2010

Что является лучшей практикой и почему: доступ к переменным базового класса через защищенное поле или открытый метод получения в закрытом поле.

(получатель будет публичным независимо)

Ответы [ 8 ]

60 голосов
/ 17 февраля 2010

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

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

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

16 голосов
/ 17 февраля 2010

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

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

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

Если вы используете установщики / получатели, вы всегда можете добавить проверку, проверку и т. Д. Позже. Вы также можете предоставить только метод получения переменной только для чтения.

10 голосов
/ 17 февраля 2010

Прямой доступ к полю не является предпочтительным. Используйте public или protected сеттеры и геттеры.

Получатель не обязательно должен быть public - если вы хотите скрыть данные от «посторонних», но передать данные подклассам, используйте protected

5 голосов
/ 11 декабря 2011

Effective Java 2nd Edition сообщает:

Элемент 13: Минимизируйте доступность классов и членов

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

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

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

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


Если вы знаете c ++, вы, вероятно, знаете, что

const int * someMember

отличается от

int * const someMember

Последний похож на последний массив в java.


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

5 голосов
/ 17 февраля 2010

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

1 голос
/ 29 января 2019

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

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

Рассмотрим следующий абстрактный пример с автомобилем: - у вас есть базовый класс автомобиль и производный класс Porshe . - Класс Car может иметь поле типа engine , значение которого не устанавливается в конструкторе Cars (возможно, тип двигателя известен только после инициализации объекта) - Вы создаете объект класса Porshe , который содержит некоторую логику, используемую для вычисления типа engine с использованием некоторых внешних данных.

В этом примере ожидается, что поле engine имеет публичный геттер, поэтому пользователи автомобиля знают, какой двигатель у машины. Тем не менее, нет никакого публичного сеттера, поскольку мы ожидаем, что водители автомобилей не настроятся на работу с двигателем! Вот почему совершенно нормально сделать engine защищенным полем, чтобы класс Porshe мог установить его значение в будущем.

Да, некоторые люди, вероятно, скажут: «Тогда используйте защищенный установщик!». И еще раз повторюсь: на языках ООП мы работаем с объектами, а не с классами. Принцип единой ответственности - да, но как объект, а не как класс. Если вы скажете: «в какой-то момент, если мы используем защищенные поля с 3 или 5 уровнями наследования, может быть сложно понять, что происходит с полем, если каждый класс выполняет с ним какую-то операцию». И тогда я отвечаю: это еще один антипаттерн - ваш объект, вероятно, слишком велик на данный момент и лишает принципа единственной ответственности.

1 голос
/ 25 июня 2014

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

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

Вот несколько ссылок, которые объясняют это немного глубже:

http://developer.android.com/training/articles/perf-tips.html#GettersSetters

http://blog.leocad.io/why-you-shouldnt-use-getters-and-setters-on-android/

Важно знать, чего вы пытаетесь достичь:

  1. Значение поля должно быть доступно клиентскому коду с использованием открытого интерфейса.
  2. Поле предназначено для использования подклассами.

В простой старой Java методы получения и установки выполняют обе задачи. Но Android отличается. Если вы делаете # 1, то вы должны использовать публичные методы получения и установки. Если вы делаете # 2, то вы должны использовать защищенные поля. Если вы делаете оба, используйте оба.

0 голосов
/ 17 февраля 2010

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

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