背景
夏天降温,各家各户使用降温神器各不一样,有的用风扇,有的用冷风扇,有的用空调,牛逼的直接有中央空调(有钱,真有钱~),重点是要用,如果没有想用那装一个,下次直用就就OK了,而一般一个房间装一台就OK,不用重复装多台(你家里有钱除外),每次就直接使用,享元模式就是解决这种重复。
享元模式是什么?
定义:享元模式(Flyweight Pattern)属于结构型模式一种,主要用于大量减少创建对象的数量,以减少内存占用和提高性能。它提供了减少对象数量从而改善应用所需的对象结构的式,但是提高了系统的复杂性,需要享元模式分享出内部状态和外部状态,而且外部状态是固有特性,内部状态是随系统改变而改变,一般享元模式+工厂模式+单例模式一起使用。
内部状态:对象内部的信息,不会随着环境改变而改变,可以共享的状态,比如的id
外部状态:对象依赖的一个标记,随着环境改变而改变的,可以共享的状态,比如昵称
享元角色
Flyweight:抽象享元类,通过这个类传入外部状态并作用于外部状态;
ConcreteFlyweight:具体享元类,具体的享元实现对象,必须是可共享的,需要封装享元对象的内部状态;
UnsharedConcreteFlyweight:非分享具体享元类,非共享的享元实现对象,并不是所有的享元对象都可以共享,非共享的享元对象通常是享元对象的组合对象;
FlyweightFactory:享元工厂类,主要用来创建并管理共享的享元对象,并对外提供访问共享享元的接口;
享元模式可以干嘛?
理解为一个家(共享池),降温工具(电器),有就用,没有就添加(创建),然后放到家(共享池)里面,下次用到直接用就可以了。享元就是家里不用重复装空调,一般一个房间一个就够了,将家抽象为一个池子,而各种降温家电为抽象享元类,空调、风扇等具体对象为具体享元类,放置这些家电的地方,墙上、地板等(工厂),而这些家电,电器名称为(内部状态),电器档数(外部状态)。
优点:
减少系统资源开销,减少了频繁创建对象,可以节省内存中的对象数量;
缺点:
增加系统复杂度,因为需要将一些内部元素共享出来,所以导致逻辑更加复杂;
可能造成线程安全,单纯的享元模式是在高并发场景下存在线程安全的,所以在创建对象的时候最好享元+单例一起使用。
享元模式类图
实现代码
/**
* @Auther: csh
* @Date: 2020/6/1 17:33
* @Description:降温工具抽象(抽象享元类)
*/
public interface ICoolingTool {
//工具
void useTool();
}
/**
* @Auther: csh
* @Date: 2020/6/1 17:36
* @Description:家电(具体享元角色类)
*/
public class ElectricTool implements ICoolingTool {
//电器名称
private String name;
//档数
private int number;
public ElectricTool(String name) {
this.name = name;
}
@Override
public void useTool() {
System.out.println("启动了"+name+"开始降温!开了"+number+"档");
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}
/**
* @Auther: csh
* @Date: 2020/6/1 17:38
* @Description:电器工厂(享元工厂角色类)
*/
public class ElectricToolFactory {
private static final HashMap<String,ICoolingTool> pool = new HashMap <String, ICoolingTool>();
public static ICoolingTool getCoolingTool(String electricName){
ICoolingTool tool = pool.get(electricName);
//双重校验
synchronized (ElectricToolFactory.class){
if(null==tool){
synchronized (ElectricToolFactory.class){
//没有就添加家电
tool = new ElectricTool(electricName);
pool.put(electricName,tool);
System.out.println("家里新添了家电"+electricName);
}
}
}
return tool;
}
}
/**
* @Auther: csh
* @Date: 2020/6/1 17:50
* @Description:
* 该例,理解为一个家(共享池),降温工具(电器),有就用,没有就添加(创建),然后放到家(共享池)里面,下次用到直接用就可以了。享元就是家里不用重复装空调,一般一个房间一个就够了,总不可能一直加嘛。将家抽象为一个池子,而各种降温家电为抽象享元类,空调、风扇等具体对象为具体享元类,放置这些家电的地方,墙上、地板等(工厂),而这些家电,电器名称为(内部状态),电器档数(外部状态)。
* 演示
*/
public class Client {
private static final String[] homeElectricityTool = { "风扇", "冷风扇", "空调", "中央空调" };
public static void main(String[] args) {
for(int i=0;i<20;i++){
ElectricTool tool = (ElectricTool)ElectricToolFactory.getCoolingTool(getRandomElectrici());
tool.setNumber(getRandomNumber());
tool.useTool();
}
//String中也是使用了享元模式进行储存,内存同样引用同一个地址
String str1 = "123456";
String str2 = "123456";
System.out.println(str1==str2);
System.out.println(str1.hashCode());
System.out.println(str2.hashCode());
}
private static String getRandomElectrici() {
return homeElectricityTool[(int)(Math.random()*homeElectricityTool.length)];
}
private static int getRandomNumber() {
return (int)(Math.random()*6 );
}
}
结果
家里新添了家电冷风扇
启动了冷风扇开始降温!开了2档
家里新添了家电风扇
启动了风扇开始降温!开了3档
启动了风扇开始降温!开了1档
家里新添了家电中央空调
启动了中央空调开始降温!开了5档
启动了中央空调开始降温!开了3档
启动了风扇开始降温!开了5档
家里新添了家电空调
启动了空调开始降温!开了1档
启动了中央空调开始降温!开了3档
启动了中央空调开始降温!开了0档
启动了空调开始降温!开了1档
启动了中央空调开始降温!开了4档
启动了风扇开始降温!开了5档
启动了空调开始降温!开了2档
启动了风扇开始降温!开了4档
启动了空调开始降温!开了1档
启动了空调开始降温!开了3档
启动了空调开始降温!开了0档
启动了风扇开始降温!开了2档
启动了中央空调开始降温!开了4档
启动了空调开始降温!开了1档
true
1450575459
1450575459
享元模式,运用了共享的技术有效的支持大量 细粒度对象的复用,解决同一类型/对象重复创建,导致系统内存不足性能问题,而产生的一个设计模式,来减少对象的重复创建,可以实现多次复用,提高系统性能。当然也引发了系统的复杂度提高,业务逻辑更加复杂。享元模式的核心在于通过一个享元工厂,用户每次需要创建对象的时候,首先去享元工厂进行查找,若存在则直接返回给用户,若不存在则创建后放到享元池子中,以便下次使用。当然该模式中的池子,建议在日常开发过程中需要有一个统一的过期时间,或可以设计成多久没有就自动被GC回复,以防由于不断堆积导致内存溢出。当然也推荐使用类似于 redis,guava这种缓存工具来使用。
用到享元模式
String
包装类型(INTEGER、Long等)
Apache Commons Pool21