`

四十:享元模式

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

二:享元模式的应用
享元模式在编辑器系统中大量使用,一个文本编辑器往往会提供很多种字体,而通常的做法就是将每一个字母做成一个享元对象。享元对象的内蕴状态就是这个字母,而字母在文本中的位置和字体风等其他信息则是外蕴状态,比如字母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)享元模式将享元对象的状态外部化,而读取外部状态使得运行时间稍微变长.
分享到:
评论

相关推荐

    Head First 设计模式 JAVA源码

    第十一讲:享元模式 第十二讲:代理模式 第十三讲:外观模式 第十四讲:组合模式 第十五讲:桥接模式 第十六讲:适配器模式 第十七讲:解释器模式 第十八讲:中介者模式 第十九讲:职责链模式 第二十讲:...

    第十四课 享元模式1

    享元模式是一种优化性能的结构性设计模式,主要解决在程序中大量相似对象导致的内存消耗问题。通过将对象的状态划分为内部状态和外部状态,享元模式能够减少实际需要创建的实例数量,从而提高系统效率。 内部状态是...

    二十四种设计模式全实现_含图解

    12. **享元模式**:享元模式运用共享技术有效地支持大量细粒度的对象,通过共享相同的对象以减少内存使用。 13. **外观模式**:外观模式提供了一个统一的接口,用来访问子系统中的一组接口。外观定义了一个高层接口...

    二十二种设计模式.pdf

    十、享元模式 十一、代理模式 十二、命令模式 十三、责任链模式 十四、解释器模式 十五、迭代器模式 十六、备忘录模式 十七、中介者模式 十八、状态模式 十九、观察者模式 二十、策略模式 二十一、模板方法模式 二十...

    设计模式PPT.rar

    02第二讲工厂方法模式 03第三讲抽象工厂模式 04第四讲工厂模式在开发中的运用 05第五讲单例模式 06第六讲原型模式 07第七讲建造者模式 08第八讲装饰模式 09第九讲策略模式 10第十讲观察者模式 11第十一讲享元模式 ...

    guide_to_php_design_pattern_zh.pdf

    - 第十三章:享元模式 - 第十四章:策略模式 - 第十五章:模板方法模式 - 第十六章:迭代器模式 - 第十七章:观察者模式 - 第十八章:命令模式 - **翻译团队**:本书的中文版由PHPChina成员翻译完成,各章节...

    C++设计模式

    (一)简单工厂模式 2 (二)策略模式 4 策略与工厂结合 6 单一职责原则 6 开放――封闭原则 6 里氏代换原则 7 依赖倒转原则 7 ...(二十二)享元模式 56 (二十三)解释器模式 59 (二十四)访问者模式 61

    二十三种设计模式小手册

    这些模式是由Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides四位大师,也被称为Gang of Four (GOF),在他们的经典著作《设计模式:可复用面向对象软件的基础》中提出的。本小手册详细阐述了这二十三种...

    java设计模式解释

    享元模式是一种结构型设计模式,它运用共享技术来有效地支持大量细粒度的对象。 **应用场景:** 当需要大量相似对象,使用享元模式可以显著减少内存中对象的数量。例如,在大规模的图形编辑器中,相同的图标可以被...

    二十三种设计模式Gof 代码

    GOF(Gang of Four)设计模式是由Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides四位作者在《设计模式:可复用面向对象软件的基础》一书中提出的,共包含了23种经典的设计模式。这些模式为软件开发提供...

    GOF-设计模式-Design Patterns-英文原版-高清-有目录-有页码

    - **享元**(Flyweight):运用共享技术有效地支持大量细粒度的对象。 - **代理**(Proxy):为其他对象提供一种代理以控制对这个对象的访问。 3. **行为型模式**: - **责任链**(Chain of Responsibility):避免...

    二十三种设计模式【PDF版】

    设计模式之 Flyweight(共享元) 提供 Java运行性能,降低小而大量重复的类的开销. C. 行为模式 设计模式之 Command(命令) 什么是将行为封装,Command 是最好的说明. 设计模式之 Observer(观察者) 介绍如何使用 ...

    设计模式C#版

    享元模式是一种结构型设计模式,它通过共享尽可能多的相似对象来减少内存使用或计算开销。享元模式适用于大量细粒度对象的管理,特别是那些在程序中大量重复出现的对象。 通过上述的解释,我们可以看出C#设计模式是...

    Mybatis 框架源码10种设计模式分析.pdf

    文章接着介绍了 Mybatis 框架源码中的 10 种设计模式应用,包括工厂模式、单例模式、原型模式、建造者模式、代理模式、适配器模式、桥模式、组合模式、享元模式和解释器模式。这些设计模式的应用能够帮助读者更好地...

    设计模式:可复用面向对象对象软件的基础

    十、享元模式(Flyweight) 享元模式通过共享大量细粒度对象,有效地支持大量对象的使用。在C++中,享元模式常用于节省内存,提高性能,特别是在处理大量类似对象时。 十一、代理模式(Proxy) 代理模式为其他对象...

    设计模式PPT合集

    享元模式运用共享技术有效支持大量细粒度的对象,通过共享相同的实例来减少内存使用。 十三、状态模式 状态模式允许对象在其内部状态改变时改变其行为,对象看起来似乎修改了它的类。 十四、策略模式 策略模式定义...

    C# 设计模式(用常用的设计模式编写C#程序)

    十四、享元模式 享元模式在系统中存储大量相似对象时,通过共享已存在的对象减少内存使用。C#中的缓存机制可以用来实现享元模式。 十五、桥接模式 桥接模式将抽象部分与实现部分分离,使它们可以独立变化。C#中的...

    二十三种设计模式 PDF

    - **Flyweight(享元)**:运用共享技术有效地支持大量细粒度的对象。 ##### 2.3 行为型模式 - **Command(命令)**:将请求封装为一个对象,从而使你可用不同的请求对客户进行参数化。 - **Observer(观察者)**:...

    Java中的23种设计模式

    **定义**: 享元模式通过共享技术有效地支持大量细粒度的对象。 **优点**: - 大幅度减少对象的数量; - 提高系统的性能。 **应用场景**: - 系统中有大量相似的对象; - 对象的大部分状态可以外部化。 #### 十二、...

    Java设计模式2

    #### 十四、模板方法模式与访问者模式 - **模板方法模式**:定义一个操作中的算法骨架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 - **访问者模式**:表示一个...

Global site tag (gtag.js) - Google Analytics