JAVA基础加强 --学习心得八(享元模式) 【非原创】

一:享元模式的用意
享元模式以共享的方法高效地支持大量的细粒度对象,享元对象能做到共享的关键是区分内蕴状态和外蕴状态。
一个内蕴状态是存储在享元对象内部的,并且是不会随环境改变而有所不同的,因此一个享元可以具有内蕴状态并可以共享。一个外蕴状态是随环境的改变 而改变的,不可以共享的状态,享元对象的外蕴状态必须由客户端保存,并在享元对象被创建之后,在需要使用的时候各方面传入到享元对象内部,外蕴状态不可以 影响享元对象的内蕴状态,换句话说,它们是相互独立的.

二:享元模式的应用
享元模式在编辑器系统中大量使用,一个文本编辑器往往会提供很多种字体,而通常的做法就是将每一个字母做成一个享元对象。享元对象的内蕴状态就是 这个字母,而字母在文本中的位置和字体风等其他信息则是外蕴状态,比如字母a可能出现在文本的很多地方,虽然这些字母a的位置和字体风格不同,但是所有这 些地方使用的都是同个字母对象,这样一来,字母对象就可以在整个系统中共享
在Java语言中,String类型就使用了享元模式.String对象是不变对象,一旦创建出来就不能改变,如果需要改变一个字符串的值,就只 好创建一个新的String对象,在JVM内部, String对象都是共享的。如果一个系统中有两个String对象所包含的字符串相同的话,JVM实际上只创建一个String对象提供给两个引用,从 而实现String对象的共享,String的inern()方法给出这个字符串在共享池中的唯一实例.

三:享元模式分为单纯享元模式和复合享元模式
(1)单纯享元模式所涉及的角色如下:
(A)抽象享元角色:此角色是所有的具体享元类的超类,为这些类规定出需要实现的公共接口,那些需要外蕴状态的操作可以通过调用商业方法以参数的形式传入
(B)具体享元(ConcreteFlyWeight)角色:实现抽象享元角色所规定的接口,如果有内蕴状态的话,必须为内蕴状态提供存储空间。享元对象的内蕴状态必须与对象所处的周围环境无关,从而使得享元对象可以在系统内共享.
(C)享元工厂(FlyweightFactory)角色:本角色负责创建和管理享元角色,本角色必须保证享元对象可以被系统适当地共享,当一个 客户端对象调用一个享元对象的时候,享元工厂角色会检查系统中是否已经有一个符合要求的享元对象,如果已经有了,享元工厂就应当提供这个已有的享元对象, 如果系统中没有这个适当的享元对象的话,享元工厂就应当创建一个合适的享元对象.
(D)客户端(Client)角色:本角色需要维护一个对所有享元对象的引用,本角色需要自行存储所有享元对象的外蕴状态.
示意性代码如下:

package cai.milenfan.basic.test; 
//抽象享元角色
public abstract class Flyweight {
//state是外蕴状态
abstract public void operation(String state);
}
package cai.milenfan.basic.test;
//这是一个具体享元类,它有一个内蕴状态,它的值在创建时就赋予并不会再改变
public class ConcreteFlyweight extends Flyweight{
private Character intrinsicState;
public ConcreteFlyweight(Character state){
this.intrinsicState = state;
}
//外蕴状态作为参量传入方法中,改变方法的行为,但是不改变对象的内蕴状态
public void operation(String state) {

}

}


package cai.milenfan.basic.test; 

import java.util.HashMap;

//客户端不可以直接将具体享元类实例化,而必须通过一个对象工厂,享元工厂对象在整个系统中只有一个,因此可以使用单例模式
public class FlyweightFactory {
private HashMap flies = new HashMap();
private Flyweight lnkFlyweight;
public FlyweightFactory(){}
public Flyweight factory(Character state){
if(flies.containsKey(state)){
return (Flyweight)flies.get(state);
}else{
Flyweight fly = new ConcreteFlyweight(state);
flies.put(state, fly);
return fly;
}
}
}


package cai.milenfan.basic.test; 

public class FlyweightTest {
public static void main(String[] args){
//创建一个享元工厂
FlyweightFactory factory = new FlyweightFactory();
//向享元工厂请求一个内蕴状态为'a'的享元对象
Flyweight fly = factory.factory(new Character('a'));
//以参量的方式传入一个外蕴状态
fly.operation("First Call");
//向享元工厂对象请求一个内蕴状态为'b'的享元对象
fly = factory.factory(new Character('b'));
//以参量形式传入一个外蕴状态
fly.operation("Second Call");
//向享元工厂请求一个内蕴状态为'a'的享元对象
fly = factory.factory(new Character('a'));
//以参量的方式传入一个外蕴状态
fly.operation("Third Call");
}
}



//虽然上面申请了三个享元对象,但是实际上只创建了两个享元对象,这就是共享的含义

(2)复合享元模式的结构
一:类图和角色
在上面的单纯的享元模式中,所有的享元对象都是单纯享元对象,也就说都是可以直接共享的。下面考虑一个较为复杂的情况,即将一些单纯享元使用合成 模式加以复合,形成复合享元对象,这样的复合享元对象本身不能共享,但是它们可以分解成单纯享元对象,而后者则可以共享.此复合享元模式涉及到如下角色:
这和单纯享元角色没多大区别,在这里直接给出示意代码:

package cai.milenfan.basic.test; 
//抽象享元角色
public abstract class Flyweight {
//state是外蕴状态
abstract public void operation(String state);
}


package cai.milenfan.basic.test; 
//这是一个具体享元类,它有一个内蕴状态,它的值在创建时就赋予并不会再改变
public class ConcreteFlyweight extends Flyweight{
private Character intrinsicState;
public ConcreteFlyweight(Character state){
this.intrinsicState = state;
}
//外蕴状态作为参量传入方法中,改变方法的行为,但是不改变对象的内蕴状态
public void operation(String state) {
}
}



package cai.milenfan.basic.test; 

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/*这是一个具体复合享元角色的源代码,它有如下两个责任:
* (1)复合享元对象是由单纯的享元对象通过复合而成,因此它提供了add()这样的聚集管理方法,由于一个复合享元对象
*具有不同的聚集元素,这些聚集元素在复合享元对象被创建之后加入,这本身就意味着复合享元对象的状态是会改变的,因
*此复合享元对象是不能共享的
* (2)复合享元角色实现了抽象享元角色所规定的接口,也就是operation()方法,这个方法有一个参量,代表复合享元对象的
*个蕴状态,一个复合享元对象的所有单纯享元对象元素的外蕴状态都是与复合享元对象的外蕴状态相等,而一个复合享元
*对象所含有的单纯享元对象的内蕴状态一般是不相等的,不然就没有使用价值了.
*/
public class ConcreteCompositeFlyweight extends Flyweight{
private HashMap flies = new HashMap(10);
private Flyweight flyweight;
public ConcreteCompositeFlyweight(){}
public void add(Character key,Flyweight fly){
flies.put(key, fly);
}
public void operation(String state) {
Flyweight fly = null;
for(Iterator it = flies.entrySet().iterator();it.hasNext();){
Map.Entry e = (Map.Entry)it.next();
fly = (Flyweight)e.getValue();
fly.operation(state);
}
}
}



package cai.milenfan.basic.test; 

import java.util.HashMap;

//享元工厂在多态性的基础上提供两种不同的方法,一个用于提供单纯的享元对象,另一个用于提供复合享元对象
public class FlyweightFactory {
private HashMap flies = new HashMap();
private Flyweight lnkFlyweight;
public FlyweightFactory(){}
public Flyweight factory(String compositeState){
ConcreteCompositeFlyweight compositeFly = new ConcreteCompositeFlyweight();
int length = compositeState.length();
Character state = null;
for(int i=0;i<length;i++){
state = new Character(compositeState.charAt(i));
System.out.println("factory(" + state + ")");
compositeFly.add(state, this.factory(state));
}
return compositeFly;
}
public Flyweight factory(Character state){
if(flies.containsKey(state)){
return (Flyweight)flies.get(state);
}else{
Flyweight fly = new ConcreteFlyweight(state);
flies.put(state, fly);
return fly;
}
}
}



在使用这个享元模式的时候首先需要创建享元工厂对象,然后向享元工厂对象要求具有某人内蕴状态的单纯享元,或者具有某个组合状态的复合享元,下面是创建复合享元对象的方法:
Flyweight fly = factory.factory("aba");
fly.operation("Composite Call");
上面的程序向享元工厂申请了一个内蕴状态为"aba"的复合享元对象,而且外蕴状态都是"Composite Call"

四:模式的实现525
(A)使用不变模式实现享元角色
享元模式里的享元对象不一定非得是不变对象,但是很多的享元对象确实被设计成了不变对象,由于不变对象的状态在被创建之后就不再变化,因此不变对象满足享元模式对享元对象的要求.
(B)使用单例模式实现享元工厂角色
系统往往中需要一个享元工厂的实例,所以享元工厂可以设计成为单例模式,下面是在单纯的共享模式中使用单例模式实现共享工厂角色的源代码:

package cai.milenfan.basic.test; 

import java.util.HashMap;

public class FlyweightFactorySingleton {
private HashMap flies = new HashMap();
private static FlyweightFactorySingleton myself = new FlyweightFactorySingleton();
private FlyweightFactorySingleton(){};
public static FlyweightFactorySingleton getInstance(){
return myself;
}
public synchronized Flyweight factory(Character state){
//检查具有此状态的享元是否存在
if(flies.containsKey(state)){
return (Flyweight)flies.get(state);
}else{
Flyweight fly = new ConcreteFlyweight(state);
flies.put(state, fly);
return fly;
}
}
}


在复合的共享模式中使用单例模式实现共享工厂角色的源代码在此不给出.还跳过了两个例子.

五:享元模式应当在什么情况下使用
(1)一个系统有大量的对象
(2)这些对象耗费大量的内存
(3)这些对象可以按照内蕴状态分成很多的组,当把外蕴对象从对象中删除时,每一个组都可以仅用一个对象代替
(4)使用享元模式需要维护一个记录了系统已有的所有享元的表,而这需要耗费资源,因此应当在有足够多的享元实现可供共享时才值的使用享元模式.

六:享元模式的优点和缺点
享元模式的优点在于它大幅度地降低内存中的对象的数量,但是也为这一点付出了代价:
(1)为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑复杂化
(2)享元模式将享元对象的状态外部化,而读取外部状态使得运行时间稍微变长.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值