2012-08-02 89 views
4

我有一个(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()的结果将是正确的。但在这种情况下,我更喜欢叉和修补框架...

+0

你的第一个反射是正确的,但最终的方法使它无法覆盖它们。你必须传递/操作“WindowItem”类型的对象吗?如果不是,则不必使用装饰模式。您可以使用组合并通过将它们包装到不扩展WindowItem但通过组合使用实例的新类中来控制对WindowItem的最终方法的调用。 – Ushox 2012-08-02 10:08:02

回答

0

你的问题的答案是肯定的,你不能重写最终方法,这意味着它不可能为这个类创建一个装饰器。

如果您可以重写出现问题的方法,并通过同步方法来解决问题,则可以将其留在那个位置。也就是说,只需使用你的子类,而不要使用装饰器模式。

+0

谢谢。如果我创建WindowItem对象,这是一个很好的解决方案。然后,我创建了一个SyncedWindowItem,解决了问题,而不是创建一个WindowItem对象。不幸的是,WindowItem对象是为我创建的(它甚至不是具体的类,它是抽象的),所以我不能在已经存在的对象上这样做。 – Paul 2012-08-02 09:47:02

1

也许你可以使用Delegation Pattern,如果WindowItem类实现了一个定义你所关心的所有方法的接口,这将很好地工作。或者,如果它不打破太多的现有代码来引用这个委托类而不是WindowItem

+0

这个问题比这个更微妙 - “WindowItem”没有'interface',而是交给了'WindowItem's。 – fommil 2012-08-02 09:55:13

+0

我相信你所说的是为WindowItem的接口创建装饰器,而不是为类本身。我同意这一点,但在这种情况下,没有这样的接口。 – Paul 2012-08-02 09:56:44

+0

@Paul Swing和一个小姐。世界上没有足够的界面吗?! – 2012-08-02 10:04:40

-1

一位同事有一个想法,我认为可以解决问题。我可以通过查看修改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); 
} 

保持状态同步并且最终方法的返回值正确。

这是一个可以工作或不工作的解决方案,具体取决于正在装饰的类的内部。

+0

这不是一个通用的解决方案,但在这种情况下,我相信它是有效的,因为如果内部状态同步(在这种情况下!),我不需要重写任何最终方法。请注意,最终的方法是getWindowCount()。如果我确定超类在“item”处具有相同数量的窗口,则不需要重写getWindowCount()。 – Paul 2012-08-02 10:04:42

+0

所以基本上,你保留实例的两个副本 - 一个作为委托,另一个作为超级 - 这样一个方法返回正确的答案,另一个是所有其他方法的正确答案以及添加的线程安全逻辑?尽情享受吧! :-) – fommil 2012-08-02 10:10:31

+0

顺便说一下,如果有两个版本容易出现[竞争条件](http://en.wikipedia.org/wiki/Race_condition),你可能已经破坏了该类的线程安全性(假设它有任何) )。我相信任何超出你的控制范围的东西都没有提到原始的“WindowItem”? – fommil 2012-08-02 10:19:52

1

如何不以这种方式考虑问题?为什么不直接处理代码中的线程问题,而没有假设线程安全性为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)。

+1

...并要求他们在实现界面时也实现一个界面。它从来不会伤害(特别是在单元测试中嘲笑)。 – 2012-08-02 10:08:19

+0

如果'interface'太重,那么至少从这样一个简单的方法中删除'final'修饰符。或者,至少是一致的http://stackoverflow.com/questions/218744 – fommil 2012-08-02 10:14:02

+0

尽管使用装饰器解决这个问题的关键是我在一个地方解决了这个问题,而不是必须修复WindowItem的所有用法我的代码。 – Paul 2012-08-02 10:18:54