Структурировать как объекты в Java - PullRequest
193 голосов
/ 31 августа 2008

Это полностью противоречит способу Java создавать объекты, подобные структуре?

class SomeData1 {
    public int x;
    public int y;
}

Я вижу класс с аксессорами и мутаторами, более похожими на Java.

class SomeData2 {
    int getX();
    void setX(int x);

    int getY();
    void setY(int y);

    private int x;
    private int y;
}

Класс из первого примера удобен для обозначения.

// a function in a class
public int f(SomeData1 d) {
    return (3 * d.x) / d.y;
}

Это не так удобно.

// a function in a class
public int f(SomeData2 d) {
    return (3 * d.getX()) / d.getY();
}

Ответы [ 20 ]

281 голосов
/ 19 декабря 2011

Похоже, что многие Java-люди не знакомы с Sun Java Conoding Guidelines которые говорят, что вполне уместно использовать общедоступную переменную экземпляра, когда класс по сути, это «структура», если Java поддерживает «структуру» (при отсутствии поведения).

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

Соглашения Java Code от 1999 и до сих пор без изменений.

10.1 Предоставление доступа к переменным экземпляра и класса

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

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

http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-137265.html#177

http://en.wikipedia.org/wiki/Plain_old_data_structure

http://docs.oracle.com/javase/1.3/docs/guide/collections/designfaq.html#28

223 голосов
/ 31 августа 2008

Используйте здравый смысл на самом деле. Если у вас есть что-то вроде:

public class ScreenCoord2D{
    public int x;
    public int y;
}

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

С другой стороны, с:

public class BankAccount{
    public int balance;
}

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

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

63 голосов
/ 31 августа 2008

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

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

public property String foo;   
a->Foo = b->Foo;

Обновление: маловероятно, что поддержка свойств будет добавлена ​​в Java 7 или, возможно, когда-либо. Другие языки JVM, такие как Groovy, Scala и т. Д., Поддерживают эту функцию сейчас. - Алекс Миллер

50 голосов
/ 02 сентября 2008

Для решения проблем изменчивости вы можете объявить x и y как окончательные. Например:

class Data {
  public final int x;
  public final int y;
  public Data( int x, int y){
    this.x = x;
    this.y = y;
  }
}

При вызове кода, который попытается записать в эти поля, будет получена ошибка времени компиляции: «поле x объявлено окончательным; его нельзя назначить».

В этом случае код клиента может иметь удобство «короткой руки», которое вы описали в своем посте

public class DataTest {
    public DataTest() {
        Data data1 = new Data(1, 5);
        Data data2 = new Data(2, 4);
        System.out.println(f(data1));
        System.out.println(f(data2));
    }

    public int f(Data d) {
        return (3 * d.x) / d.y;
    }

    public static void main(String[] args) {
        DataTest dataTest = new DataTest();
    }
}
11 голосов
/ 25 сентября 2014

Не использовать public поля

Не используйте public поля, если вы действительно хотите обернуть внутреннее поведение класса. Возьмите java.io.BufferedReader, например. Имеет следующее поле:

private boolean skipLF = false; // If the next character is a line feed, skip it

skipLF читается и записывается всеми методами чтения. Что если внешний класс, работающий в отдельном потоке, злонамеренно изменил состояние skipLF в середине чтения? BufferedReader определенно сойдет с ума.

Использовать public поля

Возьмите этот Point класс, например:

class Point {
    private double x;
    private double y;

    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public double getX() {
        return this.x;
    }

    public double getY() {
        return this.y;
    }

    public void setX(double x) {
        this.x = x;
    }

    public void setY(double y) {
        this.y = y;
    }
}

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

Point a = new Point(5.0, 4.0);
Point b = new Point(4.0, 9.0);
double distance = Math.sqrt(Math.pow(b.getX() - a.getX(), 2) + Math.pow(b.getY() - a.getY(), 2));

Класс не имеет никакого поведения, кроме простых методов получения и установки. Допустимо использовать открытые поля, когда класс представляет просто структуру данных и не имеет, и никогда не будут иметь поведения (тонкие методы получения и установки не рассматривают поведение здесь). Лучше можно написать так:

class Point {
    public double x;
    public double y;

    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }
}

Point a = new Point(5.0, 4.0);
Point b = new Point(4.0, 9.0);
double distance = Math.sqrt(Math.pow(b.x - a.x, 2) + Math.pow(b.y - a.y, 2));

Clean!

Но помните: не только ваш класс должен отсутствовать в поведении, но он также должен иметь нет причин для поведения в будущем.

<ч />

(Это именно то, что описывает этот ответ . Цитируя "Условные обозначения кода для языка программирования Java: 10. Практика программирования" :

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

Таким образом, официальная документация также принимает эту практику.)

<ч />

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

public final double x;
public final double y;
8 голосов
/ 31 августа 2008

Re: аку, изб, Джон Топли ...

Остерегайтесь проблем с изменчивостью ...

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

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

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

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

См .: " Эффективная Java " Джошуа Блоха - Предмет № 13: Неизменность в пользу.

PS: Также имейте в виду, что все JVM в эти дни будут оптимизировать getMethod, если это возможно, в результате всего лишь одна инструкция чтения поля.

8 голосов
/ 31 августа 2008

Кстати, структура, которую вы даете в качестве примера, уже существует в библиотеке базовых классов Java как java.awt.Point. В качестве открытых полей указаны x и y, проверьте сами .

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

7 голосов
/ 01 декабря 2008

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

Как уже отмечалось выше, есть 2 проблемы, с которыми вы сталкиваетесь, и они на самом деле не решаемы:

  • Практически любой автоматизированный инструмент в мире Java основан на соглашении о методах получения / установки. То же самое касается, как отмечают другие, тегов jsp, конфигурации пружин, инструментов затмения и т. Д. И т. Д. Борьба с тем, что ожидают увидеть ваши инструменты, - это рецепт для долгих сессий, троллящих по Google, пытающихся найти этот нестандартный способ инициирования весенних бобов. На самом деле не стоит хлопот.
  • Если у вас есть элегантно закодированное приложение с сотнями открытых переменных, вы, вероятно, обнаружите хотя бы одну ситуацию, в которой они недостаточны - когда вам абсолютно необходима неизменность, или вам нужно вызвать какое-то событие, когда переменная будет установлена, или вы хотите вызвать исключение при изменении переменной, потому что оно устанавливает состояние объекта в нечто неприятное. Затем вы застряли с незавидным выбором между загромождением вашего кода каким-либо специальным методом везде, где есть прямая ссылка на переменную, имея специальную форму доступа для 3 из 1000 переменных в вашем приложении.

И это в лучшем случае сценарий работы полностью в отдельном частном проекте. Как только вы экспортируете все это в общедоступную библиотеку, эти проблемы станут еще больше.

Ява очень многословна, и это заманчиво. Не делай этого.

4 голосов
/ 31 августа 2008

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

Вы также не можете воспользоваться поддержкой классов, совместимых с именами JavaBean, что может повредить, если вы решите, скажем, использовать класс на странице JavaServer, написанной с использованием языка выражений.

Статья JavaWorld Почему методы Getter и Setter являются злыми Статья также может быть интересна для размышления о том, когда не следует реализовывать методы accessor и mutator.

Если вы пишете небольшое решение и хотите минимизировать объем используемого кода, способ Java может быть неправильным - я думаю, это всегда зависит от вас и проблемы, которую вы пытаетесь решить.

3 голосов
/ 15 апреля 2010

В этом типе кода нет ничего плохого, при условии, что автор знает , что они являются структурами (или шаттлами данных) вместо объектов. Многие Java-разработчики не могут различить правильно сформированный объект (не просто подкласс java.lang.Object, но объект true в определенном домене) и ананас. Поэтому они заканчивают тем, что пишут структуры, когда им нужны объекты и наоборот.

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