23个java设计模式(二十)-- 享元模式

2016年03月28日 原创
关键词: java 设计模式
摘要 享元模式运用共享技术有效地支持大量细粒度的对象。

一、概述。

享元模式运用共享技术有效地支持大量细粒度的对象。

一个类中的成员变量表明该类所创建对象所具有的属性,在某些程序设计中可能用一个类创建若干个对象,但是发现这些对象的一个共同特点是它们有一部分属性的取值必须是完全相同的,此时就可以使用享元模式来减少重复的属性。

享元模式的关键是使用一个称作享元的对象为其他对象提供共享的状态,而且能够保证使用享元的对象不能更改享元中的数据。从享元的角度看,享元所维护的数据习惯上称作享元的内部状态,而使用享元的对象或应用程序中所维护的其他数据称作享元的外部状态,外部状态往往具有不可预见性,可能需要动态的计算来确定。

二、享元模式的结构。

享元模式包括三种角色:

  • 享元接口(Flyweight):是一个接口,该接口定义了享元对外公开其内部数据的方法,以及享元接收外部数据的方法。
  • 具体享元(Concrete Flyweight):实现享元接口的类,该类的实例称为享元。具体享元类的成员变量为享元的内部状态,享元对象的内部状态必须与所处的环境无关。因为享元对象是用来共享的,所以不能允许用户各自地使用具体享元类来创建对象。创建和管理享元对象由享元工厂负责。
  • 享元工厂(Flyweight Factory):享元工厂是一个类,该类的实例负责创建和管理享元对象,用户或其他对象必须请求享元工厂为它得到一个享元对象。享元工厂可以通过一个散列表(共享池)来管理享元对象。可以使用单例模式来设计享元工厂。

三、示例程序。

享元接口(Flyweight)

Flyweight.java

public interface Flyweight {
	double getHeight();
	double getWidth();
	double getLength();
	void printMsg(String msg);
}

享元工厂(Flyweight Factory)

FlyweightFactory.java

public class FlyweightFactory {
	private HashMap<String,Flyweight> hash;
	private static FlyweightFactory factory = new FlyweightFactory();
	private FlyweightFactory() {
		hash = new HashMap<>();
	}
	public static FlyweightFactory getFactory() {
		return factory;
	}
	public synchronized Flyweight getFlyweight(String key) {
		if(hash.containsKey(key))
			return hash.get(key);
		double width = 0, height = 0, length = 0;
		String[] str = key.split("#");
		width = Double.parseDouble(str[0]);
		height = Double.parseDouble(str[1]);
		length = Double.parseDouble(str[2]);
		Flyweight fw = new ConcreteFlyweight(width,height,length);
		hash.put(key, fw);
		return fw;
	}
}

具体享元(Concrete Flyweight)

ConcreteFlyweight.java

public class ConcreteFlyweight implements Flyweight{
	private double width;
	private double height;
	private double length;
	
	public ConcreteFlyweight(double width, double height, double length) {
		this.width = width;
		this.height = height;
		this.length = length;
	}

	@Override
	public double getHeight() {
		return height;
	}

	@Override
	public double getWidth() {
		return width;
	}

	@Override
	public double getLength() {
		return length;
	}

	@Override
	public void printMsg(String msg) {
		System.out.print(msg);
		System.out.print(" 宽度:"+width);
		System.out.print(" 高度:"+height);
		System.out.println(" 长度:"+length);
	}

}

测试程序

Car.java

public class Car {
	private Flyweight flyweight;
	private String name,color;
	private int power;
	public Car(Flyweight flyweight, String name, String color, int power) {
		this.flyweight = flyweight;
		this.name = name;
		this.color = color;
		this.power = power;
	}
	public void print() {
		System.out.print(" 名称:"+name);
		System.out.print(" 颜色:"+color);
		System.out.print(" 功率:"+power);
		System.out.print(" 宽度:"+flyweight.getWidth());
		System.out.print(" 高度:"+flyweight.getHeight());
		System.out.println(" 长度:"+flyweight.getLength());
	}
}

Example1Test.java

public class Example1Test {
	@Test
	public void testMain() {
		FlyweightFactory factory = FlyweightFactory.getFactory();
		double width = 1.82, height = 1.47, length = 5.12;
		String key = width + "#" + height + "#" + length;
		Flyweight flyweight = factory.getFlyweight(key);
		Car audiA60One = new Car(flyweight, "奥迪A6", "黑色", 128);
		Car audiA60Two = new Car(flyweight, "奥迪A6", "灰色", 160);
		audiA60One.print();
		audiA60Two.print();
		width = 1.77;
		height = 1.45;
		length = 4.63;
		key = width + "#" + height + "#" + length;
		flyweight = factory.getFlyweight(key);
		flyweight.printMsg(" 名称: 奥迪A4 颜色:蓝色 功率:126");
		flyweight.printMsg(" 名称:奥迪A4 颜色:红色 功率:138");
	}
}

四、享元模式的优点。

  1. 使用享元可以节省内存的开销,特别适合处理大量细粒度对象,这些对象的许多属性值是相同的,而且一旦创建则不容许修改。
  2. 享元模式中的享元可以使用方法的参数接受外部状态中的数据,但外部数据不会干扰到享元中的内部数据,这就使享元可以在不同的环境中被共享。

五、适合享元模式的情景。

  1. 一个应用程序使用大量的对象,这些对象之间部分属性本质上是相同的,这时应使用享元来封装相同的部分。
  2. 对象的多数状态都可以变为外部状态,就可以考虑将这样的对象作为系统中的享元来使用。