享元模式:
先让我们来看一个应用场景:
比如说一个文本系统,每个字母定一个对象,那么大小写字母一共就是52个,那么就要定义52个对象。如果有一个1M的文本,那么字母是何其的多,如果每个字母都定义一个对象那么内存早就爆了。那么如果要是每个字母都共享一个对象,那么就大大节约了资源。也就是说在一个系统中如果有多个相同的对象,那么只共享一份就可以了,不必每个都去实例化一个对象。
在Flyweight模式中,由于要产生各种各样的对象,所以在Flyweight(享元)模式中常出现Factory模式。Flyweight的内部状态是用来共享的,Flyweight factory负责维护一个对象存储池(Flyweight Pool)来存放内部状态的对象。Flyweight模式是一个提高程序效率和性能的模式,会大大加快程序的运行速度.应用场合很多,下面举个例子:
图1享元模式(Flyweight)结构图
享元模式(Flyweight):运用共享的技术有效地支持大量细粒度的对象。
抽象享元角色(Flyweight):此角色是所有的具体享元类的超类,为这些类规定出需要实现的公共接口或抽象类。那些需要外部状态(External State)的操作可以通过方法的参数传入。抽象享元的接口使得享元变得可能,但是并不强制子类实行共享,因此并非所有的享元对象都是可以共享的。
具体享元(ConcreteFlyweight)角色:实现抽象享元角色所规定的接口。如果有内部状态的话,必须负责为内部状态提供存储空间。享元对象的内部状态必须与对象所处的周围环境无关,从而使得享元对象可以在系统内共享。有时候具体享元角色又叫做单纯具体享元角色,因为复合享元角色是由单纯具体享元角色通过复合而成的。
复合享元(UnsharableFlyweight)角色:复合享元角色所代表的对象是不可以共享的,但是一个复合享元对象可以分解成为多个本身是单纯享元对象的组合。复合享元角色又称做不可共享的享元对象。这个角色一般很少使用。
享元工厂(FlyweightFactoiy)角色:本角色负责创建和管理享元角色。本角色必须保证享元对象可以被系统适当地共享。当一个客户端对象请求一个享元对象的时候,享元工厂角色需要检查系统中是否已经有一个符合要求的享元对象,如果已经有了,享元工厂角色就应当提供这个已有的享元对象;如果系统中没有一个适当的享元对象的话,享元工厂角色就应当创建一个新的合适的享元对象。
客户端(Client)角色:本角色还需要自行存储所有享元对象的外部状态。
内部状态与外部状态:在享元对象内部并且不会随着环境改变而改变的共享部分,可以称之为享元对象的内部状态,反之随着环境改变而改变的,不可共享的状态称之为外部状态。
现在让我们通过一个面向对象的文本编辑器设计来说明享元模式的应用。假设我们要设计一个文本编辑器,而且它必须创建字符对象来表示文档中的每个字符,现在让我们考虑字符对象保持什么信息呢?如:字体、字体大小和位置等等信息。
一个文档通常包含许多字符对象,它们需要大容量的内存。值得我们注意的是一般字符都是由数字、字母和其他字符组成的(它们是固定的,可知的),这些字符对象可以共享字体和字体大小等信息,现在它们专有属性只剩下位置了,每个字符对象只需保持它们在文档中的位置就OK了,通过分析我们已经降低了编辑器的内存需求。
图2享元模式(Flyweight)共享对象
我们来看如下代码:
package designpatterns; import java.util.HashMap; /* * 享元模式 */ //先定义一个抽象的FlyWeightAbstract abstract class FlyWeightAbstract{ public abstract void operation(); } //实现两个具体的类 class ConcreteFlyWeight1 extends FlyWeightAbstract { private String myString; public ConcreteFlyWeight1(String myString) { this.myString = myString; System.out.println("创建ConcreteFlyWeight1对象"); } @Override public void operation() { System.out.println("Concrete1....FlyWeight" + myString); } } class ConcreteFlyWeight2 extends FlyWeightAbstract { private String myString; public ConcreteFlyWeight2(String myString) { this.myString = myString; System.out.println("创建ConcreteFlyWeight2对象"); } @Override public void operation() { System.out.println("Concrete2....FlyWeight" + myString); } } //定义一个工厂进行对象的创建 class FlyweightFactory { private HashMap<String, Object> flyweights = new HashMap<String, Object>(); public FlyweightFactory() { } public FlyWeightAbstract GetFileweight(String strObjName) { FlyWeightAbstract flyweight = (FlyWeightAbstract)flyweights.get(strObjName); if(flyweight == null) { if(strObjName.equals("ConcreteFlyWeight1")){ flyweight = new ConcreteFlyWeight1(strObjName); } else if(strObjName.equals("ConcreteFlyWeight2")){ flyweight = new ConcreteFlyWeight2(strObjName); } flyweights.put(strObjName, flyweight); } return flyweight; } } public class Flyweight { public static void main(String[] args) { FlyweightFactory MyFactory = new FlyweightFactory(); MyFactory.GetFileweight("ConcreteFlyWeight1").operation();; MyFactory.GetFileweight("ConcreteFlyWeight2").operation(); MyFactory.GetFileweight("ConcreteFlyWeight1").operation(); MyFactory.GetFileweight("ConcreteFlyWeight1").operation(); MyFactory.GetFileweight("ConcreteFlyWeight1").operation(); MyFactory.GetFileweight("ConcreteFlyWeight2").operation(); } }
代码的运行效果如下:
创建ConcreteFlyWeight1对象
Concrete1....FlyWeightConcreteFlyWeight1
创建ConcreteFlyWeight2对象
Concrete2....FlyWeightConcreteFlyWeight2
Concrete1....FlyWeightConcreteFlyWeight1
Concrete1....FlyWeightConcreteFlyWeight1
Concrete1....FlyWeightConcreteFlyWeight1
Concrete2....FlyWeightConcreteFlyWeight2
核心总结,可以共享的对象,也就是说返回的同一类型的对象其实是同一实例,当客户端要求生成一个对象时,工厂会检测是否存在此对象的实例,如果存在那么直接返回此对象实例,如果不存在就创建一个并保存起来,这点有些单例模式的意思。通常工厂类会有一个集合类型的成员变量来用以保存对象,如hashtable,vector等。在java中,数据库连接池,线程池等即是用享元模式的应用。