github地址:https://github.com/1711680493
如需了解更多设计模式,请进入我的设计模式专栏
享元模式
运用共享技术来有效地支持大量细粒度对象的复用.
它通过共享已经存在的对象来大幅度减少需要创建的对象数量,避免大量相似类的开销,从而提高系统资源的利用率
优点
相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力
缺点
为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性.
读取享元模式的外部状态会使得运行时间稍微变长.
享元模式存在两种状态
内部状态:即不会随着环境的改变而改变可共享部分.
外部状态:指随环境改变而改变的不可以共享的部分.
享元模式的实现要领就是区分应用中的这两种状态,并将外部状态外部化
结构
1.抽象享元角色:是所有具体享元类的基类,为具体享元规范需要实现的公共接口,
非享元的外部状态以参数的形式通过方法传入
2.具体享元角色:实现抽象享元角色中所规定的接口
3.非享元角色:是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中
4.享元工厂角色:负责创建和管理享元角色,当客户对象请求一个享元对象时,
享元工厂检查系统中是否存在符合要求的享元对象,如果存在则提供给客户
如果不存在的话,则创建一个新的享元对象
例子
五子棋等网络游戏可以使用到享元模式,这里举的例子就以共享单车来举例,共享单车是由工厂制造的,并且不同种类的数量是已知的,有限的,共享单车有不同的颜色,这里使用享元模式来获取共享单车.
/**
* 享元模式
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
* @version 1.0
*/
public class Flyweight {
public static void main(String[] args) {
//创建人
Person p1 = new Person("张三");
Person p2 = new Person("李四");
//使用单车
p1.use(VehicleFactory.getBike("绿色"));
p1.use(VehicleFactory.getBike("黄色"));
p2.use(VehicleFactory.getBike("绿色"));
p2.use(VehicleFactory.getBike("黄色"));
p2.use(VehicleFactory.getBike("黄色"));
p1.use(VehicleFactory.getBike("绿色"));
}
}
/**
* 交通工具 对应抽象享元角色
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
* @version 1.0
*/
interface Vehicle {
/**
* 使用交通工具
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
* @param name 使用者的名称
* @param num 使用的次数
*/
void use(String name,int num);
}
/**
* 共享单车 属于交通工具 对应具体享元角色
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
* @version 1.0
*/
class Bike implements Vehicle {
private String color;
/**
* 在单车创建的时候就应该有颜色
*/
public Bike(String color) {
this.color = color;
}
@Override
public void use(String name,int num) {
System.out.println("共享单车被 "+ name + "第 " + num + " 次使用了,颜色是:" + color);
}
}
/**
* 人 非享元角色.
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
* @version 1.0
*/
class Person {
//每个人都有名字
private String name;
//这里记录使用单车的次数
private int num;
/**
* 初始化名字
* @param name 名称
*/
public Person(String name) {
this.name = name;
}
/**
* 使用交通工具
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
*/
public void use(Vehicle vehicle) {
vehicle.use(name,num);
//使用次数+1
num++;
}
}
/**
* 交通工具工厂 享元工厂角色
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
* @version 1.0
*/
class VehicleFactory {
//存现有的交通工具
private static final HashMap<String,Vehicle> vehicles = new HashMap<>();
/**
* 获取单车,根据单车颜色
* @author Shendi <a href='tencent://AddContact/?fromId=45&fromSubId=1&subcmd=all&uin=1711680493'>QQ</a>
* @param name
* @return
*/
public static Vehicle getBike(String color) {
//判断池子里有指定id的单车没,有直接返回,没有就创建
if (vehicles.containsKey(color)) {
System.out.println(color + "颜色的单车存在,给你返回");
return vehicles.get(color);
} else {
Vehicle v = new Bike(color);
//添加进池子里
vehicles.put(color, v);
System.out.println(color + "颜色的单车不存在,已成功创建");
return v;
}
}
}
应用场景
享元模式是通过减少内存中对象的数量来节省内存空间
系统中存在大量相同或相似的对象,这些对象耗费大量的内存资源
大部分的对象可以按照内部状态进行分组,且可将不同部分外部化,这样每一个组只需保存一个内部状态
由于享元模式需要额外维护一个保存享元的数据结构,所以应当在有足够多的享元实例时才值得使用享元模式
例子:线程池,数据库连接池,Java的字符串
扩展
单纯享元模式:这种享元模式中的所有的具体享元类都是可以共享的,不存在非共享的具体享元类
(单纯享元以上面的例子来说,就是不存在Person这个类)
复合享元模式:这种享元模式中的有些享元对象是由一些单纯享元对象组合而成的,他们就是符合向原对象,虽然复合享元对象本身不能共享,但它们可以分解成单纯享元对象在被共享
总结
享元模式就是将同一个对象(其中属性不同)存入管理,下次取如果有此对象则直接返回存的,没有则创建
(所以使用享元模式的场景需要大部分对象)