最终方法的Java类装饰器
我有一个(Java)类,WindowItem,有一个问题:其中一个方法不是线程安全的。我无法修复WindowItem,因为它是外部框架的一部分。所以我想我为它实现了一个装饰器,它在所讨论的方法上有一个“同步”关键字。最终方法的Java类装饰器
装饰器扩展WindowItem并且还包含WindowItem。在Decorator模式之后,我在Decorator中创建了调用它所包含的WindowItem的方法。
但是,WindowItem有几个最终方法,我不能在装饰器中重写。这打破了装饰者的透明度。让我们更加明确:
public class WindowItem {
private List<WindowItem> windows;
public Properties getMethodWithProblem() {
...
}
public final int getwindowCount() {
return windows.size();
}
}
public class WindowItemDecorator extends WindowItem {
private WindowItem item;
public WindowItemDecorator(WindowItem item) {
this.item = item;
}
# Here I solve the problem by adding the synchronized keyword:
public synchronized Properties getgetMethodWithProblem() {
return super.getMethodWithProblem();
}
# Here I should override getWindowCount() but I can't because it's final
}
在我自己的代码,每当我有什么地方传递WindowItem,我在一家装饰包裹它首先:新WindowItemDecorator(项目) - 和线程安全问题就消失了。但是,如果我的代码在WindowItemDecorator上调用getwindowCount(),它将始终为零:它在超类上执行getWindowCount(),而不是“item”成员。
所以我会说WindowItem的设计(它具有公共final方法的事实)使得不可能为这个类创建一个装饰器。
这是正确的,还是我错过了什么?
在这种情况下,我可能会保留装饰器中窗口列表的副本,并保持同步,然后getWindowCount()的结果将是正确的。但在这种情况下,我更喜欢叉和修补框架...
你的问题的答案是肯定的,你不能重写最终方法,这意味着它不可能为这个类创建一个装饰器。
如果您可以重写出现问题的方法,并通过同步方法来解决问题,则可以将其留在那个位置。也就是说,只需使用你的子类,而不要使用装饰器模式。
谢谢。如果我创建WindowItem对象,这是一个很好的解决方案。然后,我创建了一个SyncedWindowItem,解决了问题,而不是创建一个WindowItem对象。不幸的是,WindowItem对象是为我创建的(它甚至不是具体的类,它是抽象的),所以我不能在已经存在的对象上这样做。 – Paul 2012-08-02 09:47:02
也许你可以使用Delegation Pattern,如果WindowItem
类实现了一个定义你所关心的所有方法的接口,这将很好地工作。或者,如果它不打破太多的现有代码来引用这个委托类而不是WindowItem
。
一位同事有一个想法,我认为可以解决问题。我可以通过查看修改List窗口的所有方法来保持超类的状态和“item”成员的状态同步。有几个:addWindow,removeWindow。而不是调用的只是 “item.addWindow(...)” 中的装饰,我叫addWindow的超类:
普通装饰:
public void addWindow(WindowItem newItem) {
item.addWindow(newItem);
}
在这种情况下,我做的事:
public void addWindow(WindowItem newItem) {
super.addWindow(newItem);
item.addWindow(newItem);
}
保持状态同步并且最终方法的返回值正确。
这是一个可以工作或不工作的解决方案,具体取决于正在装饰的类的内部。
这不是一个通用的解决方案,但在这种情况下,我相信它是有效的,因为如果内部状态同步(在这种情况下!),我不需要重写任何最终方法。请注意,最终的方法是getWindowCount()。如果我确定超类在“item”处具有相同数量的窗口,则不需要重写getWindowCount()。 – Paul 2012-08-02 10:04:42
所以基本上,你保留实例的两个副本 - 一个作为委托,另一个作为超级 - 这样一个方法返回正确的答案,另一个是所有其他方法的正确答案以及添加的线程安全逻辑?尽情享受吧! :-) – fommil 2012-08-02 10:10:31
顺便说一下,如果有两个版本容易出现[竞争条件](http://en.wikipedia.org/wiki/Race_condition),你可能已经破坏了该类的线程安全性(假设它有任何) )。我相信任何超出你的控制范围的东西都没有提到原始的“WindowItem”? – fommil 2012-08-02 10:19:52
如何不以这种方式考虑问题?为什么不直接处理代码中的线程问题,而没有假设线程安全性为WindowItem
。
// I personally prefer ReadWriteLocks, but this sounds like it will do...
synchronized (windowItem) {
windowItem.getMethodWithProblem();
}
然后与软件包维护人员一起提交RFE以更好地支持线程安全。
事实上,如果该类不是线程安全的,几个关键字不太可能真正解决问题。究竟是什么人的意思是“线程安全”永远是相对的;-)
(顺便说一句,WindowItem
绝对是不线程安全的,因为它是用List
而是明确使用的“螺纹准备”变种Correct way to synchronize ArrayList in java - 也有不保证以线程安全的方式访问List
)。
你的第一个反射是正确的,但最终的方法使它无法覆盖它们。你必须传递/操作“WindowItem”类型的对象吗?如果不是,则不必使用装饰模式。您可以使用组合并通过将它们包装到不扩展WindowItem但通过组合使用实例的新类中来控制对WindowItem的最终方法的调用。 – Ushox 2012-08-02 10:08:02