享元模式 vs 静态字段

9
在我看来,享元模式的目的是通过共享通用外部状态来减少内存占用并提高性能。为什么有人会选择实现这个模式而不是将共享状态存储在静态字段中呢?
考虑以下示例:http://www.oodesign.com/flyweight-pattern-wargame-example-java-sourcecode.html 如果我的理解是正确的,那么这个示例的重点是通过持有对单个SoldierImp对象的引用,在所有SoldierClient类实例之间共享常见状态(soldierGraphicalRepresentation对象)。
为什么要麻烦地实现这个设计?我更倾向于将SoldierClient类声明如下:
public class SoldierClient implements Soldier 
{
    protected static Object soldierGraphicalRepresentation;
    private int currentLocationX;
    private int currentLocationY;

    static SoldierImp()
    {
        soldierGraphicalRepresentation = LoadGraphicalRepresentation();
    }

    public void moveSoldier(int previousLocationX, int previousLocationY, int newLocationX, int newLocationY) {
        // do stuff with the graphical representation

    }
}

这样所有的SoilderClient实例都共享同一个soldierGraphicalRepresentation对象的引用,从而达到相同的目的。我错了吗?

1
因为你有几个共享状态?我不确定我理解这个问题。你可能需要提供示例代码来说明你的意思。 - JB Nizet
2个回答

12
模式的要点在于,您可以有200个“大红色”士兵共享同一个“大红色”图形表示,300个“小蓝色”士兵共享同一个“小蓝色”图形表示等等。如果将图形表示设置为静态,所有士兵都将是相同的。

3
那么每个士兵都将有一个对这个静态集合中图形表示之一的参考。假设列表大小为5,士兵数量为1,000,000,则将有5个图形表示实例,而不是1,000,000个。这就是模式的意义所在。 - JB Nizet
当然,这会节省内存。假设一个士兵由一个10 * 100像素的图像表示,每个像素4字节。这使得每个图形表示需要4,000字节。使用5个实例,您需要20,000字节(20 KB)。对于1,000,000个图形表示,您需要4,000,000,000字节(4 GB)。成本不是指对图形表示的引用(只需要4字节指针),而是指图形表示本身。 - JB Nizet
一个士兵如何知道它必须使用SolderClient.GraphicalRepresentationCollection["bigBlue"]而不是SolderClient.GraphicalRepresentationCollection["smallRed"]或者SolderClient.GraphicalRepresentationCollection["mediumGreen"]?答案是:通过存储对其中一个静态图形表示(在创建时传递)的引用。这就是享元模式:单个重型对象在共享公共状态的数十个对象之间共享。 - JB Nizet
1
在构造时使用如下代码:mySoldier = new SoilderClient(SolderClient.GraphicalRepresentationCollection["smallRed"]),或者在任何后续阶段使用mySolder.SetGraphicalRepresentation(SolderClient.GraphicalRepresentationCollection["smallRed"])? - Tamas Pataky
1
是的,就是这样。你刚刚使用了享元模式。如果不使用它,将会是 SoldierClient mySoldier = new SoldierClient(new SmallRedGraphicalRepresentation()); - JB Nizet
显示剩余4条评论

3
静态字段可以工作,无论你有多少红/绿/蓝的图形表示。静态字段唯一的问题在于它们违反了单一职责原则。如您所见,您的SoldierClient类有两个职责:

  1. 管理图形表示池
  2. 典型SoldierClient的工作

这种设计很难在另一个上下文中重用其中的任何一个职责。例如,池不能为需要共享图形表示的Monster对象重用;典型的SoldierClient不能在与池无关的另一个上下文中使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接