接下来,以一个实际例子来简单介绍下享元模式,假设有这么一个场景有两个用户服务类,分别是普通用户服务与VIP用户服务,其中有个Operation操作,而该操作主要是为了完成用户注册过程,在注册过程中,需要通过调用短信接口来完成注册流程;这时两个用户服务都需要一个HTTP连接池来完成此功能;如果每个类都单独创建一个HTTP连接池,那么系统将会产生两份资源开销,而其利用率将会很低;
为此,通过如下图例中的设计方式,将HTTP连接池的在用户服务工厂中进行创建,通过抽象工厂模式建立多种不同的用户服务子类实现,并将HTTP连接池在子类实例间进行共享,实现降低资源开销,提升连接池的资源利用率。
又称 FlyWeight,代表轻量级的意思,结构型设计模式。享元模式是对象池的一种实现。类似于线程池,线程池可以避免不停的创建和销毁多个对象,消耗性能。享元模式也是为了减少内存的使用,避免出现大量重复的创建销毁对象的场景。
享元模式用在一批相同或相似的对象上,这些对象有可以共享的内部状态和各自不同的外部状态。
享元模式中会有一个工厂,工厂维护着一个容器,容器以键值对的方式存储,键是对象的内部状态,也就是共享的部分,值就是对象本身。客户端从这个工厂获取对象,如果容器中存在这个对象就直接返回,不存在再创建新的对象并存入容器,避免了大量重复创建对象。
使用共享对象有效的支持大量的细粒度对象的复用。
系统中存在大量的 相似对象。
细粒度的对象都具备较接近的外部状态,且内部状态与环境无关,即对象没有特定身份。
需要 缓冲池 的场景。
例1. 过年回家买火车票,无数人在客户端上订票 (有多次购票、刷票的情况),即不断向服务端发送请求。
而每次查询,服务器必须做出回应,具体地,用户查询输入出发地和目的地,查询结构返回值只有一趟列车的车票。而数以万计的人有同样需求,即不间断请求数据,每次重新创建一个查询的车票结果,即造成大量重复对象创建、销毁,使得服务器压力加重。
享元模式正好适合解决该情形的问题,例如 A 到 B 地的车辆是有限的,车上铺位分硬卧、软卧和坐票三种,将这些可公用的对象缓存起来。用户查询时优先使用缓存,反之则重新创建。
我们知道 Java 中 String 是存在于常量池中,即一个 String 被定义之后它就被缓存到了常量池中,当其他地方使用同样的字符串,则直接使用缓存,而非创建。
享元模式的优缺点
优点 - 大幅度地降低内存中对象的数量。
缺点-1) 为了使对象可共享,需将一些状态外部化,使程序的逻辑复杂化
享元模式可以理解成一组共享的对象集合,下面就是一个简单的享元设计模式(甚至可以说是工厂模式了,呵呵,工厂模式目的是将具体创建对象的过程由工厂方法提供,用户只需要知道抽象类型即可。但这个例子就不要纠结成工厂模式了)private Map<String, Object>map = new HashMap<String, Object>
public Object get(String key) {
if(map.containsKey(key)) {
return map.get(key)
}
Object obj = new Object()
map.put(key, obj)
return obj
}
单例模式见下
public class S {
private static S s = new S()
private S() {
}
public static S getInstance() {
return s
}
}
从上面我们享元设计模式是一个类有很多对象,而单例是一个类仅一个对象;享元模式是为了节约内存空间,提升程序性能(避免大量的new操作);而单例模式则主要是出于共享状态的目的