享元
享元模式(Flyweight),直译为轻量级,不过它的意译“享元”似乎更容易被我们大天朝的程序猿理解和接受。
元:元子、单元、元件、基本的元素。
享元:共享基本的单元、元件。以达到提升已有元件利用率、减少重复制造元件的开销。
所以,享元模式是一种面向对象的结构设计模式,通过共享的方式有效的支持大量的细粒度、元子级的对象。Java中String的资源池、数据库的数据池和其他一些类似的缓冲池都是享元模式的一些具体应用。
享元在生活中
与其说是一种软件开发的设计模式,不如说这是一种生活工作中的思维方式。软件开发中,可以通过这种共享来节省系统和内存的开销。在实际生活中,也会有很多运用这种享元思维来节省成本和精力的开销。
从一开始接触到享元模式的时候,脑海里就总是会想到印刷术的老祖宗,中国的四大发明之一——活字印刷术。
活字印刷的发明是印刷史上一次伟大的技术革命,是一种印刷方法,通过使用可以移动的金属或胶泥字块,来取代传统的抄写,或是无法重复使用的印刷版。活字印刷的方法是先制成单字的阳文反文字模,然后按照稿件把单字挑选出来,排列在字盘内,涂墨印刷,印完后再将字模拆出,留待下次排印时再次使用。
活字印刷术主要通过对活字模的重复使用,减少了雕刻雕版时的大量重复工作,在提高了效率的同时减少了成本。我们大天朝的祖先在一千年前的活字印刷思想中就体现出了享元思维,制作新的字模、储存和保管已用字模、通过字模的重复利用来印刷不同的文章。
享元在代码里
上面主要还是解释了一下享元的思想和用途,而在代码中能做到实现这种共享的关键就是对内蕴状态(Internal State)和外蕴状态(External State)的区分。
内蕴状态:享元内部,与环境无关,可共享。
外蕴状态:外部保存,与环境有关,不可共享。
以下是享元模式规定的几种对象角色:
抽象享元(Flyweight)角色:给出一个抽象接口,以规定出所有具体享元角色需要实现的方法。
具体享元(ConcreteFlyweight)角色:实现抽象享元角色所规定出的接口。如果有内蕴状态的话,必须负责为内蕴状态提供存储空间。
享元工厂(FlyweightFactory)角色 :本角色负责创建和管理享元角色。
下面,就用之前说的活字印刷来演示一下享元模式。首先我们需要一个抽象享元接口的角色,我们有一个类来表示抽象的字,代码如下。
public interface Word {
//示意性的印刷方法,
public void print(String externalState);
}
然后是具体的享元角色,用一个类来表示活字模,代码如下
public class Typehead implements Word{
private String internalState;
public Typehead(String internalState){
this.internalState = internalState;
}
@Override
public void print(String externalState) {
System.out.println("这个字是:" + this.internalState);
System.out.println("这个字的位置:" + externalState);
}
}
最后是创建和维护享元对象的工厂角色,负责维护已有字模和创建不存在的字模,代码如下。
public class TypeheadFactory {
private Hashtable<String, Typehead> resourcePool = new Hashtable<String, Typehead>();
public Typehead factory(String internalState)
{
/*
* 如果资源池没有该元,就实例化一个放入池中
*/
if(!resourcePool.containsKey(internalState)){
resourcePool.put(internalState, new Typehead(internalState));
}
return resourcePool.get(internalState);
}
/**
* 查看当前资源池中字模数量
*/
public void printTypeCounts(){
System.out.println("当前字模数量:" + this.resourcePool.size());
}
}
最后是测试类,代码如下:
public class Test {
public static void main(String[] args) {
TypeheadFactory factory = new TypeheadFactory();
Typehead typehead1 = factory.factory("王");
factory.printTypeCounts();
typehead1.print("第一行,第三列");
Typehead typehead2 = factory.factory("李");
factory.printTypeCounts();
typehead2.print("第二行,第四列");
Typehead typehead3 = factory.factory("王");
factory.printTypeCounts();
typehead3.print("第四行,第三列");
}
}
最后结果虽然有三个字模对象,可是在资源池中却只有两个对象需要维护。
总结
我们看到了享元模式带来的优点,通过共享,减少了资源的使用。同时也看到了这个模式的缺点,增加了系统的复杂程度。作为一种思维,我觉得还是对自己有很多帮助的。可是作为一种软件开发的设计模式,我觉得使用之前要考虑清楚利弊,这样做究竟是不是值得!