Какая сделка с публичными полями Java?
Я читал две статьи (1) (2) на javaworld.com о том, как все поля класса должны быть приватными, а методы getter/setter - это также плохо. Объект должен воздействовать на данные, которые он имеет, а не разрешать доступ к нему.
В настоящее время я работаю над присвоением Университета Connect Four. При разработке программы агенты, играющие в Игру, нуждаются в доступе к государству Совета (чтобы они могли решить, что двигаться). Им также необходимо передать этот ход в игру, чтобы он мог проверить его как законный ход. И при принятии решения о том, что перемещать куски, группируются в Угрозы с начальным и конечным точками.
Объекты Board, Threat и Point на самом деле ничего не делают. Они находятся там, где хранятся связанные данные, доступ к которым можно получить с помощью человека.
В начале проектирования я представлял Points на доске как два элементаных массива int, однако это раздражало при создании точек или ссылок на их компоненты.
Итак, класс:
public class Point {
public int x;
public int y;
public Point(int x, int y){
this.x = x;
this.y = y;
}
}
Совершенно во всех отношениях я могу думать. Кроме того, это нарушает каждое правило, которое я узнал. Согрешил ли я?
Ответы
Ответ 1
Открытые поля отображают представление объекта своим вызывающим абонентам, т.е. если представление должно меняться, то и вызывающие.
Инкапсулируя представление, вы можете обеспечить, как взаимодействуют с ним вызывающие, и может изменить это представление без необходимости изменять вызывающих абонентов при условии, что public api не изменится. В любой нетривиальной программе инкапсуляция необходима для достижения разумной ремонтопригодности. Однако, в то время как вам нужны капсулы, их правильная зернистость может быть больше, чем один класс. Например, нет смысла инкапсулировать Iterator
из внутреннего представления Collection
, на котором он работает.
С учетом этого взгляните на свой пример:
public class Point {
public int x;
public int y;
public Point(int x, int y){
this.x = x;
this.y = y;
}
}
Внутреннее представление этого класса вряд ли изменится, поэтому скрытие структуры представления путем создания полей private не принесет никакой пользы. Тем не менее, я бы запретил вызывающим абонентам изменять Point
после его создания:
public class Point {
public final int x;
public final int y;
public Point(int x, int y){
this.x = x;
this.y = y;
}
}
так что класс, который на самом деле хочет инкапсулировать свое состояние, может вернуть свой Point
без утечки своего внутреннего представления и использовать данную точку в своем представлении без захват его. Это также хорошо сочетается с математическим понятием точки, которая не имеет идентичности или изменяющегося состояния.
При разработке программы агенты, играющие в Игру, нуждаются в доступе к Правлению (чтобы они могли решить, что двигаться). Им также необходимо передать этот ход в игру, чтобы он мог проверить его как законный ход. И при принятии решения о том, что перемещать куски, группируются в Угрозы с начальным и конечным точками.
Объекты Board, Threat и Point на самом деле ничего не делают. Они находятся там, где хранятся связанные данные, доступ к которым можно получить с помощью человека.
Теперь это звучит как потерянная возможность инкапсуляции: агентам действительно не разрешается произвольно изменять доску, но ограничиваться юридическими действиями. Почему ответственность класса Game
заключается в том, чтобы решить, что такое законный ход, когда обновляемое состояние находится в классе Board
? Если Board
должен был проверить сами ходы, ни один из вызывающих абонентов, и в частности агент не мог нарушать правила игры:
public class Board {
// private fields with state
// public methods to query state
public void perform(Move move) throws IllegalMoveException;
}
Ответ 2
Не всегда грех оставлять поле публичным, но вы строго ограничиваете себя тем, что вы связываете реализацию с классами, использующими его. Скажем позже, вы хотите добавить слушателя, который уведомляет вас в любое время, когда задано значение в X. У вас нет возможности сделать это без реорганизации всего. Если вы реализуете setX() и спрячете самое поле, вам нужно будет только изменить реализацию setX(), чтобы уведомить вас о том, что было сделано изменение без изменений классам, использующим этот метод.
Ответ 3
Если вы знаете, что интерфейс не изменится, вполне законно иметь переменную public. Проблема в том, что программы становятся более сложными, вам нужно будет изменить доступ к полю, но современные IDE упрощают рефакторинг, поэтому я бы сказал, продолжайте.
Ответ 4
Да и нет.
Нет, если: вы уверены в своей сфере применения и не нуждаетесь в каких-либо нормальных свойствах Java (getters/seters), то это, вероятно, не имеет большого значения.
да, если: некоторые из этих действий могут измениться, например, вам нужно абстрагировать вычисление, влияющее на x или y, не влияя на код вызова, или вы используете то, что ожидает геттеры и/или сеттеры.
В общем, проще всего следовать нормальному шаблону свойств Java - он устраняет один тип риска. И это одна из причин, по которой я хочу, чтобы Java имела реальные свойства.
Ответ 5
Это одно из преимуществ, которое имеет С# над Java. Вы можете объявить класс, в котором есть общедоступные поля, а затем изменить свой разум, используя скрытое поле с помощью getter и setter; но синтаксис вызывающих сторон тот же
public class Point
{
public int X;
}
становится
public class Point
{
int m_x;
public int X {get {return m_x;} set {m_x = value;}
}
но вызывающий всегда выполняет
Point p;
p.X = 12;
Ответ 6
Java-правила не являются абсолютными. В вашем случае, когда вы используете объекты только для хранения данных, а не для обеспечения критического поведения, вполне нормально оставлять поля общедоступными.
С другой стороны, для полей, которые вам не нужны или вы хотите предоставить пользователю, потому что они могут быть релевантными только во внутреннем контексте, вам следует пометить поля private и предоставить только получатели и/или сеттеры если вам действительно нужно/хотите.
Чем сложнее ваш объект, тем важнее обеспечить согласованное состояние между операциями, и, следовательно, более вероятно, что программист будет поддерживать инкапсулированное состояние объекта.
Ответ 7
Я не вижу проблемы с этим. Если грех, это, по крайней мере, не смертный. Векторы - другой подход, но из того, что я собрал, векторы в java требуют слишком больших затрат.
Ответ 8
Публичные поля нарушают правило инкапсуляции, то есть защиту данных. Да, они хранят данные, но с помощью переменной экземпляра PUBLIC можно получить доступ к любому классу в вашем рабочем пространстве.
Переменные экземпляра могут быть защищены, а также private. Ваши методы getter и setter используются так, чтобы вы могли изменять данные, которые хранятся в ваших переменных экземпляра класса.
Обычно ваши методы настройки имеют какую-то проверку, поэтому мы должны защищать переменные экземпляра коррумпированных данных (маркируя их частными или защищенными)
В приведенном выше примере у вас есть конструктор, который инициализирует ваш inst. переменная, но есть вероятность, что вы, как разработчик этого класса, имеете права и знаете, что нужно вставлять в данные, чтобы сохранить целостность вашего класса. Кто-то другой, работающий над вашим классом, может не знать об этом и может получить доступ к вашим переменным, нарушив инкапсуляцию и вашу программу в целом.
Рассмотрим x = -10; когда х может быть только от 0 до 100 (например).
Я бы рекомендовал придерживаться принципов инкапсуляции.
Надеюсь, это поможет!
Ответ 9
Иногда правила предполагают использование, которое вы, возможно, не рассматривали.
Что делать, если вы создаете набор или хэш-карту точек? Для того, чтобы работать так, как вы, вероятно, хотите, чтобы он работал, вы должны реализовать перегрузки equals() и hashcode(), но лучше всего сделать объект неизменным - таким образом вы не сможете изменить значения из-под установить/карту.