硬肝系列:23种设计模式之享元模式

用示例代码来帮你了解享元模式

对于“设计模式”这个词大家肯定都不陌生,很多框架也用到了设计模式,但是大部分的开发者应该是没有深入的了解过,我准备硬肝下这23设计模式作为专题文章的开端,一共23种设计模式,我尽量在<23天肝完。

为什么要学习设计模式:https://blog.csdn.net/kaituozhe_sh/article/details/107922339

在我大学四年,对设计模式也没有什么概念,写代码就想着能实现就可以了,不会有设计模式那样的思想,但是当学习到了框架的时候,对于设计模式才有了一些更深入的了解,使用设计模式的代码在扩展性上会比暴力的代码更容易维护,特别是当一个程序猿离职了后,你去接手它的代码,里面是一大堆if else,这样真的会崩溃,修改都不知道从何下手
在这里插入图片描述

硬肝系列目录

创建型模式

23种设计模式之工厂模式

23种设计模式之抽象工厂模式

23种设计模式之建造者模式

23种设计模式之原型模式

23种设计模式之单例模式

结构型模式

23种设计模式之适配器模式

23种设计模式之桥梁模式模式

23种设计模式之代理模式模式

23种设计模式之外观模式模式

23种设计模式之装饰器模式

23种设计模式之享元模式

23种设计模式之组合模式

行为型模式

23种设计模式之责任链模式

23种设计模式之命令模式

23种设计模式之迭代器模式

23种设计模式之中介者模式

23种设计模式之备忘录模式

到目前为止、23种设计模式的创建型模式已经给大家肝完了,现在我们进入到一个全新的章节,结构型模式!!!

什么是享元模式

享元模式,最主要的是利用共享的思想,将系统中有大量的对象、这些对象消耗大量的内存、且创建这些对象会耗费大量的资源,我们就可以用共享的思想来控制这些对象

共享思想:如果们单纯的来一个请求就创建一个对象的话,这样在并发量高的时候可能会造成OOM,为什么我们不把它们放到一个容器中管理呢?比如我们将共同的部分抽象出来,用唯一标识符来表示放到内存中,如果来一个业务请求,我们先去内存查找,如果内存中有这个对象的话直接返回即可,如果没有再去创建新对象

Java应用实例:redis缓存热点数据、数据库的缓冲池、String的常量池

下面我会使用hashmap来储存对象,用一个手机库存的例子来完成享元模式的讲解

上代码,首先我们定义一个统计数量的接口

public interface Count {
    int getCount();
}

用一个实现类来实现它,在这个实现类中我们有货物的名称还有它的库存,现实生活中肯定还有一些乱七八糟的属性,在这里我们就简化了一下

@Data
public class Goods implements Count{
    private String name;
    private int count;

    public Goods(String name) {
        this.name = name;
    }

    @Override
    public int getCount() {
        return count;
    }
}

然后这里有一个享元模式的工厂类,也就是我们常说的,这里面存放的就是我们缓存的东西

public class GoodFactory {
	//map池:用来缓存对象
    private static final Map<String,Goods> STOCK = new HashMap<>();
    private static final String GOODS[] = {"huawei","xiaomi","iphone","oppo","vvio"};

	//我们通过货物名称为唯一标识来bie'an
    public static Count getGoods(String name){
        Goods good = (Goods)STOCK.get(name);
        //如果池中不存在此货品,则创建此货品
        if(good == null){
            good = new Goods(name);
            good.setCount((int) (Math.random()*100));
            STOCK.put(good.getName(),good);
            System.out.println("新加入货物: " + name + " 库存定为: " + good.getCount());
        }else{
            System.out.println("货物: " + name + " 更新库存: " + good.getCount());
        }
        return good;
    }
}

上面这段代码就代表了享元模式意义,在我们新建一个对象的时候,先去里找有没有这个对象,有的话直接返回,没有则创建新的对象并放入池中,减少了我们创建对象所耗费的资源以及控制了对象的数量

来验证一下我们的代码

public static void main(String[] args) {

        int i = 0;
        while(i<20){
            Goods goods = (Goods) GoodFactory.getGoods(GOODS[(int) (Math.random()*GOODS.length)]);
            goods.setCount((int) (Math.random()*1000));
            i++;
        }
    }

输出结果:
在这里插入图片描述
这里我们上一个java实例给大家看看:String常量池原理

我想对于String类型大家肯定都很熟悉,但是大家知道创建一个String类型的对象的过程吗?我们先来看下面的这段代码

public class demo1 {
    public static void main(String[] args) {
        String s1 = "i'm ALiangX";
        String s2 = new String("i'm ALiangX");
        String s3 = "i'm ALiangX";
        System.out.println(s1 == s2);//false
        System.out.println(s1 == s3);//true
    }
}

有人知道上面这两段代码的结果为什么不一样吗?我先给大家上副图,让大家看看上述代码执行时jvm的内存运行图
在这里插入图片描述

当我们使用指令String s1 = "i'm ALiangX"直接创建出来的字符串他会直接进入堆中的一块区域,称为字符串常量池,而通过String s2 = new String("i'm ALiangX");这段指令创建出来的字符串呢?他会先在堆中开辟一块新的区域,然后在栈中新建一个对象,这个对象指向的是堆中存放该字符串的地址,而且符号‘==’比较的是两个对象的地址,使用object.equals(object)比较的才是两个对象的内容,看上面的内存模型就知道为什么s1、s2两个对象相比较为false了

在字符串常量池中就是使用了我们享元模式的设计原理,因为对于String类型的字符串,它创建出来是不可改变的,这使得创建一个String对象的成本非常大,当我们创建s3时,他会先去常量池中检查有没有和它一样的对象在池里,如果有则直接返回池中的对象,没有的话才在池中创建一个新的对象

为什么String类型的对象创建之后不可以改变呢?我给大家看看String对象的源码
在这里插入图片描述
看到了吗,final修饰的对象创建之后不可以修改,所以String类型的对象创建出来不可以改变

享元模式的缺点:提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。

使用场景:
1、系统有大量相似对象、且对象数量多
2、需要缓冲池的场景

完成:TO: 2021/3/24 22:59

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

沉淀顶峰相见的PET

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

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

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

打赏作者

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

抵扣说明:

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

余额充值