设计模式——享元模式

一、定义与结构

享元模式(Flyweight Pattern),通过共享技术来实现大量细粒度的对象的复用。在系统中只需要持有少量的对象就可以实现对象的多次复用。享元模式有两种状态,分别是内部状态和外部状态,内部状态在享元对象内部,是可以共享的不变的状态,而外部状态是不可共享的,由具体的应用场景来传入相应的外部状态,享元模式通常和工厂模式一起使用。

享元模式主要有如下的几个模块:

Flyweight:抽象享元对象,为具体的享元对象定义了接口方法,外部对象通过该接口以参数的形式传入,抽象享元对象可以是接口或者抽象类。

FlyweightFactory:享元工厂,主要负责创建和管理享元角色,并提供获取享元对象的接口方法。

ConcreteFlyweight:具体的享元对象,是抽象享元对象的具体实现。

UnsharedConcreteFlyweight:非共享具体享元对象,并非所有享元对象都是可共享的,可以通过非共享享元类来创建非共享享元对象。

设计模式——享元模式_第1张图片

二、享元模式实例

享元模式在实际工程中应用非常广泛,比如我们经常用到的线程池、jdbc连接池等。下面我们以围棋游戏为例,来展示一下享元模式在围棋游戏中如何减少对象的数量,从而节省计算机的内存和计算资源。

在围棋游戏中,白棋180枚,黑棋181枚,总计361枚棋子,如果我们为每个棋子都创建一个对象,那一局围棋游戏我们最多需要创建361个棋子对象,这仅仅是单机游戏需要的资源,如果是网络对战模式,会同时存在成千上万局对弈,这种时候,如果还是为每个棋子都单独创建对象,对象占据的资源将会挤爆服务器。而如果我们对棋子的属性进行分析的话,可以发现棋子之间其实是相似的对象,比如,所有的棋子都可以被加到棋盘上,同时任何棋子如果被围杀都会被移除出棋盘,这些都属于棋子的公共部分,棋子之间不同的属性主要有两个,一个是棋子的归属,是属于白方还是黑方,另一个是棋子在棋盘中的位置。这种情况下,我们只需要白方和黑方分别创建一个棋子对象即可,所有的白棋共用一个白棋对象,黑棋同理,这样仅仅通过两个棋子对象就可以持有所有棋子的状态,大大节省了计算机的资源。

下面我们来看一下样例的实现,首先我们需要定义棋子接口(抽象享元接口):

public interface Go {

    void addLocation(String location);

    void removeLocation(String location);

    void printLocations();
}

在棋子接口中我们定义了棋子的一些公共方法,比如新增一个棋子的位置(某方玩家下子),移除一个棋子的位置(对方玩家吃子),打印棋子当前状态。

棋子实现(具体享元类):

public class ConcreteGo implements Go{

    private String color;

    private Set locations = new HashSet<>();

    public ConcreteGo(String color) {
        this.color = color;
    }

    @Override
    public void addLocation(String location) {
        locations.add(location);
    }

    @Override
    public void removeLocation(String location) {
        locations.remove(location);
    }

    public void printLocations(){
        System.out.println(color + "方棋子布局如下:");
        for(String location : locations){
            System.out.println("<" + location + ">");
        }
    }
}

工厂类(享元工厂):

public class GoFactory {

    HashMap goMap = new HashMap<>();

    public Go createChessman(String color){
        Go chessman = goMap.get(color);
        if(chessman != null){
            return chessman;
        }
        chessman = new ConcreteGo(color);
        goMap.put(color,chessman);
        return chessman;
    }

}

然后我们根据上面的工厂类创建黑色棋子对象和白色棋子对象,并进行对弈,最后将对弈中的棋子情况打印出来:

public class GoMain {

    public static void main(String[] args){
        GoFactory goFactory = new GoFactory();
        Go whiteChessman = goFactory.createChessman("白");
        Go blackChessman = goFactory.createChessman("黑");
        whiteChessman.addLocation("5,F");
        blackChessman.addLocation("6,D");
        whiteChessman.addLocation("12,B");
        whiteChessman.printLocations();
        blackChessman.printLocations();
    }
}

打印结果如下:

白方棋子布局如下:
<12,B>
<5,F>
黑方棋子布局如下:
<6,D>

三、总结

1.享元模式的优点

(1)可以极大地减少内存中的对象,使得相似或者相同的对象在实际使用中指向的是同一个对象,节省大量的内存资源

(2)由于外部状态的存在,使得享元对象在不影响内部状态的情况下在不同环境中被共享

2.享元模式的缺点

享元模式使得系统更加复杂,在对需求进行分析的时候需要从逻辑上分离内部状态和外部状态

3.享元模式的应用场景

在系统中存在大量相同或者相似的对象,且这些对象可以重复利用

参考

https://blog.csdn.net/justloveyou_/article/details/55045638

https://www.cnblogs.com/Bobby0322/p/4193941.html

https://blog.csdn.net/qq_40709468/article/details/82633928

你可能感兴趣的:(设计模式)