所以我有一个我自己类的对象的 ArrayList:
public class A
{...}
现在说我有一个变量来保存列表中的这些对象之一。现在我知道如果我更改该对象(例如在该对象上设置一个字段),这会更改保存对象本身的内存,因此两个引用(保存列表中对象的变量和内部数组变量)仍然相同,并且两者仍然显示给相同的,现在修改的对象。正常行为!
但我也希望当我替换变量指向的列表中的对象时也是如此。那是我做的时候
myList.set(theObjectPosition, newInstanceOfA);
...在这种情况下,我还希望我的变量自动引用这个新实例。实现这种行为的最简单方法是什么?
这就是为什么我需要这个:
简而言之,我有一组类可以处理对 ArrayList 所做的更改的撤消/重做(我没有扩展,这无论如何都不能解决问题?)。这些类被称为
UndoAction, Add, Remove, Modify (the latter 3 inherit from UndoAction)
所有类都有方法 undo() 和 redo()。
描述示例(如果需要,代码在最后):
假设我想将一个项目添加到列表中:我执行 new Add(myObject),然后我想修改列表中的项目,因此我执行 new Modify(myObject),之后它创建对象状态的 DeepCopy 作为备份,然后我更改对象本身(在完成对 new Modify(..) 的调用之后)。然后我在 Modify 对象上执行 undo() 所以它确实 list.set(somePos, previousDeepCopy) (因此这篇文章,从本质上讲,deepcopy 是一个新的实例,它把事情搞砸了!!!)....
所以你可以想象这个 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);
}
}