我用Java实现了一个不可变的系统.几乎每个班级都是不可改变的,并且它比我预期的要好得多.
我的问题是尝试发送事件.通常,您有一个事件源和一个事件监听器.源只是保存对侦听器的引用,并在事件发生时发送事件.
但是对于不可变,当您修改字段并创建新对象时,事件侦听器引用会发生更改.所以事件源发送到一些垃圾收集的旧引用.
因此,我的所有GUI类都是可变的,因为它们自然会使用很多事件.但我想找到一种处理事件的优雅方式,这样我就可以使这些事件变得不可变.
编辑:请求的示例代码:
public final class ImmutableButton {
public final String text;
public ImmutableButton(String text) {
this.text = text;
}
protected void onClick() {
// notify listeners somehow, hoping they haven't changed
}
}
public final class ImmutableWindow {
public final ImmutableButton button;
public ImmutableWindow(ImmutableButton button) {
this.button = button;
}
protected void listenForButtonClick() {
// somehow register with button and receive events, despite this object
// being entirely recreated whenever a field changes
}
}
Run Code Online (Sandbox Code Playgroud)
是的,您的设计中的冲突是您无法针对重建不可变数据模型时被替换的对象注册侦听器。我下面的解决方案是将侦听器从模型中全部删除。即使您的大多数对象都是不可变的,您仍然需要在基础上至少有 1 个可变变量来保存创建/重新创建的窗口。在这里,我使用内部类作为侦听器,您只需注册一次即可。然后,它们针对模型中的对象分派 doStuff() 调用,但它们会根据单个基本可变窗口引用进行查找。例如。window.getButton1().doStuff();
我并不认为这是一个很好的解决方案,但它是我可以根据您的要求提出的最简单、最干净的解决方案。侦听器不会变得无效,并且从 Window 开始的所有内容都可以是不可变的。
public final MutableBase {
private ImmutableWindow window; // single mutable variable
public MutableBase() {
Magic.registerClickListener(new WindowClickEvent());
Magic.registerClickListener(new Button1ClickEvent());
rebuildWindow();
}
public void rebuildWindow() {
// rebuild here when needed - change single mutable variable
this.window = new ImmutableWindow(new ImmutableButton("text"));
}
class WindowClickEvent implements ClickListener {
public void onClick() {
this.window.doStuff();
}
}
class Button1ClickEvent implements ClickListener {
public void onClick() {
this.window.getButton1().doStuff();
}
}
}
Run Code Online (Sandbox Code Playgroud)