java享元模式 | 如何共享对象

666 阅读5分钟

享元模式.png

这是我参与8月更文挑战的第12天,活动详情查看:8月更文挑战

欢迎来到今天的学习,今天我们一起来学习下实现原理很简单,但是应用场景比较狭窄的一种模式----享元模式。多唠叨几句,我本月将会对java的设计模式精讲,欢迎点击头像,关注我的专栏,我会持续更新,加油!

系列文章:

设计模式之单例模式

设计模式之工厂模式

设计模式之建造者模式

设计模式之代理模式

设计模式之访问者模式

设计模式之适配器模式

设计模式之命令者模式

java状态模式 | 随时随地监控状态改变

java观察者模式 | 如何通知事物的变化

java备忘录模式 | 如何记录历史信息

java迭代器模式模式 | 如何对各个元素进行访问

...持续更新中

话不多说,进入正题

享元模式

听到名字,享元,很抽象,这什么玩意,有什么用呢。 拆开来讲,享,即共享,元,可以理解为元数据,内存当中的数据,对象。看来是共享对象喽

哈,马上到七夕了,来共享下对象啊

image.png

哈哈,预祝大家七夕快乐!

回归正题,我们先这样理解该模式,拿spring的常量池、数据库连接池、缓冲池等等这些都是享元模式的应用。

比如我们每次创建字符串对象时,如果每次都创建一个新的字符串对象的话,内存开销会很大,所以如果第一次创建了字符串对象“七夕“,下次再创建相同的字符串”七夕“时,只是把它的引用指向”七夕“,这样就实现了”七夕“字符串再内存中的共享。

场景的话,可以想像王者荣耀,一台服务器连接了多个客户端(多个玩家),如果每个进去的地图都要创建对象,那对象将无数啊。。。。所以这里要使用享元模式,将地图对象减少到几个实例。

官方是这样定义的:摒弃了在每个对象中保存所有数据的方式,通过共享多个对象所共有的相同状态,从而让我们能在有限的内存容量中载入更多对象。

我们下面看张图

image.png

  • 享元类(Flyweight):定义了享元对象需要实现的公共操作方法。在该方法中会使用一个状态作为输入参数,也叫外部状态。

  • 享元工厂类(Flyweight Factory):管理一个享元对象类的缓存池

  • 可共享的具体享元类(ConcreteFlyweight):能够复用享元工厂内部状态并实现享元类公共操作的具体实现类。

  • 非共享的具体享元类(UnsharedConcreteFlyweight):不复用享元工厂内部状态,但实现享元类的具体实现类。

我们下面拿王者荣耀场景例子实现下

场景代码展示

我们都知道 王者荣耀分为战士,刺客,法师等等类型的英雄。下面用代码帮你深入理解下

 //定义英雄抽象类
 public abstract class HeroType {
     public abstract void init();  
 }
 
 
 
 //具体英雄类型
 public class ConcreteHeroType extends HeroType {

    private String typeName = "";

    public ConcreteHeroType(String typeName) {
        this.typeName = typeName;
    }

    @Override
    public void init() {
        System.out.println("英雄分类:" + typeName);
    }

}


//英雄工厂类
public class HeroTypeFactory {

    //用map作为存储
    private HashMap<String, ConcreteHeroType> pool = new HashMap<>();

    //获得英雄分类
    public HeroType getHeroTypeCategory(String key) {
        if(!pool.containsKey(key)) {
            pool.put(key, new ConcreteWebSite(key));
        }

        return (HeroType)pool.get(key);
    }

    //获得英雄分类总数
    public int getHeroTypeCount() {
        return pool.size();
    }

}

我们调用下,传几种类型的英雄,大家看下最后的个数

public class Client {
    public static void main(String[] args) {
        HeroTypeFactory factory = new HeroTypeFactory();

        HeroType fx = factory.getHeroTypeCategory("刺客");
        fx.init();

        HeroType fy = factory.getHeroTypeCategory("刺客");
        fy.init();

        HeroType fz = factory.getHeroTypeCategory("法师");
        fz.init();

        HeroType fa = factory.getHeroTypeCategory("法师");
        fa.init();

        HeroType fb = factory.getHeroTypeCategory("射手");
        fb.init();

        HeroType fc = factory.getHeroTypeCategory("射手");
        fc.init();

        System.out.println("英雄分类总数为:" + factory.getWebSiteCount());
    }
}
//输出
英雄分类:刺客
英雄分类:刺客
英雄分类:法师
英雄分类:法师
英雄分类:射手
英雄分类:射手

英雄分类总数为:3

是不是本来该6个对象的,现在是3个。

所以说,享元模式本质上是通过创建更多的可复用对象的共有特征来尽可能地减少创建重复对象的内存消耗。

OK 代码到这里就结束了

总结

我们来看下java当中是如何用的 ,在 Java 中,享元模式一个常用的场景就是,使用数据类的包装类对象的 valueOf() 方法。比如,使用 Integer.valueOf() 方法时,实际的代码实现中有一个叫 IntegerCache 的静态类,它就是一直缓存了 -127 到 128 范围内的数值,如下代码所示,你可以在 Java JDK 中的 Integer 类的源码中找到这段代码。

image.png

你会发现,享元模式本质上在使用时就是找到不可变的特征,并缓存起来,当类似对象使用时从缓存中读取,以达到节省内存空间的目的。

优点:

1、第一个,减少内存消耗,节省服务器成本。 比如,当大量商家的商品图片、固定文字(如商品介绍、商品属性)在不同的网页进行展示时,通常不需要重复创建对象,而是可以使用同一个对象,以避免重复存储而浪费内存空间。

2、聚合同一类的不可变对象,提高对象复用性

弦外之音

感谢你的阅读,如果你感觉学到了东西,麻烦您点赞,关注。

我已经将本章收录在专题里,点击下方专题,关注专栏,我会每天发表干货,本月我会持续输入设计模式。

加油! 我们下期再见!