JAVA设计模式之享元模式

来源:互联网 发布:sql update 多个行 编辑:程序博客网 时间:2024/06/05 08:19

1. 什么是享元模式?

享元模式(Flyweight Pattern)主要用于减少对象被重复创建的数量,以减少内存占用和提高性能。享元模式是对象结构型模式,以共享的方式高效的支持大量的细粒度(小而多)的对象。


享元模式尝试重用现有的对象。在有大量对象时,有可能会造成内存溢出,我们把其中共同的部分抽象出来,如果有相同的业务请求,直接返回在内存中已有的对象,避免重新创建。


享元模式要实现对象共享,必须要区分对象的内蕴状态外蕴状态:

(1)内蕴状态是存储在享元对象内部的对象,而且不会随环境改变而改变的。一般是成员变量。内蕴状态可以共享。内蕴状态的不同可以将共享对象分成多个组。

(2)外蕴状态是随环境变化的,不可以共享的。一般为享元对象方法传入的参数。外蕴状态和内蕴状态相互独立,互不影响。外蕴状态必须由客户端管理,在享元对象创建后传入其内部。


享元模式一般应用在系统的底层之中,常常用在缓冲池的技术实现中。

 

2. 角色

 

图片来源于网络

抽象享元角色(Flyweight):此角色可以是一个接口或抽象类。所有具体享元的父类,定义了一些公共接口(方法),需要子类提供具体的实现。

具体享元角色(ConcreteFlyweight):具体享元角色提供了父类具体的实现。

享元工厂角色(FlyweightFactory):负责创建并管理享元角色。其中有一个集合用来维护创建好的享元对象,可以是数组、List、hashMap等等。当客户端调用一个享元对象的时候,工厂类首先判断此享元是否已经存在,若存在直接返回,若不存在新建一个返回并存放到集合中。

3. 优点

    大幅度减少重复对象的创建,减少内存中对象的数量,节约内存提高系统性能。

4. 缺点

    使系统复杂。使用享元模式需要对对象的内蕴状态和外蕴状态进行分离,使程序逻辑复杂化。

5. 使用场景

(1)系统中存在大量重复的对象,且这些对象消耗大量内存。

(2)系统不依赖这些细粒度对象的身份,对象内部变化不会影响系统的运行。

(3)需要缓冲池的场景。比如JDK的String类,Integer类。创建String对象的时候首先去字符串常量池中匹配,如果没有才创建一个新的。而Integer类在-128-127范围内的整数都是用的系统缓存。

(4)系统在使用享元模式时必须维护一个记录享元模式的集合,我们称之为对象池(例如示例中的HashMap pool)。由于集合也需要占用一定的系统资源,因此当在有足够多的享元对象时才值得使用享元模式。

6. 示例代码

(1)抽象享元角色

/** * 抽象享元接口 */public interface IFlyWeight {    public void write(Object obj);}

(2)具体享元角色

/** * 具体享元角色 * 内部有一个内蕴状态:name,和外蕴状态:age * age的变化不会影响name。 */public class ConcreteFlyWeight implements IFlyWeight {private String name;public ConcreteFlyWeight(String name) {this.name=name;}@Overridepublic void write(Object age) {System.out.println("姓名:"+name+",年龄:"+age);}}

(3)享元工厂角色

/** * 享元工厂方法类,负责创建和维护享元对象 */public class FllyWeightFactory {//维护享元对象,此处称之为享元对象池private static volatile HashMap<String, IFlyWeight> pool=new HashMap<String, IFlyWeight>();/** * 获取享元对象方法 */public static synchronized IFlyWeight getFlyWeight(String key){IFlyWeight fw=pool.get(key);if(fw==null){fw=new ConcreteFlyWeight(key);pool.put(key, fw);System.out.println(key+" 被创建,并放入池中");}else{System.out.println(key+" 已存在,并从池中取出");}return fw;}public static void getInit() {// 初始化享元对象池中的数据String[] name = { "小明", "小华", "TOM", "LILY" };for (String str : name) {getFlyWeight(str);}}}


(4)测试

public class Client{public static void main(String[] args) {// 初始化享元对象池中的数据FllyWeightFactory.getInit();System.out.println("-----------");//使用享元工厂创建一个新的对象IFlyWeight ifw=FllyWeightFactory.getFlyWeight("B");ifw.write(20);System.out.println("-----------");//使用享元工厂取一个已有的对象IFlyWeight ifw2=FllyWeightFactory.getFlyWeight("TOM");ifw2.write(25);}}


(5)测试结果

小明 被创建,并放入池中小华 被创建,并放入池中TOM 被创建,并放入池中LILY 被创建,并放入池中-----------B 被创建,并放入池中姓名:B,年龄:20-----------TOM 已存在,并从池中取出姓名:TOM,年龄:25


7. 享元模式和单例模式的异同

    单例模式确保一个类只有一个实例,并提供一个全局访问点,只有访问这个全局访问点才能得到这个类的实例对象。享元模式用于减少被重复创建对象的数量,以减少内存占用和提高性能。可以看出来单例模式是类级别的,而享元模式是对象级别的。单例模式要求全局只提供一个对象来访问,而享元模式要求系统多个使用到相同对象的地方,都只需要使用对象池中这一个对象即可,对象池中可以有多个共享的对象。单例可以看作是享元的一种特例,单例提供一个共享的对象,享元可以提供多个共享对象。单例比享元更加严格的控制对象的唯一性。




参考资料http://www.runoob.com/design-pattern/flyweight-pattern.html

【四川乐山程序员联盟,欢迎大家加群相互交流学习5 7 1 8 1 4 7 4 3】