大家好,欢迎来到编程队伍,我是作者王小伍,你可以叫我伍先生
这篇文章是设计模式系列文章的第十一篇:享元模式
设计模式系列前几篇没看的可以点击对应的文章快速查看
正文
我们还是老规矩,用一个具体案例开始我们的设计模式之旅
假如我们要做一个功能,可以生产各种各样的汽车
首先我们得有一个汽车类,类里面有几个成员变量,分别是:汽车型号、轮子、窗户、发动机
public class Car {
private String model; // 型号
private String wheel; // 轮子
private String windows; // 窗户
private String engine; // 发动机
public Car(String model, String wheel, String windows, String engine) {
this.model = model;
this.wheel = wheel;
this.windows = windows;
this.engine = engine;
}
}
接下来就可以生产汽车了
我们先生产一辆五菱宏光
Car car = new Car("五菱宏光", "轮胎", "窗户", "发动机");
当然,只有这一辆肯定是不够的。我们还要生产更多汽车,可能会有上千万辆
也就是说我们的汽车类Car
要被创建上千万次
那么问题来了,系统中有上千万个 Car
的实例,服务器的内存能抗住吗?
我们的例子中,为了方便大家理解,汽车类Car
成员变量只有4个,实际情况中可能会有很多个
成员变量越多,每个Car
的实例占用的内存就越多。上千万个Car
实例,肯定会消耗服务器很多内存。如果服务器内存不够就会导致内存溢出
我们需要减少内存的消耗
这时候我们就应该想到要用享元模式了,因为享元模式的目的就是将内存消耗最小化
假设同一型号的汽车,它们的轮胎、窗户、发动机都是一样的
那么,我们就可以让相同型号的汽车,去共享轮胎、窗户、发动机这些成员变量
在享元模式中,轮胎、窗户、发动机这些可以被共享的成员变量称为内在状态;型号这个不能被共享的成员变量称为外在状态
我们根据享元模式的设计思想,把汽车类Car
进行拆分
型号这个外在状态的成员变量保持不变;轮胎、窗户和发动机这3个内在状态拆分出去,作为BaseCar
方便共享

拆分完成以后,我们就要想办法把BaseCar
进行共享,也就是让它只有一个实例
说到这里你可能会想到单例模式,但它并不是单例的
我们之前说了,同一型号才能共享BaseCar
对于不同型号的汽车来说,BaseCar
是不同的实例,所以它不是单例的
下面我们用代码演示一下该怎么共享BaseCar
public class BaseCarFactory {
// map用来存储BaseCar,key是型号
private static Map<String, BaseCar> map = new HashMap<>();
// 获取BaseCar对象
public static BaseCar getBarseCar(String model, String wheel, String windows, String engine) {
// 如果同一型号的BaseCar在map中存在,则直接使用
if (map.containsKey(model)) {
System.out.println(model + "使用了缓存");
return map.get(model);
}
// 如果同一型号的BaseCar在map中不存在,则新生成一个并保存到map中
System.out.println(model + "没有使用缓存");
BaseCar baseCar = new BaseCar(wheel, windows, engine);
map.put(model, baseCar);
return baseCar;
}
}
接下来我们生产10辆五菱宏光试一下
for (int i = 0; i < 10; i++) {
String model = "五菱宏光";
BaseCar baseCar = BaseCarFactory.getBarseCar(model, "轮胎", "窗户", "发动机");
Car car = new Car(model, baseCar);
}
最后输出的结果如下

从运行结果可以看到,只有在第一次时没有使用缓存,后面都使用了缓存
对于五菱宏光这个汽车对象来说,不管要生产多少辆,它的BaseCar
的实例只有一个,这就在一定程度上减少了内存的消耗
这样我们就用一个简单的享元模式,完成了减少内存消耗的功能
基本介绍
享元模式是最常用的设计模式之一,在创建型模式、结构型模式和行为型模式分类中,享元模式归属于结构型模式
享元模式中,享就是共享,元表示一个细粒度的对象,享元模式就是共享细粒度的对象
细粒度对象一定是轻量级的,因此享元模式也被称为轻量级模式
享元模式只有一个目的: 将内存消耗最小化
享元模式中,分为内在状态和外在状态。
-
可以被共享的,不会发生变化的成员变量作为内在状态 -
每个对象独有的不能被共享的成员变量作为外在状态
享元模式的实现步骤总共分为两步:
-
对一个对象进行分析,抽象出对象的内在状态和外在状态 -
对内在状态进行缓存管理,如果内在状态已经被缓存则直接使用;如果内在状态不存在,则新创建一个并缓存
优点
可以极大减少内存中对象的数量,使得相同或相似对象在内存中只保存一份,从而可以降低系统的内存消耗
缺点
1、享元模式虽然节省了内存消耗,但是它会使系统的执行速度变慢
因为内在状态全局只有一份,所有外在状态要共享它。共享就势必会争抢,为了避免争抢就势必会加锁,加锁就会降低执行速度
2、代码结构会变得复杂,不容易理解
本来是一个对象,为什么要拆成内在状态和外在状态两个对象?对于不了解详情的研发人员肯定会有这样的疑问
适用场景
在程序中需要创建大量的相似对象,而又没有足够的内存容量时可以使用享元模式
与其他模式关系
-
与单例模式的关系
单例模式是全局只有一个实例;享元模式是特定情况下有一个实例
比如,上文的例子中。如果我们的系统只生产五菱宏光这一种车型,那么BaseCar
就可以是单例的
单例模式回顾:设计模式(一):单例模式
-
与工厂模式的关系
我们通常会使用工厂模式+享元模式来管理内在状态的创建
工厂模式回顾:设计模式(二):工厂模式
-
与外观模式的关系
享元模式是解决了系统中需要生成大量相似对象的问题
外观模式是解决一个对象代表多个子系统的问题
外观模式回顾:设计模式(十):外观模式
最后还是那句话,设计模式不是万能的,只有合理利用设计模式才能写出合理的代码
-- 以上内容来自公众号 编程队伍,转载请注明出处