享元模式
享元模式(Flyweight Pattern),运用共享技术有效地支持大量细粒度的对象。
作用
- 极大减少内存中相似或相同对象数量,节约系统资源,提供系统性能。
- 提高效率,由于创建对象的数量减少,所以对系统内存的需求也减小,使得速度更快,效率更高。
- 享元模式中的外部状态相对独立,且不影响内部状态。
主要目的是实现对象的共享,即共享池,用于减少创建对象的数量,以减少内存占用和提高性能。 享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。
缺点
为了使对象可以共享,需要将享元对象的部分状态外部化,分离内部状态和外部状态,使程序逻辑复杂
细粒度/内部状态/外部状态
- 细粒度:较小的对象,所包含的内部状态较小
- 内部状态:不随环境改变而改变的状态,内部状态可以共享
- 外部状态:随着环境改变而改变的状态,不可以共享的状态,享元对象的外部状态通常由客户端保存,并在享元对象创建后,需要的时候传入享元对象内部,不同的外部状态是相互独立的。
适用场景
- 系统有大量相似对象,比如 JAVA 中的 String
- 需要缓冲池的场景,比如数据库的数据连接池
- 对象大部分状态都可以外部化
- 剥离出对象的外部状态后,可以使用相对较少的共享对象取代大量对象
- 常常应用于系统底层的开发,以便解决系统的性能问题
类图
- Flyweight(抽象享元类):通常是接口或抽象类,抽象享元类中声明了具体享元类公共方法,这些方法可以向外界提供享元对象的内部数据(内部状态),同时也可以通过这些方法来设置外部数据(外部状态)
- ConcreteFlyweight(具体享元类):继承抽象享元类,在具体享元类中为内部状态提供存储空间。通常可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象
- UnshareConcreteFlyweight(非分享具体享元类):并不是所有的具体享元类都需要被共享,不能被共享的子类可以设计为非共享具体享元类,当需要一个非共享具体享元类的对象时可以直接通过实例化创建,一般不会出现在享元工厂中
- FlyweightFactory(享元工厂类):创建并管理享元对象,将各种具体享元类存储到一个享元池中,享元池一般为“键值对”集合,可以结合工厂模式进行设计。当用户请求一个具体享元对象时,享元池中如果保存的有就直接返回给用户,如果没有就创建该享元对象返回给用户并存储到享元池中
代码表达
// Flyweight(抽象享元类)
public abstract class WebSite {
public abstract void use(User user);
}
// ConcreteFlyweight(具体享元类)
public class ConcreteWebSite extends WebSite {
// 共享的部分;属于内部状态
private String type = "";
public ConcreteWebSite(String type) {
this.type = type;
}
// User 不是共享的部分;属于外部状态
@Override
public void use(User user) {
System.out.println("ConcreteWebSite type:" + type + " by user:" + user.getName());
}
}
// UnshareConcreteFlyweight(非分享具体享元类)
public class User {
private String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
// FlyweightFactory(享元工厂类)
public class WebSiteFactory {
private HashMap<String, ConcreteWebSite> pool = new HashMap<>();
public WebSite getWebSite(String key) {
if (!pool.containsKey(key)) {
pool.put(key, new ConcreteWebSite(key));
}
return pool.get(key);
}
public int getWebSiteCount() {
return pool.size();
}
}
客户端调用
WebSiteFactory factory = new WebSiteFactory();
WebSite webSite1 = factory.getWebSite("weibo");
webSite1.use(new User("Tom"));
WebSite webSite2 = factory.getWebSite("weibo");
webSite2.use(new User("Bob"));
WebSite webSite3 = factory.getWebSite("weibo");
webSite3.use(new User("Andy"));
WebSite webSite4 = factory.getWebSite("weixin");
webSite4.use(new User("Jhon"));
System.out.println("getWebSiteCount:" + factory.getWebSiteCount());