Hi,我是十三。天气好热都要融化了,恐怖的三伏天…
Photo by Dawid Zawiła on Unsplash
前言
这是设计模式系列的第 十 篇学习记录,天气还是那么炎热,我盼望着富婆的到来,我不想努力了,然而什么都没有出现,继续搬砖…
在开始我们今天的学习前,十三首先问大家一个基础的问题,假设现在有 两 个字符串对象,它们的内容也就是赋值相同,那么它们是同一个对象吗?我们先用代码描述出来先 :
那么最后我们运行程序的结果是什么呢,是 true 还是 false 呢?
没错,答案是 true,我们知道在 Java 中字符串常量都是存放在常量池中的,Java 会确保 一 个字符串常量在常量池中只有 一 个拷贝,并且 String 对象是 final 类型,对象一旦创建就不可改变。在平时开发中,会出现许多 String 类型的变量,如果只是小应用倒还好,假设是一个庞大的系统,有好多处都需要用定义 String 类型的变量,那开销可想而知,而 JDK 的开发者自然想到了这点,采用了享元模式解决创建大量相同 String 变量带来的开销问题。
那么问题就来了,这个享元模式到底是什么呢?我们接着往下看。
享元模式
享元模式是池技术的重要实现方式,它不仅可以减少应用程序创建的对象,而且降低了程序内存的占用,提高程序的性能。
.
支持大量细粒度对象,就会使得对象数量多并且性质基本上是相同的,这些对象分为两部分:内部状态和外部状态。
内部状态是对象可分享出来的信息,存储在享元对象内部并且不会随环境的改变而改变;如上面的字符串例子中,字符串对象具有字符串类型的特性,这些特性是每个字符串对象都拥有的且是不会改变的。
外部状态则是对象依赖的一个标记,它是随环境改变而改变的并且不可共享的状态。如每个字符串对象的内容都可能会不同,它是会随时变化的。
享元模式中的角色及职责 :
Flyweight : 抽象享元角色,同时定义出对象的外部状态和内部状态的接口或者实现.
ConcreteFlyweight: 具体享元角色,实现抽象享元角色定义的业务方法.
FlyweightFactory: 享元工厂,负责管理对象池和创建享元对象.
光说不练假把式,咱们这里我们就以一个线上手机商城为例子把享元模式用起来 :
这里我们先定义了一个接口,里面分别提供了 showPrice 这个方法,它便对应模式里抽象享元这个角色。我们接着往下看 :
接着我看到这里定义了个类,它实现了前面我们定义的抽象享元角色接口并实现了接口里的方法,那么它对应的角色便是具体享元这个角色了。除了实现接口方法外,我们还定义两个字段属性和构造方法,字段属性分别是手机名字以及手机型号。好,我们继续往下面看 :
我们看到这个类名带有 Factory,那么想都不用想,它就是享元工厂类这个角色了。享元工厂是用来创建享元对象的,它通过 Map 容器来存储对象,将内部状态的属性作为 Map 的 key,这里我们手机的内部状态便是 PhoneName,不管怎么样手机名字是不会改变的。然后我们进行判断,如果 Map 容器包含这个 key,那我们就使用 Map 容器存储的这个对象;否则就新创建对象并放入 Map 容器中,以达到共享的目的。
最后我们通过运行结果可以得到,首先由于是第一次创建对象,Map 容器中并没有存储这个对象,所以它便新建对象便存储到 Map 容器中,接下来,只要再创建对象时便直接使用 Map 容器存储的对象。我们在回过头来想下,在这里,手机名字作为内部状态它是不会改变的,而手机型号则是外部状态它的值是会变化的,当然最终的手机价格也会发生改变。如此一来,因为有了共享对象,节约了开销。
总结
那么到最后我们来简单总结下 :
如果一个应用程序使用了大量的相似对象,而这些大量的对象造成了很大的存储开销就应该考虑使用;当需要缓冲池的场景。
减少应用程序创建的对象,降低程序内存的占用,提高程序的性能。
最后来张观察者模式的合照 :
Flyweight 结构图
好了,关于享元模式的学习记录就到这里了,天气那么热,各位注意防晒和多吃点水果。
领取专属 10元无门槛券
私享最新 技术干货