一、享元模式
享元模式(Flyweight Pattern)是一种对象结构型模式,本质是缓存共享对象,降低内存消耗。
定义:运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。
使用频率:1星
模式结构:
享元模式的主要角色有如下:
1、抽象享元角色(Flyweight):是所有的具体享元类的基类,为具体享元规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法传入。
2、具体享元(Concrete Flyweight)角色:实现抽象享元角色中所规定的接口。
3、非享元(Unsharable Flyweight)角色:是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中。
4、享元工厂(Flyweight Factory)角色:负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。
享元模式结构图:
优点:
1、相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。
缺点:
1、为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性。
2、读取享元模式的外部状态会使得运行时间稍微变长。
应用场景:
享元模式其实是工厂方法模式的一个改进机制,享元模式同样要求创建一个或一组对象,并且就是通过工厂方法模式生成对象的,只不过享元模式为工厂方法模式增加了缓存这一功能。
1、系统中存在大量相同或相似的对象,这些对象耗费大量的内存资源。
2、大部分的对象可以按照内部状态进行分组,且可将不同部分外部化,这样每一个组只需保存一个内部状态。
3、由于享元模式需要额外维护一个保存享元的数据结构,所以应当在有足够多的享元实例时才值得使用享元模式。
二、享元模式实例之围棋棋子
1.实例说明
使用享元模式设计一个围棋软件,在系统中只存在一个白棋对象和一个黑棋对象,但是它们可以在棋盘的不同位置显示多次。要求使用简单工厂模式和单例模式实现享元工厂类的设计。
2.实例类图
3.实例代码
本例中,Coordinates充当外部状态类,IgoChessman充当抽象享元类,其子类BlackIgoChessman和WhiteChessman充当具体享元类,IgoChessmanFactory充当享元工厂类,它是一个单例类。
import java.util.*;
//坐标类:外部状态类
class Coordinates{
private int x;
private int y;
public Coordinates(int x,int y){
this.x=x;
this.y=y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
//围棋棋子类:抽象享元类
abstract class IgoChessman{
public abstract String getColor();
public void locate(Coordinates coord){
System.out.println("棋子颜色:"+this.getColor()+",棋子位置:"+coord.getX()+","+coord.getY());
}
}
//黑色棋子类:具体享元类
class BlackIgoChessman extends IgoChessman{
public String getColor(){
return "黑色";
}
}
//白色棋子类:具体享元类
class WhiteIgoChessman extends IgoChessman{
public String getColor(){
return "白色";
}
}
//围棋棋子工厂类:享元工厂类
class IgoChessmanFactory{
private static IgoChessmanFactory instance = new IgoChessmanFactory();
private static Hashtable hashtable;
private IgoChessmanFactory(){
hashtable = new Hashtable();
IgoChessman black,white;
black = new BlackIgoChessman();
hashtable.put("b", black);
white = new WhiteIgoChessman();
hashtable.put("w",white);
}
public static IgoChessmanFactory getInstance(){
return instance;
}
public static IgoChessman getIgoChessman(String color){
return (IgoChessman) hashtable.get(color);
}
}
客户端测试:
public class Client {
public static void main(String[] args) {
IgoChessman black1,black2,black3,white1,white2;
black1 = IgoChessmanFactory.getIgoChessman("b");
black2 = IgoChessmanFactory.getIgoChessman("b");
black3 = IgoChessmanFactory.getIgoChessman("b");
System.out.println("判断两颗黑棋是否相同:"+(black1==black2));
white1 = IgoChessmanFactory.getIgoChessman("w");
white2 = IgoChessmanFactory.getIgoChessman("w");
System.out.println("判断两颗白棋是否相同:"+(white1==white2));
black1.locate(new Coordinates(1,2));
black2.locate(new Coordinates(3,4));
black3.locate(new Coordinates(1,3));
white1.locate(new Coordinates(2,4));
white2.locate(new Coordinates(2,5));
}
}
运行结果:
判断两颗黑棋是否相同:true
判断两颗白棋是否相同:true
棋子颜色:黑色,棋子位置:1,2
棋子颜色:黑色,棋子位置:3,4
棋子颜色:黑色,棋子位置:1,3
棋子颜色:白色,棋子位置:2,4
棋子颜色:白色,棋子位置:2,5
更有趣的代码可以查看享元模式(详解版)中的享元应用实例。
本例中,享元类IgoChessman通过注入的方式来设置一个外部状态Coordinates,可以在相同的享元对象中注入不同的外部状态。在享元工厂中提供一个Hashtable类型的享元池,享元工厂使用单例模式设计,确保系统中只能有一个享元工厂对象,并且通过一个静态工厂方法getIgoChessman向客户端返回存储在享元池中的享元对象。
参考文献
【1】享元模式(详解版)
【2】设计模式实训教程(第2版) 刘伟 编著 清华大学出版社