использование частного ключевого слова - PullRequest
20 голосов
/ 02 июня 2010

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

public class BadOO {
    public int size;

    public int weight;
    ...
}

public class ExploitBadOO {
    public static void main (String [] args) {
        BadOO b = new BadOO();
        b.size = -5; // Legal but bad!!
    }
}

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

Ответы [ 12 ]

22 голосов
/ 02 июня 2010

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

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

17 голосов
/ 02 июня 2010

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

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

Другой программист понимает, что размещение общедоступных методов получения и установки вокруг закрытой переменной-члена нарушает дух инкапсуляции, он видит бесполезность методов и решает просто сделать общедоступной переменную-член. Возможно, это немного повышает производительность, или, может быть, программист просто хочет «написать так, как он используется». Три месяца спустя ему нужно убедиться, что переменная никогда не «устанавливается» на ноль. Он просматривает каждый доступ к переменной, эффективно просматривая всю кодовую базу, включая весь код, который может получить доступ к переменной через отражение. Это включает в себя все сторонние библиотеки, которые расширили его код, и все вновь написанные модули, которые использовали его код после того, как он был написан. Затем он либо изменяет все вызовы, чтобы гарантировать, что переменная никогда не будет установлена ​​в нуль. Дело никогда не закрывается, потому что он не может эффективно найти все доступы к незащищенному участнику и не имеет доступа ко всему стороннему исходному коду. С несовершенным знанием недавно написанных модулей, опрос гарантированно будет неполным. Наконец, он не контролирует будущий код, который может получить доступ к общедоступному члену, и этот код может содержать строки, которые устанавливают переменную-член в значение null.

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

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

6 голосов
/ 02 июня 2010

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

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

Многие ранние классы Java имеют контракты, которые требуют, чтобы они были поточно-ориентированными.Это добавляет значительные издержки в случаях, когда только один поток может получить доступ к экземпляру.Более новые выпуски имеют новые классы, которые дублируют или улучшают функциональность, но отбрасывают синхронизацию.Следовательно, был добавлен StringBuilder, и в большинстве случаев его следует использовать вместо StringBuffer.

4 голосов
/ 02 июня 2010

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

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

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

Вместо этого, если у вас был метод set, вы могли бы расширить его до

void SetSize(int newSize)
{ 
   size = newSize;
   DoCalculation;
}

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

2 голосов
/ 02 июня 2010

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

Ваш вопрос рассматривается в пунктах 13 и 14 этой книги:

  • Пункт 13: Минимизировать доступность классов и членов
  • Элемент 14: В открытых классах используйте методы доступа, а не открытые поля
1 голос
/ 02 июня 2010

Это действительно используется, чтобы скрыть внутреннюю реализацию. Это также помогает обеспечить дополнительную логику для ваших переменных. Скажем, вам нужно убедиться, что значение, передаваемое для varable, не должно быть 0 / null, вы можете предоставить эту логику в методе set. Также таким же образом вы можете предоставить некоторую логику при получении значения, скажем, у вас есть переменная объекта, которая не инициализирована, и вы обращаетесь к этому объекту, в этом случае вы можете предоставить логику для проверки нуля для этого объекта и всегда возвращать объект.

1 голос
/ 02 июня 2010

Это обычное явление многих программистов - код Java с полями private и аксессорами и мутаторами public. Эффект, как вы говорите, эти поля могли бы быть public.

Есть языки программирования, которые говорят и за другую крайность. Посмотрите на Python; почти все публично, в некоторой степени.

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

  • Если поле используется исключительно в качестве атрибута, доступного как для чтения, так и для записи, сделайте его public.
  • Если поле используется только для внутреннего использования, используйте private. Укажите getter, если вам нужен доступ для чтения, и укажите setter, если вы хотите права на запись.
  • Существует особый случай: иногда вы хотите обработать дополнительные данные при обращении к атрибуту. В этом случае вы бы предоставили как методы получения, так и установки, но внутри этих функций свойств вы бы сделали больше, чем просто return - например, если вы хотите отслеживать количество раз, когда атрибут читается другими программами во время объекта. время жизни.

Это просто краткий обзор уровней доступа. Если вам интересно, читайте также о protected доступе.

1 голос
/ 02 июня 2010

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

0 голосов
/ 02 июня 2010

Как указывалось ранее, причина создания закрытой переменной заключается в том, чтобы скрыть ее снаружи. Но если вы сделаете геттер И сеттер, то вы можете также сделать саму переменную общедоступной. Если позже вы окажетесь в положении, в котором вы сделали неправильный выбор, то вы должны изменить свой код с использования общедоступной переменной на использование метода получения / установки, что может не составлять проблемы. Но это может быть проблемой, если другой код, который вы не контролируете, запускается в зависимости от вашего кода. Тогда такой рефакторинг сломает другой код. Если вы используете геттеры и сеттеры с самого начала, вы уменьшите этот риск в обмен на небольшие усилия. Так что это зависит от вашей ситуации.

0 голосов
/ 02 июня 2010

Хорошо. Мы говорим об объектах здесь. Объекты реального мира. Если они не являются частными, пользователь вашего класса может измениться. Что если для класса Circle и для атрибута / свойства radius класса Circle пользователь задает значение как «0». Круг не имеет смысла существовать с радиусом как «0». Вы можете избежать таких ошибок, если вы сделаете свои атрибуты частными и дадите метод установки, в котором и сгенерируете исключение / ошибку (инструктируя пользователя), что нельзя создавать круг с radisu как «0». По сути, объекты, созданные из вашего класса, должны существовать так, как вы хотели, чтобы они существовали. Это один из способов добиться этого.

...