【设计模式】享元模式,池技术的指导思想。

什么是享元模式(Flyweight)

概念

享元模式(Flyweight Pattern)属于结构型模式,主要是用于减少对象的创建和销毁的性能消耗,以减少内存的占用。面向对象的语言能够给我们的开发带来一定的灵活性和扩展性,而且更加符合人的思维方式,但这会让我们的系统创建很多对象,这会让我们系统的性能下降,所以可以使用一个缓冲池来把这些反复使用的对象存储起来,减少反复创建和销毁的次数,享元模式的思想就是复用对象

我们的生活中也有非常多的享元模式的例子,例如,围棋的棋子,共享单车,还有游泳池的游泳圈,这些东西都是被反复使用的。在开发中,我们常见的就是线程池、String常量池、数据库连接池,这些都是为了避免创建很多的对象而建立的一个“池子”。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yvExSHSD-1633181028589)(https://gitee.com/xuxiaojian1999/image-bed/raw/master/img/%E7%BE%8E%E5%A5%B3%E6%B8%B8%E6%B3%B3.gif)]

优点

降低内存,提高性能。我们想要使用一个对象只需要去“池子”里拿就行了,不需要再去创建,节省了不断创建和销毁的系统开销。

缺点

增加程序复杂度。需要把对象的粒度细化,把不能共享的部分外部化才能够实现共享,这增加编码的复杂度和代码的理解难度。

原则

“+”代表遵守,“-”代表不遵守或者不相关

原则开放封闭单一职责迪米特里氏替换依赖倒置接口隔离合成复用
++--+-+

适用场景

  1. 需要大量相同的对象或是一类对象中有很多相同的部分,并且创建或销毁这些对象会带来较大的性能开销。就可以把这些对象抽取出来一个不变的对象,然后存在池子里,需要适用的时候直接拿出来用。
  2. 系统不依赖这些对象的状态,只是拿来用一下。
  3. 一个对象可以分为内外两部分,内部分可以反复使用,外部分由使用者自行定义。

表情复杂

如何实现

想要实现享元模式,需要以下五样东西:

  1. 享元类接口(Flyweight):定义所有享元类要实现的规范。
  2. 具体享元类(Concrete Flyweight):实现享元类接口。
  3. 非享元类接口(Unsharable Flyweight):定义非享元类要实现的规范。
  4. 具体非享元类(Concrete Unsharable Flyweight):实现非享元类接口。
  5. 享元工厂类(Flyweight Factory):负责创建和管理享元类对象。

上类图

image-20210603235429763

有那么一点点滴复杂,可以结合下面的代码来理解。

上代码

惠州是一个旅游胜地(我为惠州代言),红花湖是一个非常适合骑单车游玩的地方,现在有一家车店,专门在红花湖出租单车,供来玩的游客骑着环绕红花湖,车店(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();
}

这里是将近两百行的代码了,也加了非常多的注释,没有功劳也有苦劳,希望各位能够看在我这么辛苦的份上给个赞👍。

我图已经发完了,快点赞

总结

享元模式的重点在于共享和复用对象,把细粒度化的对象存储起来,下次再适用的适合直接适用即可,不需要再去创建了,避免了大量创建相似对象的开销,从而提高系统的性能。非常多的池技术就是基于享元模式而来的,但并不是任何地方都能够适用享元模式的,只有存在大量创建每一类型的对象、系统不依赖这些对象的状态的情况,才能够使用。

享元模式跟单例模式非常地相似,我曾经也一度怀疑享元模式是不是应该列入创建型模式,我现在学习完后,我发现我的怀疑是错误的。享元模式就应该是结构型模式,它是在管理一组对象,这一组对象和管理类结合在一起了,关注的是对象的结构。单例模式是关注这个类的对象是否是单一的,保证每个类只有一个对象,而享元模式是为了节约内存、提高性能,并不会保证每个类只有一个对象,一旦对象不够用,就会去创建新的。

开猿节流

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员徐小白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值