定义:对象结构型模式运用共享技术有效地支持大量细粒度的对象。
在面向对象编程中,有时候应用中创建的对象过多,导致存储空间的不必要的浪费(一部分属性是很多对象共享的,另一部分是每个对象根据自己的使用情况独有的,但是每个对象都将所有的属性全部进行创建,这样即使是可共享的属性,也都每个对象都有自己单独的,就造成了资源的浪费)
举个例子:
一个字符有颜色、使用位置两对象性,
class char{
private Color color;//颜色
private Position position;//位置
}
位置需要根据实际使用情况进行设置,属于不可共享的,而颜色(假如系统只提供了红色和蓝色)属于共享的,
如果不使用享元模式,假如有1000个字符,这样就要创建1000个颜色对象和1000个位置对象,生成了1000个字符对象,好浪费
使用享元模式,将位置对象的属性去掉,并创建一个方法
class char{
private Color color;//颜色
public void operation(Position position){}//位置
}
创建一个颜色工厂,只存储了两个颜色对象,使用以下代码进行创建字符
char c1=factory.getChar("红色");
c.operation(position1);
char c2=factory.getChar("蓝色");
c.operation(position2);
char c3=factory.getChar("红色");
c.operation(position3);
....
这样,1000个字符其实只创建了2个字符对象,位置在真正使用的时候作为参数传入给字符的方法,相当于一个字符对象在使用时根据实际情况,不停的更换自己的位置
享元模式关键在于区分内蕴状态(color)和外蕴状态(position)
在享元对象内部并且不会随环境改变而改变的共享部分为内蕴状态,而随环境改变而改变的、不可以共享的状态就是外蕴状态。
进入正题:
享元模式的结构分为单纯享元模式和复合享元模式
单纯享元模式:
(1) 抽象享元角色(Flyweight):为具体享元角色规定了必须实现的方法,也是每个对象独有的属性对象进入的入口(当然外蕴状态不是必须的,如果需要的话就定义),以参数的形式通过此方法传入。在java中可以由抽象类、接口来担当。
public interface Flyweight{
public void operation( Out state );//必要的实现方法,Out为外部状态,不是必须的,用来接收每个对象独有的属性(有的话)
}
public class ConcreteFlyweight implements Flyweight {
private In state;//内部状态
public ConcreteFlyweight(In state){
this.state=state;
}
public void operation( Out state ){
//具体操作
}
}
public class FlyweightFactory { //一个Flyweight内存池,存储内部状态
//Flyweight pool
private HashMap flyweights = new HashMap();
public Flyweight getFlyweight( In state ) {
Flyweight flyweight = (Flyweight) flyweights.get(state);
if( flyweight == null ) {
//产生新的ConcreteFlyweight
flyweight = new ConcreteFlyweight(state);
flyweights.put( state, flyweight );
}
return flyweight;
}
}
//Client:
FlyweightFactory factory = new FlyweightFactory();
Flyweight fly = factory.getFlyweight( inState );
fly.operation(outState);
适用性
举一个完整的例子结束: 咖啡屋系统1)一个应用程序使用大量相同或者相似的对象,造成很大的存储开销。
2)对象的大部分状态都可以外部化,可以将这些外部状态传入对象中。
3)如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象。
4) 应用程序不依赖于对象标识。由于Flyweight对象可以被共享,对于概念上明显有别的对象,标识测试将返回真值。
5)使用享元模式需要维护一个存储享元对象的享元池,而这需要耗费资源,因此,应当在多次重复使用享元对象时才值得使用享元模式
系统要求:
有一家咖啡屋,有不同口味的咖啡,顾客购买后,服务员会将咖啡送到顾客相应的桌位上
系统分析:
定义一个订单对象,包含口味、购买顾客的桌位号两个属性,而口味是固定的几种,不受环境的影响,可作为内部状态,可被所有订单共享,而桌位号是根据顾客来定的,受环境影响,可作为外部状态,不可被共享,这样就把内部状态和外部状态区分开,使用享元模式
系统设计:
interface Order{
public void serve(Table table);
}
class Flavor implement Order{
private String flavor;
public Flavor(String flavor){
this.flavor=flavor;
}
public void serve(Table table){
System.out.printlnn("Serving table"+table.getNumber()+"with flavor"+flavor);
}
}
class FlavorFactory{
private HashMap flavors = new HashMap();
public Order getOrder(String flavor) {
Order order = (Order) flavors.get(flavor);
if( order == null ) {
order = new Flavor(flavor);
flavors.put(flavor,order);
}
return order;
}
}
class Table{
private int number;
public int getNumber(){return number};
public void setNumber(int number){this.number=number};
}
public class Client{
FlavorFactory flavorFactory;
public static void main(Stirng[] args){
flavorFactory=new FlavorFactory();
Order order1=flavorFactory.get("Black Coffee");
Order order2=flavorFactory.get("Capucino");
Order order3=flavorFactory.get("Black Coffee");
Order order4=flavorFactory.get("Espresson");
order1.serve(new Table(1));
order2.serve(new Table(2));
order3.serve(new Table(3));
order4.serve(new Table(4));
}
}
(1) 抽象享元角色(Flyweight):Order
(2) 具体享元角色(ConcreteFlyweight):Flavor
(4) 客户端角色(Client):Client
复合享元模式:
复合享元角色(UnsharedConcreteFlyweight),其所代表的对象是不可以共享的,但是一个复合享元对象可以分解成为多个本身是单纯享元对象的组合,复合享元角色又称做不可共享的享元对象
复合享元对象是由单纯享元对象通过复合而成,因此它提供了add()这样的聚集管理方法。由于一个复合享元对象具有不同的聚集元素,这些聚集元素在符合享元对象被创建之后加入,这本身就意味着复合享元对象的状态是会改变的,因此复合享元对象是不能共享的
复合享元角色实现了抽象享元角色所规定的接口,也就是operation(),这个方法有一个参量,代表复合享元对象的外蕴状态。一个复合享元对象的所有单纯享元对象元素的外蕴状态都是与复合享元对象的外蕴状态相等的;而一个复合享元对象所含有的单纯享元对象的内蕴状态一般是不等的
public interface Flyweight{
public void operation( Out state );//必要的实现方法,Out为外部状态,不是必须的,用来接收每个对象独有的属性(有的话)
}
public class ConcreteFlyweight implements Flyweight {
private In state;//内蕴状态
public ConcreteFlyweight(In state){
this.state=state;
}
public void operation( Out state ){
//具体操作
}
}
public class UnsharedConcreteFlyweight extends Flyweight{
private HashMap flies=new HashMap();
private Flyweight flyweight;
public UnsharedConcreteFlyweight(){}
//增加一个新的单纯享元对象到聚集中
public void add(In state,Flyweight fly){
files.put(state,fly);
}
public void operation(Out state){
Flyweight fly=null;
for(Iterator it=files.entrySet().iterator();it.hasNext()){
Map.Entry e=(Map.Entry)it.next();
fly=(Flyweight)e.getValue();
fly.operation(state);
}
}
}
public class FlyweightFactory {
//Flyweight pool
private HashMap flyweights = new HashMap();
//复合享元工厂
public Flyweight getUnsharedFlyweight(Vector states){
UnsharedConcreteFlyweight ucfly=new UnsharedConcreteFlyweight();
int length=states.size();
In state=null;
for(int i=0;i<length;i++){
state=new In(states.get(i));
ucfly.add(state,this.getFlyweight(state));
}
return ucfly;
}
public Flyweight getFlyweight( In state ) {
Flyweight flyweight = (Flyweight) flyweights.get(state);
if( flyweight == null ) {
//产生新的ConcreteFlyweight
flyweight = new ConcreteFlyweight(state);
flyweights.put( state, flyweight );
}
return flyweight;
}
}
FlyweightFactory factory = new FlyweightFactory();
Flyweight fly = factory.getUnsharedFlyweight(states);
fly.operation(outState);
复合享元角色是单纯享元 角色的聚集,所包含的单纯享元角色,其外蕴状态都是一致的,内蕴状态不一致,
拿上面的咖啡屋系统举例,当同一个桌号的客人点了多个风味的咖啡时,此时外蕴状态是一致的,如果我们仍使用单纯享元模式的话:
Order order1=flavorFactory.get("Black Coffee");
Order order2=flavorFactory.get("Capucino");
Order order3=flavorFactory.get("Black Coffee");
Order order4=flavorFactory.get("Espresson");
order1.serve(new Table(1));
order2.serve(new Table(1));
order3.serve(new Table(1));
order4.serve(new Table(1));
写起来很不方便,如果使用复合享元模式的话,就方便很多:
Vector flavors=new Vector();
flavors.add("Black Coffee");
flavors.add("Capucino");
flavors.add("Black Coffee");
flavors.add("Espresson");
Order order=flavorFactory.getComposite(flavors);
order.serve(new Table(1));