Как эффективно реализовать неизменяемые и доступные только для чтения версии изменяемых объектов? - PullRequest
2 голосов
/ 06 апреля 2020

Контекст:

  • данные пакета, publi c:
public interface _Data {
   public String getData();
}
public class _PackageAPI {
    DataHolder holder;

    public void createHolder(String data) {
        holder = new DataHolder();
        holder.setData(data);
    }

    public void mutateHolder(String data) {
        holder.setData(data);
    }

    public _Data getSnapshot() {
        return DataSnapshot.from(holder.getData());
    }

    public _Data getReader() {
        return holder.readOnly();
    }
}
  • данные пакета, приватный пакет:
class DataHolder {
    private String data;

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }

    public _Data readOnly() {
        return new _Data() {
            @Override
            public String getData() {
                return DataHolder.this.data;
            }
        };
    }
}
class DataSnapshot {
    public static _Data from(String data){
        return new _Data() {
            @Override
            public String getData() {
                return data;
            }
        };
    }
}
  • пример использования клиента:
package clientPackage;

import data._Data;
import data._PackageAPI;

public class ExampleRunner {

    public static void main(String[] args) {

        _PackageAPI handler;

        System.out.println("Creating...");

        handler = new _PackageAPI();
        handler.createHolder("INITIAL DATA");

        System.out.println("Done creating...");

        _Data initialSnapShot =  handler.getSnapshot();
        _Data initialReader = handler.getReader();

        System.out.println("initialSnapShot holds :" + initialSnapShot.getData() );
        System.out.println("initialSnapShot class :" + initialSnapShot.getClass() );
        System.out.println("initialReader class :" + initialReader.getClass() );

        System.out.println("initialReader holds :" + initialReader.getData() );


        System.out.println("Mutating...");

        handler.mutateHolder("MUTATED DATA");
        _Data subsequentSnapShot =  handler.getSnapshot();
        _Data subsequentReader = handler.getReader();

        System.out.println("Done mutating...");


        System.out.println("initialSnapShot holds :" + initialSnapShot.getData() );
        System.out.println("initialReader holds :" + initialReader.getData() );

        System.out.println("subsequentSnapShot holds :" + subsequentSnapShot.getData() );
        System.out.println("subsequentReader holds :" + subsequentReader.getData() );



    }
}
  • И вывод консоли:
Creating...
Done creating...
initialSnapShot holds :INITIAL DATA
initialSnapShot class :class data.DataSnapshot$1
initialReader class :class data.DataHolder$1
initialReader holds :INITIAL DATA
Mutating...
Done mutating...
initialSnapShot holds :INITIAL DATA
initialReader holds :MUTATED DATA
subsequentSnapShot holds :MUTATED DATA
subsequentReader holds :MUTATED DATA
  • ПЕРВЫЙ ВОПРОС : данный метод getSnapshot () возвращает _Data (класса: DataSnapshot $ 1), метод которого getData () возвращает "реальную" ссылку на данные, ie содержимое переменной данные объекта DataHolder, это безопасно или как-то возможно изменить DataHolder, используя доступ к этой ссылке? Если да, то как?

  • ПЕРВЫЙ ВОПРОС сокращен : возможно ли каким-либо образом изменить содержимое памяти, на которую ссылается ссылка, используя только ссылку?

(Конечно, решение этой проблемы заключается в клонировании указанной строки.)

  • ВТОРОЙ ВОПРОС : есть ли способ изменить экземпляр DataSnapshot $ 1 («неизменяемая» версия _Data)?

  • ТРЕТИЙ ВОПРОС : заданный DataHolder $ 1 (the " readOnly "версия _Data) содержит внутреннюю ссылку на экземпляр DataHolder, предоставляя его, безопасно ли выставлять такой DataHolder $ 1, или есть в любом случае связываться с экземпляром DataHolder из объекта DataHolder $ 1?

РЕДАКТИРОВАТЬ: Я бы поставил параноидный тег, если бы был один

1 Ответ

1 голос
/ 06 апреля 2020

это безопасно или как-то возможно изменить DataHolder, используя доступ к этой ссылке? Если да, то как?

Поскольку getData возвращает значение String, которое является неизменным, вызывающая сторона не может ничего изменить с помощью возвращенной ссылки. Вы также не можете получить доступ к DataHolder через String. Почему String знает о вашем DataHolder классе?

есть ли в любом случае мутировать DataSnapshot$1?

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

Вы можете спросить об этом, потому что видели, как изменился initialReader. Ну, поскольку DataHolder$1 является внутренним классом изменяемого DataHolder, он не является на самом деле неизменным, даже если у него нет методов-мутаторов.

безопасно ли выставлять такие DataHolder$1 или в любом случае нужно связываться с экземпляром DataHolder из объекта DataHolder$1?

Нет способа получить доступ к внешнему классу из внутреннего класса , так как в DataHolder$1 нет методов мутатора, вы не можете мутировать DataHolder извне, только с DataHolder$1.

Однако, если DataHolder изменится, изменения отразятся на DataHolder$1 (как показано в вашем примере кода), который, я думаю, побеждает цель неизменности.


Вот как я бы реализовал неизменность в этом сценарии.

Я бы сделал DataHolder агрегат _Data. DataHolder конечно, может это сделать, не так ли? В конце концов, у него есть метод String getData()! DataHolder будет иметь метод asReadOnly, который создает копию this и возвращает _Data. На самом деле, я бы переименовал _Data в ReadOnlyData.

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