享元模式
运用共享技术有效的支持大量细粒度的对象。
Flyweight Pattern
Use sharing to support large numbers of fine-grained objects efficiently.
类图
模式的结构与使用
享元方法模式的结构中包括三种角色。 + 享元接口(Flyweight):是一个接口,该接口定义了享元对外公开其内部数据的方法,以及享元接收外部数据的方法。 + 具体享元(Concrete Flyweight):实现享元接口的类,该类的实例称为享元对象,或简称享元。具体享元类的成员变量为享元对象的内部状态,享元对象的内部状态必须与所处的周围环境无关,即要保证使用享元对象的应用程序无法更改享元的内部状态,只有这样才能使得享元对象在系统中被共享。因为享元对象是用来共享的,所以不能允许用户各自地使用具体享元类来创建对象,这样就无法达到共享的目的,因为不同用户用具体享元类创建的对象显然是不同的,所以,具体享元类的构造方法必须是private的,其目的是不允许用户程序直接使用具体享元类来创建享元对象,创建和管理享元对象由享元工厂负责。 + 享元工厂(Flyweight Factory):享元工厂是一个类,该类的实例负责创建和管理享元对象,用户或其他对象必须请求享元工厂为它得到一个享元对象。享元工厂可以通过一个散列表(也称共享池)来管理享元对象,当用户程序或其他若干个对象向享元工厂请求一个享元对象时,如果享元工厂的散列表中已有这样的享元对象,享元工厂就提供这个享元对象给请求者,否则就创建一个享元对象添加到散列表中,同时该享元对象提供给请求者。显然,当若干个用户或对象请求享元工厂提供一个享元对象时,第一个用户获得该享元对象的时间可能慢一些,但是后继的用户会较快地获得这个享元对象。可以使用单例模式来设计享元工厂,即让系统中只有一个享元工厂的实例。另外,为了让享元工厂能生成享元对象,需要将具体享元类作为享元工厂的内部类。
简单的例子
Flyweight的抽象类Flyweight.java
package Flyweight;
public interface Flyweight {
public double getHeight();
public double getWidth();
public double getLength();
public void printMess(String mess);
}
Flyweight Factory的实现类FlyweightFactory.java
package Flyweight;
import java.util.HashMap;
public class FlyweightFactory {
private HashMap<String, Flyweight> hashMap;
static FlyweightFactory factory =
new FlyweightFactory();
public FlyweightFactory() {
this.hashMap =
new HashMap<String, Flyweight>();
}
public static FlyweightFactory
getFactory() {
return factory;
}
public synchronized Flyweight
getFlyweight(String key) {
if (hashMap.containsKey(key)) {
return hashMap.get(key);
}
else {
double width =
0,height =
0,lenght =
0;
String[] str = key.split(
"#");
width = Double.parseDouble(str[
0]);
height = Double.parseDouble(str[
1]);
lenght = Double.parseDouble(str[
2]);
Flyweight ft =
new ConcreteFlyweight(width, height, lenght);
hashMap.put(key, ft);
return ft;
}
}
class ConcreteFlyweight implements Flyweight {
private double width;
private double height;
private double lenght;
public ConcreteFlyweight(
double width,
double height,
double lenght) {
this.width = width;
this.height = height;
this.lenght = lenght;
}
@Override
public double getHeight() {
return height;
}
@Override
public double getWidth() {
return width;
}
@Override
public double getLength() {
return lenght;
}
@Override
public void printMess(String mess) {
System.out.println(mess);
System.out.print(
"宽度:" + width);
System.out.print(
"高度:" + height);
System.out.print(
"长度:" + lenght);
}
}
}
需要使用共享数据的类Car.java
package Flyweight;
public class Car {
Flyweight flyweight;
String name, color;
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.println(
"名称:" + name);
System.out.println(
"颜色:" + color);
System.out.println(
"功率:" + power);
System.out.println(
"宽度:" + flyweight.getWidth());
System.out.println(
"高度:" + flyweight.getHeight());
System.out.println(
"长度:" + flyweight.getLength());
}
}
测试类Application.java
package Flyweight;
public class Application {
public static void main(String[] args) {
FlyweightFactory factory = FlyweightFactory.getFactory();
double width =
1.82, height =
1.47, lenght =
5.12;
String key =
"" + width +
"#" + height +
"#" + lenght;
Flyweight flyweight = factory.getFlyweight(key);
Car audiA60ne =
new Car(flyweight,
"奥迪A6",
"黑色",
128);
Car audiA6Two =
new Car(flyweight,
"奥迪A6",
"灰色",
160);
audiA60ne.print();
audiA6Two.print();
width =
1.77;
height =
1.45;
lenght =
4.63;
key =
"" + width +
"#" + height +
"#" + lenght;
Car audiA40ne =
new Car(flyweight,
"奥迪A4",
"蓝色",
126);
Car audiA4Two =
new Car(flyweight,
"奥迪A4",
"红色",
138);
flyweight.printMess(
"名称: 奥迪A4 颜色:蓝色 功率:126");
flyweight.printMess(
"名称: 奥迪A4 颜色:红色 功率:138");
}
}
执行结果
享元模式的优点
使用享元模式可以节省内存的开销,特别适合处理
大量细粒度对象,这些对象的许多属性值是相同的,而且一旦创建则不容许修改。享元模式中的享元可以使用方法的参数接收外部状态中的数据,但外部状态数据不会干扰到享元中的内部数据,这就使享元可以在不同的环境中被共享。
适用享元模式的情景
下列情况之一就可以考虑使用享元模式:
一个应用程序使用大量的对象,这些对象之间部分属性本质上是相同的,这时应使用享元来封装相同的部分。对象的多数状态都可变为外部状态,就可以考虑将这些对象作为系统中的享元来使用。
下载源码请到
MyGitHub