概述
享元模式是对象池的一种实现,英文名称 Flyweight
代表着轻量级的意思.
享元模式使用共享物件来尽可能的减少内存使用量,适用于可能存在大量重复对象的时候,缓存可共享的对象,达到对象共享,避免创建过多对象的效果.享元模式中可共享的状态称为内部状态,不可共享的状态称为外部状态,内部状态不会跟随环境而变化,外部状态会随着环境而变化.在经典的享元模式中会存在一个map容器,键是享元对象的内部状态,值为享元对象本身.
定义
使用共享对象可有效地支持大量的细粒度的对象.在JAVA与模式
中是这样描述的,享元模式是对象的结构模式。享元模式以共享的方式高效地支持大量的细粒度对象。
使用场景
- 系统中存在大量的相似对象
- 细粒度的对象都具备较接近的外部状态,而且内部状态与环境无关,也就是说对象没有特定身份.
- 需要缓冲池的场景
UML
享元模式分为单纯享元模式和复合享元模式,在单纯的享元模式中,所有的享元对象都是可以共享的.抽象享元模式UML如下
其中涉及到的角色有:
Flyweight
(抽象享元角色): 享元对象抽象基类或者接口,规定出所有具有享元角色需要实现的方法.ConcreteFlyweight
(具体享元角色) : 具体的享元对象,实现了Flyweight
接口FlyweightFactory
(享元工厂角色) : 享元工厂,负责管理享元对象池和创建享元对象.本角色必须保证享元对象可以被系统适当地共享.
复合享元模式将一些单纯享元使用合成模式加以复合.形成复合享元对象,复合享元对象本身不能共享,但可以分解为单纯享元对象实现共享,复合享元对象的UML如下
其中涉及到的角色
Flyweight
(抽象享元角色) : 享元对象的抽象接口.ConcreteFlyweight
(具体享元角色) : 实现了Flyweight
接口的享元角色.ConcreteCompositeFlyweight
(复合享元角色) : 单纯享元对象的组合,不可共享的享元对象.FlyweightFactory
(享元工厂角色) : 创建和管理享元角色.
实例
如下以火车票售票系统为例模拟,当数以万计的人查询票价信息时,假设 出发地-终点地
,上铺,下铺,坐票
看做是享元对象进行缓存,减少服务器压力,如下是一个单纯享元模式示例
- 首先创建
Flyweight
public interface Ticket {
void showTicketInfo(String bunk);
}
- 创建
ConcreteFlyweight
对象,这里是火车票
public class TrainTicket implements Ticket {
public String from;//始发地
public String to;//目的地
public int price;//价钱
private static final String TAG = "TrainTicket";
public TrainTicket(String _from, String _to) {
from = _from;
to = _to;
}
@Override public void showTicketInfo(String bunk) {
price = new Random().nextInt(300);
Log.d(TAG, "showTicketInfo: -->"+ from + "--" + to + "--" + bunk + "--" + price);
}
}
- 创建
FlyweightFactory
享元工厂
public class TicketFactory {
static Map<String, Ticket> sTicketMap = new ConcurrentHashMap<>();
private static final String TAG = "TicketFactory";
public static Ticket getTicket(String from, String to) {
String key = from + "-" + to;
if (sTicketMap.containsKey(key)) {
Log.d(TAG, "getTicket: -->" + "使用缓存" + key);
return sTicketMap.get(key);
} else {
Log.d(TAG, "getTicket: -->" + "创建对象" + key);
Ticket ticket = new TrainTicket(from, to);
sTicketMap.put(key, ticket);
return ticket;
}
}
}
Client
调用代码如下
Ticket ticket01 = TicketFactory.getTicket("北京","广州");
ticket01.showTicketInfo("上铺");
Ticket ticket02 = TicketFactory.getTicket("北京","广州");
ticket02.showTicketInfo("下铺");
Ticket ticket03 = TicketFactory.getTicket("北京","广州");
ticket03.showTicketInfo("坐票");
会得到如下打印信息
D/TicketFactory: getTicket: –>创建对象北京-广州
D/TrainTicket: showTicketInfo: –>北京–广州–上铺–165
D/TicketFactory: getTicket: –>使用缓存北京-广州
D/TrainTicket: showTicketInfo: –>北京–广州–下铺–227
D/TicketFactory: getTicket: –>使用缓存北京-广州
D/TrainTicket: showTicketInfo: –>北京–广州–坐票–223
可以看到这里用向元模式避免了大量创建对象
享元模式的特点
优点:
- 大幅度的降低了内存中对象的数量
缺点:
因为其上的有点所付出的代价
- 使系统变得更加复杂,为了对象共享,将一些状态外部化,使得程序逻辑复杂化
- 因为状态的外部化,使得读取外部状态的导致运行时间变长.
参考:
《JAVA与模式》之享元模式