4

我有一个(Java)类 WindowItem,它有一个问题:其中一种方法不是线程安全的。我无法修复 WindowItem,因为它是外部框架的一部分。所以我想我为它实现了一个装饰器,它在有问题的方法上有一个“同步”关键字。

装饰器扩展了 WindowItem 并且还将包含 WindowItem。按照装饰器模式,我在装饰器中创建调用它包含的 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 时,我首先将它包装在一个装饰器中:new WindowItemDecorator(item)——线程安全问题就消失了。但是,如果我的代码在 WindowItemDecorator 上调用 getwindowCount(),它将始终为零:它在超类而不是“item”成员上执行 getWindowCount()。

所以我想说 WindowItem 的设计(事实上它有公共的 final 方法)使得为这个类创建一个装饰器是不可能的。

这是正确的,还是我错过了什么?

在这种情况下,我可以在装饰器中保留窗口列表的副本,并使其保持同步,然后 getWindowCount() 的结果将是正确的。但在那种情况下,我更喜欢分叉和修补框架......

4

4 回答 4

2

不这样想问题怎么办?为什么不只处理代码中的线程问题,而不假设WindowItem.

// I personally prefer ReadWriteLocks, but this sounds like it will do...
synchronized (windowItem) {
    windowItem.getMethodWithProblem();
}

然后向包维护者提交 RFE,以更好地支持线程安全。

事实上,如果该类的设计不是线程安全的,那么几个synchronized关键字就不太可能真正解决问题。某人所说的“线程安全”总是相对的;-)

(顺便说一句,WindowItem绝对不是线程安全的,因为它正在使用List而不是显式使用“线程就绪”变体在java中同步ArrayList的正确方法- 也不能保证List以线程安全的方式访问)。

于 2012-08-02T10:00:13.750 回答
1

也许您可以使用委托模式WindowItem,如果该类实现了一个定义您关心的所有方法的接口,它会很好地工作。或者,如果引用这个委托类而不是WindowItem.

于 2012-08-02T09:53:43.843 回答
0

你的问题的答案是肯定的,你不能覆盖 final 方法,这意味着不可能为这个类创建一个装饰器。

如果您可以覆盖有问题的方法,并通过同步该方法来解决问题,则可以将其保留。也就是说,只使用您的子类,而不使用装饰器模式。

于 2012-08-02T09:44:26.763 回答
-1

一位同事提出了一个我认为可以解决问题的想法。通过查看修改列表窗口的所有方法,我可以使超类的状态和“项目”成员的状态保持同步。有几个:addWindow、removeWindow。我不仅在装饰器中调用“item.addWindow(...)”,而是在超类上调用 addWindow:

普通装饰器:

public void addWindow(WindowItem newItem) {
   item.addWindow(newItem);
}

在这种情况下,我这样做:

public void addWindow(WindowItem newItem) {
   super.addWindow(newItem);
   item.addWindow(newItem);
}

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

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

于 2012-08-02T09:55:02.353 回答