这是我参与更文挑战的第29天,活动详情查看:更文挑战
什么是享元模式(Flyweight)
概念
享元模式(Flyweight Pattern)属于结构型模式,主要是用于减少对象的创建和销毁的性能消耗,以减少内存的占用。面向对象的语言能够给我们的开发带来一定的灵活性和扩展性,而且更加符合人的思维方式,但这会让我们的系统创建很多对象,这会让我们系统的性能下降,所以可以使用一个缓冲池来把这些反复使用的对象存储起来,减少反复创建和销毁的次数,享元模式的思想就是复用对象。
我们的生活中也有非常多的享元模式的例子,例如,围棋的棋子,共享单车,还有游泳池的游泳圈,这些东西都是被反复使用的。在开发中,我们常见的就是线程池、String常量池、数据库连接池,这些都是为了避免创建很多的对象而建立的一个“池子”。
优点
降低内存,提高性能。我们想要使用一个对象只需要去“池子”里拿就行了,不需要再去创建,节省了不断创建和销毁的系统开销。
缺点
增加程序复杂度。需要把对象的粒度细化,把不能共享的部分外部化才能够实现共享,这增加编码的复杂度和代码的理解难度。
原则
“+”代表遵守,“-”代表不遵守或者不相关
原则 | 开放封闭 | 单一职责 | 迪米特 | 里氏替换 | 依赖倒置 | 接口隔离 | 合成复用 |
---|---|---|---|---|---|---|---|
+ | + | – | – | + | – | + | |
适用场景
- 需要大量相同的对象或是一类对象中有很多相同的部分,并且创建或销毁这些对象会带来较大的性能开销。就可以把这些对象抽取出来一个不变的对象,然后存在池子里,需要适用的时候直接拿出来用。
- 系统不依赖这些对象的状态,只是拿来用一下。
- 一个对象可以分为内外两部分,内部分可以反复使用,外部分由使用者自行定义。
如何实现
想要实现享元模式,需要以下五样东西:
- 享元类接口(Flyweight):定义所有享元类要实现的规范。
- 具体享元类(Concrete Flyweight):实现享元类接口。
- 非享元类接口(Unsharable Flyweight):定义非享元类要实现的规范。
- 具体非享元类(Concrete Unsharable Flyweight):实现非享元类接口。
- 享元工厂类(Flyweight Factory):负责创建和管理享元类对象。
上类图
有那么一点点滴复杂,可以结合下面的代码来理解。
上代码
惠州是一个旅游胜地(我为惠州代言),红花湖是一个非常适合骑单车游玩的地方,现在有一家车店,专门在红花湖出租单车,供来玩的游客骑着环绕红花湖,车店(FlyweightFactory)专门提供单车,不够单车了就会再去买新的单车,游客(ConcreteUnsharableFlyweight)呢骑完单车后就会归还给车店。
/**
* 享元模式测试
* Created on 2021/6/3.
*
* @author xuxiaobai
*/
public class FlyweightTest {
public static void main(String[] args) {
FlyweightFactory factory = new FlyweightFactory(4);
//模拟十个人来接车
for (int i = 0; i < 20; i++) {
int finalI = i;
new Thread(()->{
Flyweight flyweight = factory.getFlyweight();
flyweight.operate(new ConcreteUnsharableFlyweight("小明"+ finalI +"号"));
try {
//愉快地骑单车中
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//骑完归还
flyweight.release();
}).start();
}
try {
//休眠五秒钟,让线程跑完
Thread.sleep(20000);
} catch (InterruptedException e) {
}
System.out.println("工厂的单车数量:"+factory.getFlyweightSize());
/**
* 结果:
* 当前id:1已占用
* 开锁成功!
* 当前id:2已占用
* 开锁成功!
* 当前单车id:1 当前单车id:2 小明1号骑单车
* 小明0号骑单车
* 当前id:3已占用
* 开锁成功!
* 当前单车id:3 小明2号骑单车
* 暂无空闲的单车,请稍等
* 暂无空闲的单车,请稍等
* 当前id:3已占用
* 开锁成功!
* 当前单车id:3 小明4号骑单车
* 暂无空闲的单车,请稍等
* 当前单车id:3 小明5号骑单车
* 暂无空闲的单车,请稍等
* 当前id:4已占用
* 开锁成功!
* 当前单车id:4 小明19号骑单车
* 暂无空闲的单车,请稍等
* 当前单车id:4 小明3号骑单车
* 暂无空闲的单车,请稍等
* 当前id:5已占用
* 开锁成功!
* 当前单车id:5 小明17号骑单车
* 暂无空闲的单车,请稍等
* 当前单车id:5 小明18号骑单车
* 当前id:1已归还
* 当前id:3已归还
* 当前id:2已归还
* 当前id:1已占用
* 开锁成功!
* 当前单车id:1 小明15号骑单车
* 当前id:2已占用
* 开锁成功!
* 当前单车id:2 小明16号骑单车
* 当前id:3已占用
* 开锁成功!
* 当前单车id:3 小明14号骑单车
* 暂无空闲的单车,请稍等
* 当前id:3已归还
* 当前id:3已归还
* 当前id:3已占用
* 开锁成功!
* 暂无空闲的单车,请稍等
* 当前单车id:3 小明13号骑单车
* 当前id:4已归还
* 当前id:4已归还
* 当前id:4已占用
* 开锁成功!
* 当前单车id:4 小明12号骑单车
* 暂无空闲的单车,请稍等
* 当前id:5已归还
* 当前id:5已归还
* 当前id:5已占用
* 开锁成功!
* 暂无空闲的单车,请稍等
* 当前单车id:5 小明11号骑单车
* 当前id:1已归还
* 当前id:3已归还
* 当前id:2已归还
* 当前id:1已占用
* 开锁成功!
* 当前id:2已占用
* 开锁成功!
* 当前单车id:2 小明9号骑单车
* 当前id:3已占用
* 开锁成功!
* 当前单车id:3 小明8号骑单车
* 暂无空闲的单车,请稍等
* 当前单车id:1 小明10号骑单车
* 当前id:3已归还
* 当前id:3已占用
* 开锁成功!
* 暂无空闲的单车,请稍等
* 当前单车id:3 小明7号骑单车
* 当前id:4已归还
* 当前id:4已占用
* 开锁成功!
* 当前单车id:4 小明6号骑单车
* 当前id:5已归还
* 当前id:3已归还
* 当前id:1已归还
* 当前id:2已归还
* 当前id:3已归还
* 当前id:4已归还
* 工厂的单车数量:6
*/
}
}
/**
* 享元工厂类
* 模拟车店
*/
class FlyweightFactory {
private List<Flyweight> flyweights = new ArrayList();
private int flyweightSize=0;
public FlyweightFactory(int num) {
for (int i = 1; i < num; i++) {
flyweights.add(new ConcreteFlyweight(i));
flyweightSize++;
}
}
/**
* 每次只有一个人能来拿单车
* @return
*/
public synchronized Flyweight getFlyweight() {
Flyweight flyweight1 = getOneFlyweight();
if (flyweight1 != null) return flyweight1;
try {
//模拟等待
Thread.sleep(500);
} catch (InterruptedException e) {
}
//再看看有没有回来的车
flyweight1 = getOneFlyweight();
if (flyweight1 != null) return flyweight1;
//实在没等到,买个新车
return newFlyweight();
}
/**
* 去买新单车
* @return
*/
private synchronized Flyweight newFlyweight(){
ConcreteFlyweight flyweight = new ConcreteFlyweight(this.flyweightSize);
flyweightSize++;
flyweights.add(flyweight);
return flyweight;
}
/**
* 车店内部找车
* @return
*/
private Flyweight getOneFlyweight() {
Iterator<Flyweight> iterator = flyweights.iterator();
while (iterator.hasNext()){
Flyweight flyweight = iterator.next();
if (flyweight.occupy()) {
System.out.println("开锁成功!");
return flyweight;
}
}
System.out.println("暂无空闲的单车,请稍等");
return null;
}
public int getFlyweightSize(){
return flyweightSize;
}
}
/**
* 具体享元类
* 模拟单车类
*/
class ConcreteFlyweight implements Flyweight {
private int id;
UnsharableFlyweight unsharableFlyweight;
//0是闲置,1是被占用了
private volatile int state = 0;
public ConcreteFlyweight(int id) {
this.id = id;
}
/**
* 骑单车
* @param unsharableFlyweight
*/
@Override
public synchronized void operate(UnsharableFlyweight unsharableFlyweight) {
System.out.print("当前单车id:"+id+" ");
unsharableFlyweight.operate();
}
/**
* 占用
*
* @return true:占用成功,false占用失败
*/
@Override
public synchronized boolean occupy() {
if (state == 0) {
state = 1;
System.out.println("当前id:" + id +"已占用");
return true;
}
return false;
}
/**
* 释放/归还
*/
@Override
public synchronized void release() {
System.out.println("当前id:" + id +"已归还");
this.state = 0;
}
}
/**
* 享元类接口
* 模拟车类
*/
interface Flyweight {
void operate(UnsharableFlyweight unsharableFlyweight);
void release();
boolean occupy();
}
/**
* 具体非享元类
* 模拟骑单车的人
*/
class ConcreteUnsharableFlyweight implements UnsharableFlyweight {
private String name;
public ConcreteUnsharableFlyweight(String name){
this.name=name;
}
@Override
public void operate() {
System.out.println(name+"骑单车");
}
}
/**
* 非享元类接口
*/
interface UnsharableFlyweight {
void operate();
}
复制代码
这里是将近两百行的代码了,也加了非常多的注释,没有功劳也有苦劳,希望各位能够看在我这么辛苦的份上给个赞?。
总结
享元模式的重点在于共享和复用对象,把细粒度化的对象存储起来,下次再适用的适合直接适用即可,不需要再去创建了,避免了大量创建相似对象的开销,从而提高系统的性能。非常多的池技术就是基于享元模式而来的,但并不是任何地方都能够适用享元模式的,只有存在大量创建每一类型的对象、系统不依赖这些对象的状态的情况,才能够使用。
享元模式跟单例模式非常地相似,我曾经也一度怀疑享元模式是不是应该列入创建型模式,我现在学习完后,我发现我的怀疑是错误的。享元模式就应该是结构型模式,它是在管理一组对象,这一组对象和管理类结合在一起了,关注的是对象的结构。单例模式是关注这个类的对象是否是单一的,保证每个类只有一个对象,而享元模式是为了节约内存、提高性能,并不会保证每个类只有一个对象,一旦对象不够用,就会去创建新的。
——————————————————————————————
你知道的越多,不知道的就越多。
如果本文章内容有问题,请直接评论或者私信我。如果觉得我写得还不错的话,点个赞也是对我的支持哦
未经允许,不得转载!