2

だから私は自分のクラスのオブジェクトのArrayListを持っています:

public class A
{...}

ここで、リストからこれらのオブジェクトの1つを保持する変数があるとします。これで、そのオブジェクトを変更した場合(たとえば、そのオブジェクトにフィールドを設定した場合)、オブジェクト自体を保持するメモリが変更されるため、両方の参照(リスト内のオブジェクトを保持する変数と内部配列変数)が引き続き使用されることがわかります。同一であり、両方とも同じ、現在変更されているオブジェクトに表示されます。通常の動作!

しかし、変数が指すリスト内のオブジェクトを置き換えるときにもそうなるようにしたいと思います。それは私がするときです

myList.set(theObjectPosition, newInstanceOfA);

...この場合、変数にこの新しいインスタンスへの参照を自動的に持たせたいと思います。そのような振る舞いを実装する最も簡単な方法は何でしょうか?

これが私がこれを必要とする理由です:

要するに、私はArrayListに加えられた変更の取り消し/やり直しを処理するクラスのセットを持っています(これは拡張していませんが、とにかく問題を解決しませんか?)。クラスは呼ばれます

UndoAction, Add, Remove, Modify (the latter 3 inherit from UndoAction)

すべてのクラスには、メソッドundo()とredo()があります。

説明による例(必要に応じてコードは最後にあります):

リストにアイテムを追加したいとします。新しいAdd(myObject)を実行し、次にリスト内のアイテムを変更して、新しいModify(myObject)を実行します。その後、バックアップとしてオブジェクト状態のDeepCopyを作成します。 、その後、オブジェクト自体を変更します(新しいModify(..)の呼び出しを行った後)。次に、Modifyオブジェクトでundo()を実行して、list.set(somePos、previousDeepCopy)を実行します(したがって、この投稿では、作成されたディープコピーは、本質的に、物事を台無しにする新しいインスタンスです!!!)...

したがって、このlist.setがいくつかの問題を引き起こすことを想像できます。置き換えられたオブジェクトへの参照はすべて失われます。そのため、そのようなオブジェクトへの参照が常に置き換えられる場合、そのようなリストを効果的に操作することはできません。したがって、私の元に戻るマネージャーは、この方法で失敗する運命にあります。

では、これらの設計上の欠陥とどのように戦うのでしょうか?どんな提案でも大歓迎です。


いくつかのコード:

 protected abstract class UndoAction {
    protected HashSet<Integer> ids = new HashSet<Integer>();
    protected Marker marker;

    public UndoAction(List<E> l) {
      for (E e : l) {
        if (!entities.contains(e)) {
          entities.add(e);
          trace.append(entities.indexOf(e), new Stack<UndoAction>());
        }
        ids.add(entities.indexOf(e));
      }
    }

    public boolean sameAffectedTargets(UndoAction undoAction) {
      if (this.ids.containsAll(undoAction.ids) && undoAction.ids.containsAll(this.ids))
        return true;
      return false;
    }

    public Marker getMarker() {
      return new Marker(this);
    }

    public void firstRun() {
    }

    public void undo() {
    }

    public void redo() {
    }

    protected List<Data> getCopyEntities() {
      List<Data> l = new ArrayList<Data>();
      for (Integer id : ids) {
        E e = entities.get(id);
        int pos = adapterList.indexOf(e);
        l.add(new Data(id, pos, e));
      }
      return l;
    }

    protected List<Data> getDeepCopyEntities() {
      List<Data> l = new ArrayList<Data>();
      for (Integer id : ids) {
        E e = DeepCopy.copy(entities.get(id));
        int pos = adapterList.indexOf(entities.get(id));
        l.add(new Data(id, pos, e));
      }
      return l;
    }

    public void addEntities(List<Data> datas) {
      for (Data d : datas)
        d.addEntity();
    }

    public void setEntities(List<Data> datas) {
      for (Data d : datas)
        d.setEntity();
    }

    public void removeEntities(List<Data> datas) {
      for (Data d : datas)
        d.removeEntity();
    }

    protected class Data {
      public int id;
      public int pos;
      public E entity;

      public void addEntity() {
        entities.set(this.id, this.entity);
        adapterList.add(this.pos, this.entity);
      }

      public void setEntity() {
        entities.set(this.id, this.entity);
        E oldEntity = adapterList.get(this.pos);
        adapterList.set(this.pos, this.entity);
        notifyEntityReplaced(oldEntity, adapterList.get(this.pos), this.pos);
      }

      public void removeEntity() {
        entities.set(this.id, null);
        adapterList.remove(this.entity);
      }

      public Data(int id, int pos, E entity) {
        this.id = id;
        this.pos = pos;
        this.entity = entity;
      }
    }
  }

  protected class Add extends UndoAction {
    protected List<Data> addBackup;

    public Add(List<E> l) {
      super(l);
    }

    @Override
    public void undo() {
      super.undo();
      addBackup = getCopyEntities();
      removeEntities(addBackup);
    }

    @Override
    public void firstRun() {
      super.firstRun();
      adapterList.addAll(entities);
    }

    @Override
    public void redo() {
      super.redo();
      addEntities(addBackup);
    }
  }

  // call before modifying
  protected class Modify extends UndoAction {
    protected List<Data> beforeDeepCopies;
    protected List<Data> afterDeepCopies;

    public Modify(List<E> l) {
      super(l);
    }

    @Override
    public void undo() {
      super.undo();
      if (!skipModifying) {
        if (afterDeepCopies == null)
          afterDeepCopies = getDeepCopyEntities();
        setEntities(beforeDeepCopies);
      }
    }

    @Override
    public void firstRun() {
      super.firstRun();
      if (!skipModifying) // TODO
        beforeDeepCopies = getDeepCopyEntities();
    }

    @Override
    public void redo() {
      super.redo();
      if (!skipModifying)
        setEntities(afterDeepCopies);
    }
  }

  protected class Remove extends UndoAction {
    protected List<Data> removeBackup;

    public Remove(List<E> l) {
      super(l);
    }

    public List<E> getRemoved() {
      List<E> l = new ArrayList<E>();
      for (Data data : removeBackup)
        l.add(data.entity);
      return l;
    }

    @Override
    public void undo() {
      super.undo();
      addEntities(removeBackup);
    }

    @Override
    public void firstRun() {
      super.firstRun();
      removeBackup = getCopyEntities();
    }

    @Override
    public void redo() {
      super.redo();
      removeEntities(removeBackup);
    }
  }
4

4 に答える 4

2

Java()のオブジェクト参照ClassNameがCポインタ(ClassName*)のようなものである場合、pointers()へのCポインタのような動作が必要なようですClassName**

明らかな方法の1つは、クラスへの参照を保持することだけを目的としたホルダークラスを作成することです。このようにして、オブジェクト自体への参照を変更できます。すべてのオブジェクトがホルダーへの参照を保持している限り、それらは新しく置き換えられたオブジェクトも指すようになります。

public class AHolder{
    public A aRef; // or private with getter setter.
}
于 2013-01-30T09:28:26.780 に答える
1

間接参照の追加レイヤーが必要だと思います。他のコードに「A」を参照させるのではなく、おそらく「Stack <A>」を参照し、「peek()」を使用して現在の値を参照する必要があります(配列の値を変更する場合は、おそらく、新しい値を「push()」したいと思うでしょう)。

ただし、コードをクリーンにするために、「A」をインターフェイスにし、「A」と同じインターフェイスを提供する「A」の実装(「DelegatingStackAImpl」と呼びます)を作成することで、これを少しすっきりさせることができます。ただし、「push()」と「pop()」のサポートと、現在スタックの最上位にあるインスタンスへの呼び出しの転送は除きます。したがって、この余分な間接層をサポートしながら、「A」の既存の使用法を壊すことはありません。

于 2013-01-30T09:31:58.983 に答える
1

最も簡単な方法は、クラスが「可変」であること、つまり、新しいオブジェクトの1つで内部状態を変更できることを確認することです。

myOldObject.replaceValues(myNewObject);

オブジェクト自体が何らかの理由で変更可能にできない場合は、オブジェクトの周りにラッパーを作成するだけです。

于 2013-01-30T09:27:42.417 に答える
0

置換するオブジェクトがそのインターフェースを介してのみ参照される場合は、最初にJDK動的プロキシーを使用して、実際の実装に委任することができます。実際の実装を変更するには、プロキシ呼び出しハンドラーを変更する必要があります。

于 2013-01-30T09:45:27.697 に答える