如果一个程序需要使用大量对象,但这些对象中有好多重复的对象,如果对其进行重复的创建对象,那么必然会造成很大的开销,其中这里面有很大一部分开销是不必要的。我们之前讲单例模式的时候,讲过单例模式就是严格控制单个进程只有一个实例对象,而且对于该实例对象不重复创建,这样可以节省系统开销。其实,本文提到的享元模式也是使用同样的思想,使得创建过对象不再重复创建,并且在使用过程中创建有需要的对象,如果对象已经创建过只需要直接取出,如果没有则进行创建并存储。说到底享元模式其实更像一个创建单例的简单工厂。
1.适用性和优缺点
1.适用性
当具备下列情况时,使用享元模式:
a.一个应用程序使用了大量的对象。
b.完全由于使用了大量的对象,造成很大的存储开销。
c.对象的大多数状态都可变为外部状态。
d.如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。
e.应用程序不依赖于对象标识。由于享元对象可以被共享,对于概念上明显有别的对象,标识测试将返回真值。
f.当一个类有许多的实例,而这些实例能被同一个方法控制的时候。
g.ThreadPool、线程池和数据库连接池都有使用享元模式。
2.优点
a.减少运行时对象实例的个数,节省内存。
b.将许多“虚拟对象”的状态集中管理。
3.缺点
一旦实现了它,那么单个的逻辑实例将无法拥有独立而不同的行为。
2.示例讲解
Flyweight:描述一个接口,通过这个接口flyweight可以接受并作用于外部状态。
FlyweightImpl:实现Flyweight接口,并为内部状态(如果有的话)增加存储空间。FlyweightImpl对象必须是共享的。
FlyweightFactory:创建并管理flyweight对象。确保合理地共享flyweight。当用户请求一个flyweight时,FlyweightFactory对象提供一个已创建的实例或者创建一个(如果不存在的话)。类似一个受单例模式约束的简单工厂。
首先建立一个享元接口,有一个内部函数可以接受外部状态。
package flyweightpattern;
public interface Flyweight {
void action(int arg);
}
一个享元实现类,实现方法
package flyweightpattern;
public class FlyweightImpl implements Flyweight {
String msg;
public FlyweightImpl(String msg)
{
this.msg=msg;
}
public void action(int arg) {
// TODO Auto-generated method stub
System.out.println(msg+"的参数值: " + arg);
}
}
然后是享元工厂,负责创建享元对象和管理享元对象,其中flyweights是一个Map集合,getFlyweight方法是一个工厂方法,如果在flyweights找到对象,则不再创建,直接从集合中取出即可,如果没有目标对象,则进行创建享元对象并存储。其中getFlyweight对象传入的参数,是flyweights集合的key值,也是创建享元对象的参数,也比较容易区分对象是否相同。
package flyweightpattern;
import java.util.*;
public class FlyweightFactory {
private static Map<String,Flyweight> flyweights = new HashMap();
public FlyweightFactory(String arg) {
flyweights.put(arg, new FlyweightImpl(arg));
}
public static Flyweight getFlyweight(String key) {
if (flyweights.get(key) == null) {
flyweights.put(key, new FlyweightImpl(key));
}
return flyweights.get(key);
}
public static int getSize() {
return flyweights.size();
}
}
测试程序,
package flyweightpattern;
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Flyweight fly1 = FlyweightFactory.getFlyweight("a");
fly1.action(1);
Flyweight fly2 = FlyweightFactory.getFlyweight("a");
System.out.println(fly1 == fly2);
Flyweight fly3 = FlyweightFactory.getFlyweight("b");
fly3.action(2);
Flyweight fly4 = FlyweightFactory.getFlyweight("c");
fly4.action(3);
Flyweight fly5 = FlyweightFactory.getFlyweight("d");
fly4.action(4);
fly5.action(5);
System.out.println(FlyweightFactory.getSize());
}
}
运行结果:
a的参数值: 1
true
b的参数值: 2
c的参数值: 3
c的参数值: 4
d的参数值: 5
4
根据上述例子可以更加清楚地看出,享元模式类似于一个要求更加严格的简单工厂模式,要求它的工厂方法符合单例的要求,即创建过的对象不再重复创建。
3.与其它模式的比较
1.享元模式与单例模式
单例模式是严格控制单个进程中只有一个实例对象,是自身控制,需要增加不属于该对象本身的逻辑。而享元模式可以通过自己实现对外部的单例,也可以在需要的时候使用创建更加的对象,享元模式的逻辑判断都交给工厂类进行判断。
2.享元模式与简单工厂模式
简单工厂模式只负责创建对象,而享元模式还包含对对象的存储和管理。