Java - изменить внутренние данные объекта или создать новый объект с измененными данными - PullRequest
0 голосов
/ 23 октября 2018

Вопрос

В Java, в соответствии со стандартным стилем кода и принципами проектирования, когда уместнее модифицировать внутренние данные объекта и когда уместнее создавать новый объект с использованием измененных данных?

Например, допустим, у меня есть некоторый класс Vector2D, который содержит компонент x и компонент yy, и я хочу повернуть этот вектор:

Подход A

public class Vector2D{
    private int x;
    private int y;
    public Vector2D(int x, int y){
        this.x = x;
        this.y = y;
    }
    public void rotate(double angle){
        //code for finding rotated vector
        x = rotated_x;
        y = rotated_y;
    }
}

Затем объект Vector2D можно вращать с vector1.rotate(angle)

Подход B

public class Vector2D{
    private final int x;
    private final int y;
    public Vector2D(int x, int y){
        this.x = x;
        this.y = y;
    }
    public Vector2D rotate(double angle){
        //code for finding rotated vector
        Vector2D rotated = new Vector2D(rotated_x, rotated_y);
        return(rotated);
    }
}

Затем объект Vector2D можно вращать с vector1 = vector1.rotate(angle)

Результаты такfar

Мое исследование этой проблемы до сих пор привело меня к убеждению, что подход B полезен для обеспечения возможности цепочки методов, например, vector1 = vector1.rotate(angle).scale(scalar), который имеет свои преимущества, больше с точки зрения читаемости вещей.

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

Когда уместно сделать объектНеизменен, особенно в этом случае?(и вообще для точек, векторов, многоугольников и т. д. Должны ли все эти типы быть неизменными?)

Редактировать:

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

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

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

Ответы [ 6 ]

0 голосов
/ 24 октября 2018

Рекомендуется по возможности использовать неизменяемые объекты, которые проще поддерживать и которые позволяют писать код более функциональным способом.

0 голосов
/ 23 октября 2018

В целом, вы должны предпочесть сделать объекты неизменяемыми.Причины, по которым вы можете выбрать изменяемые объекты:

  1. Объекты имеют много состояний и, следовательно, их создание дорого.Например, представьте себе массивную матрицу.Если вы не разрешите мутацию, вам придется каждый раз дублировать матрицу, что может быть слишком дорого (например, см. pandas - он предлагает оба варианта).
  2. Делая его неизменным, API становится совсем не интуитивным.Это также немного зависит от истории языка.Например, большинство библиотек сортировки, например, в Java, изменяют коллекцию, а не возвращают копию.Если вы собираетесь реализовать shuffle, может быть лучше изменить список, даже если вы не беспокоитесь о (1), просто потому, что было бы удивительно, если бы вы вернули копию.

Некоторые языки и библиотеки придумали третий способ, при котором мутация возвращает новую «версию» объекта при совместном использовании предыдущего состояния (см. this ).Это решает (1), что позволяет вам извлечь выгоду из неизменности без затрат на копирование.Некоторые утверждают, что это означает, что вы практически всегда должны выбирать неизменность, потому что с (1) больше нет никаких причин выбирать изменчивость, кроме фамильярности.В действительности, некоторые алгоритмы очень трудно выразить без изменяемых состояний (например, см. this ).так что мы, вероятно, продолжим иметь изменчивые состояния.

0 голосов
/ 23 октября 2018

В подходе A вы все равно можете вернуть изменяемый объект и использовать свободный API.Это не исключительно для второго подхода.

Что касается неизменности, есть две основные причины:

  1. Неизменяемые объекты проще, так как после их создания невозможно изменитьих состояние, поэтому нет необходимости делать проверки для методов-мутаторов.

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

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

0 голосов
/ 23 октября 2018

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

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

Таким образом, реальный вопрос скорее таков:

Когда уместно сделать объект неизменным?

Как вы хотите гарантировать несколько вещей для одного объекта?объект, такой как:

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

Классы дат Java 8(LocalDate и LocalTime) являются хорошими примерами соответствующего использования неизменяемости.

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

0 голосов
/ 23 октября 2018

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

Так что в общем случае umif у вас нет особых проблем, как над вамиможет работать с изменяемыми объектами, чтобы избежать дополнительной работы для себя, jvm и gc:)

0 голосов
/ 23 октября 2018

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

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

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

...