白话设计模式之享元模式

2022-07-26 17:03:52 浏览数 (1)

码农在囧途

人的挫败感大多来自努力了很久,没有看到成效,从而心里产生很大的落差,就像喜欢一个女孩子很久,一直聊得很开,并且彼此的反馈都很不错, 终于有一天鼓起勇气说出来了,但是她却说,我只是把你当成好朋友来看待,其实女孩子说的可能是她的真实想法,但是我觉得大多的原因应该是 时间拖得太久了,以至于本来对方的那种激烈的内心已经平复下来了,这就是不勇敢而导致的,其实不论是追女孩子,还是学习,工作,都要学会 看时机,有些东西错过了真的会后悔的,你五年前在学校想上台去表演一下,但是由于你自卑,不自信,所以错过了一次体验,一些美好的回忆, 但是今天你回忆起来,你会觉得,即使上去丢脸了,也没啥啊,老子今天依然还活着啊!同样,今天,你想做的事如果你不去做,那么五年后, 你也会后悔,可是,人生又有多少个五年呢!

享元模式定义

运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率, 我们平时用到的线程池,连接池都是类似的思想,享元模式属于结构型设计模式。

应用示例

我们知道像微博这种流量很大的系统,在某明星进行官宣,或者塌房的时候,微博的搜索量总是很大,像前段时间王先生,还有凡凡,都是这种情况,对于 这种爆炸消息,消息肯定不能存放在关系型数据库中,要么存放在缓存中,比如Redis,要么存在搜索引擎中,比如elasticsearch,其目的都是减少 数据库的操作和搜索对象的创建,今天我们用享元模式来实现热点数据的查询,提升性能,减少内存的消耗。

代码结构

代码语言:javascript复制
com
  └─steak
      └─desgin
          └─flyweight 
              ├─HotspotData.java
              └─HotSpotDataClient.java
              └─HotspotDataFactory.java
              └─HotSpotRpcService.java
              └─IHotspotData.java
              └─R.java

编码

1.定义热点数据接口IHotspotData

此接口定义了获取热点数据的方法getHotSpotData,参数为key,比如搜索凡凡

代码语言:javascript复制
public interface IHotspotData {
    R getHotSpotData(String key);
}

2.热点数据接口实现类HotspotData

实现类主要是调用热点数据Rpc接口获取热点数据。

代码语言:javascript复制
public class HotspotData implements IHotspotData {
    @Override
    public R getHotSpotData(String key){
        HotSpotRpcService hotSpotRpcService = new HotSpotRpcService();
        return hotSpotRpcService.queryHotSpotDataByKey(key);
    }
}

3.模拟获取热点数据Rpc接口

此接口模仿获取数据库中的数据,进行一定的休眠,标识请求消耗的时间。

代码语言:javascript复制
public class HotSpotRpcService {
    public R queryHotSpotDataByKey(String key) throws InterruptedException {
        //模拟获取数据时间
        TimeUnit.MILLISECONDS.sleep(10);
        HashMap<String, Object> hashMap = new HashMap<>();
        hashMap.put("title","王思聪当舔狗");
        hashMap.put("content","王思聪和孙一宁的聊天中出现了舔狗情景,“宝,今天输液,输的什么液,想你的夜”");
        return new R(200,"获取成功",hashMap);
    }
}

4.热点数据享元工厂

热点数据享元工厂里面主要进行对象实例和数据的缓存,其结构是HashMap,getHotspotDataByKey方法中如果发现热点key存在 缓存中,那么就取缓存中的数据,如果缓存中不存在,就获取数据后将数据存储在缓存中,以便别人获取直接从缓存中获取,防止创建大量的对象(HotspotData)和频繁的 访问数据接口。

代码语言:javascript复制
public class HotspotDataFactory {
    private static final Map<String, R> hotMap = new HashMap<>();
    public static R getHotspotDataByKey(String key) throws InterruptedException {
        if (!hotMap.containsKey(key)) {
            IHotspotData instance = new HotspotData();
            R data = instance.getHotSpotData(key);
            hotMap.put(key, data);
        }
        return hotMap.get(key);
    }
}

5.统一返回结构体

代码语言:javascript复制
@Data
@AllArgsConstructor
public class R {
    private int code;
    private String msg;
    private Object data;
}

6.代码测试

在测试代码中,我们发送10000个请求取获取数据,并且记录下所消耗的时间。

代码语言:javascript复制
public class HotSpotDataClient {
    public static void main(String[] args) throws InterruptedException {
        long start = System.currentTimeMillis();
        String key = "王思聪当舔狗";
        for (int i = 0 ; i < 10000 ; i  ){
            R hotspotData = HotspotDataFactory.getHotspotDataByKey(key);
            System.out.println(hotspotData);
        }
        long  time = System.currentTimeMillis() - start;
        System.out.println("花费时间:  " time);
    }
}

从返回结果中看出消耗了84毫秒。

以上是我们使用了享元模式来对数据进行了缓存,所以要的时间非常短,那么我们下面来测试不使用享元模式的情况下,需要消耗多少时间呢。

7.不使用享元模式

没有使用享元模式,其实就是不使用缓存了,每次获取数据需要先创建一个对象实例,然后再去调用方法。

代码语言:javascript复制
public class HotspotDataFactory {
    public static R getHotspotDataByKey(String key) throws InterruptedException {
        return new HotspotData().getHotSpotData(key);
    }
}

从图看出不使用享元模式的话,每次都要创建对象,然后再去调用获取热点数据的接口,大量的时间都花费再创建对象和远程Rpc接口上面,所以花费 了大量的时间,因为像这种数据本身就不会发生改变,所以就直接存在缓存中就行了,每次去数据库中查询是不理智的。

UML图

结语

关于享元模式的介绍就到这里,通过享元模式,我们可以减少对对象的创建,从而减少内存的开销,因为如果在某一时刻,创建了大量的对象, 那么是有可能导致OOM的,而享元模式的思想就能很好的避免这个问题,它的核心理念就是缓存,通过缓存来提高对象和数据的复用, 不过现在如果涉及到频繁的数据,我们可以存放在缓存中间件中,但是对于对象的创建,享元模式依然值得我们借鉴。

今天的分享就到这里,感谢你的观看,我们下期见。

0 人点赞