Подтвердить что ты не робот

Какая сделка с публичными полями 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;
    }
}

Совершенно во всех отношениях я могу думать. Кроме того, это нарушает каждое правило, которое я узнал. Согрешил ли я?

4b9b3361

Ответ 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(), но лучше всего сделать объект неизменным - таким образом вы не сможете изменить значения из-под установить/карту.