<大话设计模式>

本教程说明及版权声明

国士工作室是一支专注于Android平台企业级应用开发的技术团队,致力于做中国最棒的Android应用程序开发机构,提供最棒的Android企业级应用开发培训服务。

企业培训和开发合作官方联系方式:

     电话:18610086859

     Email:hiheartfirst@gmail.com

     QQ:1740415547

国士工作室 有你更美好!

l 该文档参考和使用了网络上的免费开放的图片和内容,并以免费开放的方式发布,希望为移动互联网和智能手机时代贡献绵薄之力!可以随意转载,但不得使用该文档谋利。

l 如果对该文档有任何疑问或者建议,请进入官方博客

   http://www.cnblogs.com/guoshiandroid/留言或者直接与国士工作室联系(后附联系方式),我们会慎重参考您的建议并根据需要对本文档进行修改,以造福更多开发者!

l 《大话设计模式》的最新及完整内容会在国士工作室官方博客定期更新,请访问国士工作室博客

http://www.cnblogs.com/guoshiandroid/获取更多更新内容。

 

享元模式 短信可以这样发 

享元模式应用场景举例 

GG每天给MM至少发一条短信,而且每天入睡前是必有一条短信的,往往是一些琐事和一些比较肉麻的情话。开始的一个月,GG还对此是乐不可支,随着时间的推移,那些肉麻的话说了很多遍,自己也觉得厌烦了,而且更让人不可忍耐的是这些肉麻的情话每次都要重复的输入。GG把这一烦心事告诉了自己的好友K,K说,“你这个大傻瓜,怎么不把一些你常用的话存放在你的手机中,这样,要用的时候,直接拿来用就行了”,傻GG一听,顿时觉得醍醐灌顶,于是立即在手机中存放入了“宝贝儿,晚安喔”、“你是我天使”,“宝贝儿,我永远的爱你”等话语。

享元模式解释: 

享元模式(Flyweight Pattern)是通过使用共享的方式,达到高效地支持大量的细粒度对象。它的目的就是节省占用的空间资源,从而实现系统性能的改善。

享元的英文是Flyweight,它是一个来自于体育方面的专业用语,在拳击、摔跤和举重比赛中特指最轻量的级别。把这个单词移植到软件工程里面,也是用来表示特别小的对象,即细粒度对象。至于为什么我们把Flyweight翻译为“享元”,可以理解为共享元对象,也就是共享细粒度对象。

    英文定义为:Use sharing to support large numbers of fine-grained objects efficiently.

享元模式的UML图: 

       享元模式涉及以下的角色:

       抽象享元(Flyweight)角色: 它是所有具体享元类的超类。为这些类规定出需要实现的公共接口,那些需要外蕴状态(External State)的操作可以通过方法的参数传入。抽象享元的接口使得享元变得可能,但是并不强制子类实行共享,因此并非所有的享元对象都是可以共享的。

     具体享元(ConcreteFlyweight)角色:具体享元类实现了抽象享元类所规定的接口。如果有内蕴状态(Internal State)的话,必须负责为内蕴状态提供存储空间。享元对象的内蕴状态必须与对象所处的周围环境无关,从而使得享元对象可以在系统内共享。有时候具体享元类又称为单纯具体享元类,因为复合享元类是由单纯具体享元角色通过复合而成的。

不能共享的具体享元类(UnsharableFlyweight): 不能共享的享元类,又叫做复合享元类。一个复合享元对象是由多个单享元对象组成,这些组成的对象是可以共享的,但是复合享元类本身并不能共享。

享元工厂类(FlyweightFactory): 享元工厂类负责创建和管理享元对象。当一个客户端对象请求一个享元对象的时候,享元工厂需要检查系统中是否已经有一个符合要求的享元对象,如果已经有了,享元工厂角色就应当提供这个已有的享元对象;如果系统中没有适当的享元对象的话,享元工厂角色就应当创建一个新的合适的享元对象。

客户类(Client): 客户类需要自行存储所有享元对象的外蕴状态。

享元模式的UML图如下所示:

享元模式深入分析

       它通过与其他类似对象共享数据来减小内存占用。

       享元对象的第一类状态称为内蕴状态(Internal State)。它不会随环境改变而改变,存储在享元对象内部,因此内蕴状态是可以共享的,对于任何一个享元对象来讲,它的值是完全相同的。

享元对象的第二类状态称为外蕴状态(External State)。它会随环境的改变而改变,因此是不可以共享的状态,对于不同的享元对象来讲,它的值可能是不同的。享元对象的外蕴状态必须由客户端保存,在享元对象被创建之后,需要使用的时候再传入到享元对象内部。所以享元的外蕴状态与内蕴状态是两类相互独立的状态,彼此没有关联。

享元模式使用场景分析及代码实现:

       在上面的使用场景中,存储在GG手机中的肉麻短信就是具体的享元对象。

       UML模型图如下所示:

 

建立抽象享元角色:

package com.diermeng.designPattern.Flyweight;

/*

 * 抽象享元角色

 */

public interface BaseSweetWord {

    //显示方法

    public void display();

}

 

建立具体享元角色:

package com.diermeng.designPattern.Flyweight.impl;

import com.diermeng.designPattern.Flyweight.BaseSweetWord;

 

/*

 * 具体的享元类

 */

public class MySweetWord implements BaseSweetWord{

    //具体享元属性

    private String mychar;

 

    public MySweetWord() {}

    //具有实例化属性功能的构造方法

    public MySweetWord(String mychar) {

       this.mychar = mychar;

    }

 

    //对现实功能的具体实现

    public void display() {

       System.out.println(mychar);

    }

}

 

建立享元工厂类:

package com.diermeng.designPattern.Flyweight.impl;

 

import java.util.HashMap;

import java.util.Map;

 

import com.diermeng.designPattern.Flyweight.BaseSweetWord;

 

/*

 * 享元工厂类

 */

public class MySweetWordFactory {

    //享元对象的集合

    private Map<String,BaseSweetWord> pool;

 

    //构造函数 实例化享元对象的集合

    public MySweetWordFactory() {

       pool = new HashMap<String,BaseSweetWord>();

    }

 

    //获取享元对象 如果集合中不存在该享元对象 就创建这个对象

    public BaseSweetWord getMyCharacter(String character) {

       BaseSweetWord myChar = pool.get(character);

       if(myChar == null) {

           myChar = new MySweetWord(character);

           pool.put(character, myChar);

       }

       return myChar;

    }

}

最后我们建立测试客户端:

package com.diermeng.designPattern.Flyweight.client;

 

import com.diermeng.designPattern.Flyweight.BaseSweetWord;

import com.diermeng.designPattern.Flyweight.impl.MySweetWordFactory;

 

/*

 * 享元模式测试客户端

 */

public class FlyweightTest {

 

    public static void main(String[] args) {

       //创建享元工厂

       MySweetWordFactory factory = new MySweetWordFactory();

 

       //从具体的享元工厂中取出相应的MyCharacter

       BaseSweetWord myChar1 = factory.getMyCharacter("宝贝儿,晚安喔");

       BaseSweetWord myChar2 = factory.getMyCharacter("你是我天使");

       BaseSweetWord myChar3 = factory.getMyCharacter("宝贝儿,晚安喔");

       BaseSweetWord myChar4 = factory.getMyCharacter("宝贝儿,我永远的爱你");

 

       myChar1.display();

       myChar2.display();

       myChar3.display();

       myChar4.display();

 

       if(myChar1 == myChar3) {

           System.out.println("哈哈,恭喜!我们是同一句话,直接复制就行了!");

       } else {

           System.out.println("哎呀,我们不是同一句话!");

       }

    }

 

}

输出的结果如下:

宝贝儿,晚安喔

你是我天使

宝贝儿,晚安喔

宝贝儿,我永远的爱你

哈哈,恭喜!我们是同一句话,直接复制就行了!

 

享元模式的优缺点分析:

       优点:

使用享元模式可以降低内存中对象的数量,从而为系统节省大量的内存空间。

缺点:

享元模式使得系统更加复杂,因为为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑复杂化。而且,由于享元工厂需要维护所有的享元对象,此时,如果要维护的享元对象很多的话,在查找具体的享元对象的时候就要消耗大量的时间,换句话说,享元模式是一种以时间换空间的模式。

享元模式的实际应用简介:

享元模式在一般的项目开发中并不常用,而是常常应用于系统底层的开发,以便解决系统的性能问题。

Java中的String类型就是使用了享元模式。

如果在Java中已经创建了一个字符串对象string1,那么下次再创建相同的字符串string2的时候,系统只是把string2的引用指向string1所引用的具体对象,这就实现了相同字符串在内存中的共享。如果每次执行string1=“abc”操作的时候,都创建一个新的字符串对象的话,那么内存的开销会很大。

如果大家有兴趣的话,可以用下面的程序进行测试,就会知道string1和string2的引用是否一致:

String string1= "爱你一万年,爱你的心永不改变";

String string2= "爱你一万年,爱你的心永不改变";

//“==”用来判断两个对象是否是同一个,equals判断字符串的值是否相等

if( string1 == string2 ){

System.out.println("两者一样");

}else{

System.out.println("两者不一样");

}

程序运行后,输出的结果为“两者一样”,这说明String类的设计采用了享元模式。如果string1的内容发生了变化,比如执行了string1 += "让我们结婚吧!"的语句,那么s1与s2的引用将不再一致。

我们额外的谈一下PHP中String的处理。作为一种弱类型语言,PHP的字符串类型是一种基本类型,不是对象。另外,它的执行方式与Java有明显区别,每一个脚本文件执行开始,将会装入所有需要的资源;执行结束后,又将占用的资源就立即全部释放,所以它基本上不会产生类似的性能问题,它的字符串处理的设计,自然也使用不到享元模式。

温馨提示:

 

       面向对象虽然很好地解决了抽象性的问题,但是对于一个实际运行的软件系统,我们还需要考虑面向对象的代价问题,享元模式解决的就是面向对象的代价问题。享元模式采用对象共享的做法来降低系统中对象的个数,从而降低细粒度对象给系统带来的内存压力。

在具体实现方面,我们要注意对象状态的处理,一定要正确地区分对象的内蕴状态和外蕴状态,这是实现享元模式的关键所在。

享元模式的优点在于它大幅度地降低内存中对象的数量。为了做到这一点,享元模式也付出了一定的代价:

1、享元模式为了使对象可以共享,它需要将部分状态外部化,这使得系统的逻辑变得复杂。

2、享元模式将享元对象的部分状态外部化,而读取外部状态使得运行时间会有所加长。

另外,还有一个比较令人关心的问题:到底系统需要满足什么样的条件才能使用享元模式。对于这个问题,总结出以下几点:

1、一个系统中存在着大量的细粒度对象;

2、这些细粒度对象耗费了大量的内存。

3、这些细粒度对象的状态中的大部分都可以外部化;

4、这些细粒度对象可以按照内蕴状态分成很多的组,当把外蕴对象从对象中剔除时,每一个组都可以仅用一个对象代替。

5、软件系统不依赖于这些对象的身份,换言之,这些对象可以是不可分辨的。

满足以上的这些条件的系统可以使用享元对象。最后,使用享元模式需要维护一个记录了系统已有的所有享元的哈希表,也称之为对象池,而这也需要耗费一定的资源。因此,应当在有足够多的享元实例可供共享时才值得使用享元模式。

作者: 国士工作室 发表于 2011-05-27 20:38 原文链接

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架