Структурировать объекты в Java

Это полностью против Java-способа создания объектов типа struct?

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();
}

Ответ 1

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

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

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

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

Ответ 2

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

Люди склонны думать, что геттеры и сеттеры являются способом 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

Ответ 3

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

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

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

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

public class BankAccount{
    public int balance;
}

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

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

Ответ 4

Чтобы решить проблемы с изменчивостью, вы можете объявить 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();
    }
}

Ответ 5

Не используйте 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));

Чистота!

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


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

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

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


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

public final double x;
public final double y;

Ответ 6

Re: aku, izb, John Topley...

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

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

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

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

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

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

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

Ответ 7

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

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

Ответ 8

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

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

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

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

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

Ответ 9

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

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

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

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

Ответ 10

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

Ответ 11

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

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

Кстати, по утверждению e-bartek, очень маловероятно, что IMO добавит поддержку свойства в Java 7.

Ответ 12

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

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

Ответ 13

Очень старый вопрос, но позвольте мне сделать еще один короткий вклад. В Java 8 представлены лямбда-выражения и ссылки на методы. Лямбда-выражения могут быть простыми рекомендациями метода, а не объявлять "истинное" тело. Но вы не можете "преобразовать" поле в ссылку метода. Таким образом,

stream.mapToInt(SomeData1::x)

не является законным, но

stream.mapToInt(SomeData2::getX)

есть.

Ответ 14

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

Ответ 15

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

Ответ 16

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

import java.io.*;

class NameList {
    String name;
    int age;
}

class StructNameAge {
    public static void main(String [] args) throws IOException {

        NameList nl[]=new NameList[5]; // Create new radix of the structure NameList into 'nl' object
        NameList temp=new NameList(); // Create a temporary object of the structure

        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));

        /* Enter data into each radix of 'nl' object */

        for(int i=0; i<5; i++) {
            nl[i]=new NameList(); // Assign the structure into each radix

            System.out.print("Name: ");
            nl[i].name=br.readLine();

            System.out.print("Age: ");
            nl[i].age=Integer.parseInt(br.readLine());

            System.out.println();
        }

        /* Perform the sort (Selection Sort Method) */

        for(int i=0; i<4; i++) {
            for(int j=i+1; j<5; j++) {
                if(nl[i].age>nl[j].age) {
                    temp=nl[i];
                    nl[i]=nl[j];
                    nl[j]=temp;
                }
            }
        }

        /* Print each radix stored in 'nl' object */

        for(int i=0; i<5; i++)
            System.out.println(nl[i].name+" ("+nl[i].age+")");
    }
}

Вышеприведенный код является свободным от ошибок и протестирован... Просто скопируйте и вставьте его в свою IDE и... Знаете, и что???:)

Ответ 17

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

Ответ 18

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

Ответ 19

Как и в большинстве случаев, существует общее правило, а затем есть особые обстоятельства. Если вы делаете закрытое, захваченное приложение, чтобы вы знали, как данный объект будет использоваться, вы можете использовать больше свободы для улучшения видимости и/или эффективности. Если вы разрабатываете класс, который будет использоваться публично другими, находящимися вне вашего контроля, то наклоняйтесь к модели getter/setter. Как и во всех вещах, просто используйте здравый смысл. Часто бывает нормально делать первый раунд с публикацией, а затем менять их позже на getter/seters.

Ответ 20

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

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

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

Является ли аспектный стиль чище или нет, является несколько качественным. Мне было бы легко видеть только переменные в классе и отдельно рассматривать логику. Фактически, raison d'etre для ориентированного на Apect программирования состоит в том, что многие проблемы являются сквозными, и их разделение в самом классе классов не является идеальным (запись в качестве примера - если вы хотите зарегистрировать все, то Java хочет, чтобы вы напишите целую кучу геттеров и сохраните их в синхронизации, но AspectJ позволяет вам использовать один лайнер).

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

Аннотации кажутся похожими на аспектно-ориентированное программирование с первого взгляда, однако они требуют, чтобы вы исчерпывающе перечисляли pointcuts, добавляя аннотации, в отличие от краткой спецификации pointcut wild-card-like в AspectJ.

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