Java设计模式之享元模式

版权声明:本文出自汪磊的博客,转载请务必注明出处。

设计模式系列与数据结构算法系列为本人知识的梳理,大体上没有什么新玩意,都是前人们工作经验的结晶,好了,不啰嗦了,基础的重要性懂得自然懂。等这两个系列总结完在写框架方面的,好的框架必然包含一种或多种设计模式与数据结构。

一、享元模式

享元模式解决的就是在有大量对象时,有可能会造成内存溢出,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建,说白了就是对象的复用,而不是每次都直接新创建一个对象。

享元模式核心就是相同或者相似对象的共享复用,分享的是对象相同的部分,而不同的部分用接口暴露出去根据环境动态的设置,而这在享元模式中分别叫做内部状态与外部状态。

接口暴露的就是对象可以改变的部分,称作外部状态,而那些通过外部设置但是不允许共享的部分称作内部状态。并且外部状态的改变不能影响内部状态。

享元模式很重要一点是分离对象变与不变的部分,变化的部分通过接口提取,共享对象时需要外部自己设置,不变的部分可以共享出去。

日常开发中,能够共享的内部状态是有限的,因此通过享元模式共享的对象一般都设计为较小的对象,它所包含的内部状态较少,这种对象也称为细粒度对象。所谓内部状态较少说白了就是对象的属性大部分外部使用时都可以根据自己需要来设置。

我们使用享元模式的目的就是使用共享技术来实现大量细粒度对象的复用。

好了,通过上面描述估计一部分同学蒙圈了,这都是什么玩意,接下来我们通过实际代码来看一下。

假设这样一个业务场景,我们需要不同的玩具对象使用,而玩具的大小属性一开始就设定好了,颜色属性外部可以自己根据需要设置。

根据上述需求,我们就可以提取出玩具对象的内部状态为大小属性,不可外部随意设置,而颜色属性为外部状态,可根据需要自行设置。

抽象出享元对象接口:

public interface IToys {
void setColor(String color);

int getSize();
String getColor();
}

具体享元对象:

 1 public class SpecificToys implements IToys {
 2     
 3     private String color;
 4     private int size;
 5 
 6     public SpecificToys(int size) {
 7         this.size = size;
 8     }
 9 
10     @Override
11     public void setColor(String color) {
12         this.color = color;
13     }
14 
15     public String getColor() {
16         return color;
17     }
18 
19     public int getSize() {
20         return size;
21     }
22 
23     public void setSize(int size) {
24         this.size = size;
25     }26 }

具体共享对象是不允许直接外部使用创建的,这里我们需要定义一个工厂类,需要使用此对象全部由工厂类控制。

 1 public class ToysFactory {
 2 
 3     //存储共享对象
 4     private Map<Integer, IToys> cacheMap = new HashMap<>();
 5 
 6     private static ToysFactory instance = null;
 7     private ToysFactory(){}
 8 
 9     public static ToysFactory getInstance(){
10         if (null == instance){
11             synchronized (ToysFactory.class){
12                 if (null == instance){
13                     instance = new ToysFactory();
14                 }
15             }
16         }
17         return instance;
18     }
19 
20     public IToys getIToysInstance(int size){
21 
22         IToys iToys = cacheMap.get(size);
23         if (null == iToys){
24             iToys = new SpecificToys(size);
25             cacheMap.put(size, iToys);
26         }
27         return iToys;
28     }
29 
30 }

工厂类核心就是cacheMap,存储已经产生过的对象,获取的时候先检查内存中是否有对应对象缓存,有则直接复用。

最后测试一下:

 1 IToys iToys1 = ToysFactory.getInstance().getIToysInstance(1);
 2 iToys1.setColor("red");
 3 System.out.println(iToys1+"---"+iToys1.getSize()+"---"+iToys1.getColor());
 4 
 5 IToys iToys2 = ToysFactory.getInstance().getIToysInstance(1);
 6 iToys1.setColor("blue");
 7 System.out.println(iToys2+"---"+iToys2.getSize()+"---"+iToys2.getColor());
 8 
 9 IToys iToys3 = ToysFactory.getInstance().getIToysInstance(1);
10 iToys1.setColor("yellow");
11 System.out.println(iToys3+"---"+iToys3.getSize()+"---"+iToys3.getColor());

打印如下:

1 com.wanglei55.mjavalib.SpecificToys@75b84c92---1---red
2 com.wanglei55.mjavalib.SpecificToys@75b84c92---1---blue
3 com.wanglei55.mjavalib.SpecificToys@75b84c92---1---yellow

看到了吧,这样就实现了对象的复用,这里的ToysFactory只能产生一种实例对象,要是想生产多种呢?自己思考一下。。。

二、享元模式总结

享元模式是提升系统性能的一种设计模式,通过享元模式我们可以实现对象的复用,从而减少创建对象的开销,提升性能。

使用场景:

当我们遇到需要创建大量相同或相似对象,并且这些对象的大部分属性都可以外部化那么此时就可以考虑使用享元模式

此外享元模式的使用增加了系统的复杂度,我们需要把对象的外部状态与内部状态区分开来,这些都是其缺点。

享元模式以共享的方式高效地支持大量的细粒度对象,享元对象能做到共享的关键是区分内部状态和外部状态。其中内部状态是存储在享元对象内部并且不会随环境改变而改变的状态,因此内部状态可以共享;外部状态是随环境改变而改变的、不可以共享的状态。

好了,享元模式介绍到此为止了,希望对你有用。