使用Guava ForwardingList装饰集合的意外行为

时间:2012-12-13 23:26:31

标签: java collections decorator guava generic-collections

我遇到了一个非常讨厌的我会说'副作用',但这显然是一个糟糕的设计问题。我正在使用Guava ForwardingList模式来装饰常规List。我的目的是建立一个大小限制列表,当满足maximumSize时,最旧的元素被踢出(简单的FIFO设计)。请注意,我不会代理或克隆我现有的集合。但我有这个非常讨厌的副作用:

List<String> originalList = new ArrayList<String>();
int maximumSize = 2;

originalList.add("foo");
originalList.add("bar");

System.out.println(originalList); // [foo, bar]

ListFactory<String> factory = ListFactory.getInstance(String.class);
List<String> decoratedList = factory.newTalendList(originalList, maximumSize);

decoratedList.add("beer");
System.out.println(originalList); // [bar, beer]

originalList.add("ben");
System.out.println(originalList); // [bar, beer, ben] <-- !!!
System.out.println(decoratedList); // [bar, beer, ben] <-- !!!

(注意:我的装饰类会覆盖add()以在添加新元素时删除列表的第一个元素。所有其他未覆盖的方法(包括toString())都被委托给原始List。

好吧,你可能会看到我是否使用原始的add()方法添加一个元素,我可以超过maximumsize ...好吧,我想这是不可避免的(毕竟设计并没有错)。但这对于decorList来说并非如此。

我找到的唯一解决方法是:

List<String> decoratedList = factory.newTalendList(new ArrayList<String>(originalList), maximumSize);

但它似乎不是最好的方式(我不确定它是否适用于所有环境):我不是装饰 originalList,而是她的匿名克隆!我想知道:也许我完全搞砸了我的设计?有没有更好的方法来构建它?

2 个答案:

答案 0 :(得分:9)

此模式的唯一方法是在创建装饰器后再次引用originalListForwardingList无法控制originalList中发生的事情。 (没有装饰者可以。)

你应该可能做的一件事就是创建一个工厂方法,它返回一个全新的装饰列表,永远不会让你访问原始列表。

答案 1 :(得分:1)

你尝试做的事非常奇怪。原始列表对被约束一无所知,也限制列表认为他只是使用原始列表。因此,不要以可能弄乱其装饰版本的方式使用原始列表。