Java设计模式之享元模式

前言

当系统中存在大量相同或者相似的对象时,享元模式是一种值得考虑的解决方案,它通过 共享技术实现相同或相似的细粒度对象的复用,从而节约了内存空间、提高了系统性能。在享元模式中提供了一个享元池用于存储已经创建好的香园对象,并通过享元工厂类将享元对象提供给客户端使用。

正文

一、享元池

享元模式通过共享技术实现相同或相似对象的重用,在逻辑上每一个出现的字符都有一个对象与之对应,然而在物理上它们却共享同一个享元对象,这个对象可以出现在一个字符串的不同地方,相同的字符对象都指向同一个实例,在享元模式中存储这些共享实例对象的地方称为享元池(Flyweight Pool)。

二、享元的内部状态和外部状态

享元模式以共享的方式高效地支持大量细粒度对象的重用,享元对象能做到共享的关键是区分了内部状态(Intrinsic State)和外部状态(Extrinsic State)。
(1)内部状态是存储在享元对象内部并且不会随环境改变而改变的状态,内部状态可以共享。例如字符的内容不会随外部环境的变化而变化,字符“a"始终是”a",不会变成“b"。
(2)外部状态是随环境改变而改变的、不可以共享的状态。享元对象的外部状态通常由客户端保存,并在享元对象被创建之后需要使用的时候再传入到享元对象内部。一个外部状态与另一个外部状态之间是相互独立的。例如有的”a"是红色的,有的是绿色的,有的“a"是五号字,有的是四号字。
正因为区分了内部状态和外部状态,可以将具有相同内部状态的对象存储在享元池中,享元池中的对象是可以实现共享的,当需要的时候就将对象从享元池中取出,实现对象的复用。通过向取出的对象注入不同的外部状态可以得到一系列相似的对象,而这些对象在内存中实际上只存储一份。

三、定义

定义如下:

享元模式: 运用共享技术有效地支持大量细粒度对象的复用。
Flyweight Pattern: Use sharing to support large numbers of fine-grained objects efficiently.

享元模式要求能够被共享的对象必须是细粒度对象,它又称为轻量级模式,享元模式是一种对象结构型模式。

四、结构

享元模式的结构较为复杂,通常结合工厂模式一起使用,包含了一个享元工厂类:
(1)Flyweight(抽象享元类):抽象享元类通常是一个接口或抽象类,抽象享元类中声明了具体享元类公共的方法,这些方法可以向外界提供享元对象的内部数据(内部状态)。同时也可以通过这些方法来设置外部数据(外部状态)。
(2)ConcreteFlyweight(具体享元类):具体享元类实现了抽象享元类,其实例称为享元对象;在具体享元类中为内部状态提供了存储空间。通常可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象。
(3)UnsharedConcreteFlyweight(非共享具体享元类):并不是所有的抽象享元类的子类都需要被共享,不能被共享的子类可以设计为非共享具体享元类;当需要一个非共享具体享元类的对象时可以直接通过实例化创建。
(4)FlyweightFactory(享元工厂类):享元工厂类用于创建并管理享元对象,它针对抽象享元类编程,将各种类型的具体享元对象存储在一个享元池中,享元池一般设计为一个存储“键值对”的集合(也可以是其他类型的集合),可以结合工厂模式进行设计;当用户请求一个具体享元对象时,享元工厂提供一个存储在享元池中已创建的实例或者创建一个新的实例(如果不存在),返回新创建的实例并将其存储在享元池中。

五、实现

典型的抽象享元类代码如下:

public abstract class  Flyweight{
	public abstract void operation(String extrinsicState);
}

在具体享元类中通常将内部状态作为具体享元类的成员变量,而外部状态通过注入的方式添加的具体享元类中。典型代码如下:

public class ConcreteFlyweight extends Flyweight{
	//内部状态intrinsicState作为成员变量,同一个享元对象的内部状态是一致的
	private String intrinsicState;
	public ConcreteFlyweight(String intrinsicState){
		this.intrinsicState = intrinsicState;
	}

	//外部状态extrinsicState在使用时由外部设置,不保存在享元对象中,即使是同一个对象
	//在每一次调用时可以传入不同的外部状态
	public void operation(String extrinsicState){
		//实现业务方法
	}
}

在使用享元模式时,有时候还需要处理那些不需要共享的抽象享元类的子类,典型代码如下:

public class UnsharedConcreteFlyweight extends Flyweight{
	public void operation(String extrinsicState){
		//实现业务方法
	}
}

享元工厂类的代码如下:

public class FlyweightFactory{
	//定义一个HashMap用于存储享元对象,实现享元池
	private HashMap flyweights = new HashMap();

	public Flyweight getFlyweight(String key){
		//如果对象存在,则直接从享元池获取
		if(flyweight.containsKey(key)){
			return (Flyweight)flyweight.get(key);
		}
		//如果对象不存在,先创建一个新的对象添加到享元池中,然后返回
		else{
			Flyweight fw = new ConcreteFlyweight();
			flyweights.put(key,fw);
			return fw;
		}
	}
}

六、单纯享元模式与复合享元模式

1、单纯享元模式

在单纯享元模式中所有的具体享元类都是可以共享的,不存在非共享具体享元类。

2、复合享元模式

将一些单纯享元对象使用组合模式加以组合可以形成复合享元对象,这样的复合享元对象本身不能共享,但是它们可以分解成单纯享元对象,而后者可以共享。
通过使用复合享元模式可以让复合享元类CompositeConcreteFlyweight中包含的每个单纯享元类ConcreteFlyweight都具有相同的外部状态,而这些单纯享元的内部状态往往可以不同。

七、享元模式与String类

JDK类库中的String类使用了享元模式。
Java String类这种在修改享元对象时现将原有对象复制一份,然后再新对象上实施修改操作的机制称为“Copy On White"。

八、享元模式优缺点与适用环境

优点

(1)享元模式可以减少内存中对象的数量,使得相同或者相似对象在内存中只保存一份,从而可以节约系统资源,提供系统性能。
(2)享元模式的外部状态相对独立,而且不会影响其内部状态,从而使享元对象可以在不同的环境中被共享。

缺点

(1)享元模式使系统变得复杂,需要分离出内部状态和外部状态,这使得程序的逻辑复杂化。
(2)为了使对象可以共享,享元模式需要将对象的部分状态外部化,而读取外部状态将使运行时间变长。

适用环境

(1)一个系统有大量相同或者相似的对象,造成内存的大量耗费
(2)对象的大部分状态都可以外部化,可以将这些外部状态传入对象中
(3)在使用享元模式时需要维护一个存储享元对象的享元池,而这需要耗费一定的系统资源,因此应当在需要多次重复使用享元对象时才使用享元模式。

以上文字,大量摘抄自《Java设计模式》一书,由刘伟老师编著,故本文应当列入转载一类,有兴趣的朋友可以直接阅读原书。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值