23种设计模式之_享元模式

来源:互联网 发布:java生成时间戳 编辑:程序博客网 时间:2024/06/05 19:49

    • 一什么是享元模式
    • 二享元模式几个角色
        • 抽象享元类Flyweight
        • 具体享元类FlyWeightAImlFlyWeightBIml
        • 享元工厂类FlyweightFactoiy
        • 客户类Client
    • 三享元模式使用场景
    • 四代码分析

设计模式的熟练掌握,能够更容易理解系统的底层架构实现。

一、什么是享元模式

这里写图片描述

  享元模式(Flyweight Pattern):以共享的方式高效的支持大量的细粒度对象。通过复用内存中已存在的对象,降低系统创建对象实例的性能消耗。
  享元的英文是Flyweight,是一个来自体育方面的专业用语,在拳击、摔跤和举重比赛中特指最轻量的级别。把这个单词移植到软件工程中,也是用来表示特别小的对象,即细粒度的对象。至于为什么把Flyweight翻译为“享元”,可以理解为共享元对象,也就是共享细粒度对象。
  在面向对象中,大量细粒度对象的创建、销毁及存储所造成的资源和性能上的损耗,可能会在系统运行时形成瓶颈。那么该如何避免产生大量的细粒度对象,同时又不影响系统使用面向对象的方式进行操作呢?享元模式提供了一个比较好的解决方案。

二、享元模式几个角色

uml类图:

这里写图片描述

抽象享元类(Flyweight)

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

具体享元类(FlyWeightAIml,FlyWeightBIml)

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

享元工厂类(FlyweightFactoiy)

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

客户类(Client)

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

三、享元模式使用场景

  当系统中某个对象类型的实例较多的时候;
  当系统设计时候,对象实例真正有区别的分类很少,例如对于拼音,如果对每个字母都初始化一个对象实例的话,这样实例就太多了。使用享元模式只需要提前初始化基本拼音,就可以任意进行组装成不同的拼音

四、代码分析

网上很多例子,看着不太明白。使用字母进行举例子通俗易懂。

CharactorFactory 工厂

package Chartflyweight;import java.util.Hashtable;public class CharactorFactory {    private Hashtable<String, FlyWeight> charactors = new Hashtable<String, FlyWeight>();    // 构造函数    public CharactorFactory() {        charactors.put("A", new FlyWeightAIml());        charactors.put("B", new FlyWeightBIml());    }    // 获得指定字符实例    public FlyWeight getCharactor(String key) {        FlyWeight charactor = (FlyWeight) charactors.get(key);        if (charactor == null) {            if (key.equals("A")) {                charactor = new FlyWeightAIml();            } else if (key.equals("B")) {                charactor = new FlyWeightBIml();            }            charactors.put(key, charactor);        }        return charactor;    }}

FlyWeight 享元抽象类

package Chartflyweight;public abstract class FlyWeight {    protected String charStr = "";    protected int fontSize;    protected abstract void operator(int fontSize);    // 显示方法    protected abstract void displayCharator();}

FlyWeightAIml 具体实现类

package Chartflyweight;public class FlyWeightAIml extends FlyWeight {    @Override    protected void operator(int fontSize) {       this.fontSize=fontSize;    }    public FlyWeightAIml() {        this.charStr = "A";        this.fontSize=12;    }    @Override    protected void displayCharator() {        System.out.println("字符:" + this.charStr + ",大小:" + fontSize);    }}

FlyWeightBIml 具体实现类

package Chartflyweight;public class FlyWeightBIml extends FlyWeight {    @Override    protected void operator(int fontSize) {        this.fontSize = fontSize;    }    public FlyWeightBIml() {        this.charStr = "B";        this.fontSize = 12;    }    @Override    protected void displayCharator() {        System.out.println("字符:" + this.charStr + ",大小:" + fontSize);    }}

Client 测试类 1 ( 简单的剔除外蕴状态,在client中进行存储)

package Chartflyweight;//如何有特别多的外部状态,则需要很多的函数,函数进行抽取public class Clinet {    public static void main(String[] args) {        FlyWeightAIml a = new FlyWeightAIml();        FlyWeightBIml b = new FlyWeightBIml();        // 显示字符A        display(a, 12);        // 显示字符B        display(b, 14);    }    // 设置字符的大小    public static void display(FlyWeight objChar, int nSize) {        try {            System.out.println("字符:" + objChar.charStr + ",大小:" + nSize);        } catch (Exception err) {        }    }}

测试类2 (考虑到复用性,将外蕴状态作为参数在使用时候进行传递)

package Chartflyweight;//如何有特别多的外部状态,则需要很多的函数,函数进行抽取public class Clinet2 {    public static void main(String[] args) {        FlyWeightAIml a = new FlyWeightAIml();        FlyWeightBIml b = new FlyWeightBIml();        // 设置字符A的大小        a.operator(12);        // 显示字符B        a.displayCharator();        // 设置字符B的大小        b.operator(14);        // 显示字符B        b.displayCharator();    }}

输出结果:

字符:A,大小:12
字符:B,大小:14

上面代码内蕴共享对象是 A,B ,而外蕴不共享状态是 fontSize.


引用个例子:

享元模式在一般的项目开发中并不常用,而是常常应用于系统底层的开发,以便解决系统的性能问题。
Java和.Net中的String类型就是使用了享元模式。如果在Java或者.NET中已经创建了一个字符串对象s1,那么下次再创建相同的字符串s2的时候,系统只是把s2的引用指向s1所引用的具体对象,这就实现了相同字符串在内存中的共享。如果每次执行s1=“abc”操作的时候,都创建一个新的字符串对象的话,那么内存的开销会很大。
如果大家有兴趣的话,可以用下面的程序进行测试,就会知道s1和s2的引用是否一致:
Java代码:

String s1 = "测试字符串1";String s2 = "测试字符串1";//“==”用来判断两个对象是否是同一个,equals判断字符串的值是否相等if( s1 == s2 ){System.out.println("两者一致");}else{System.out.println("两者不一致");}

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

Java DEMO :http://pan.baidu.com/s/1bpKhdpl

引用:
享元(Flyweight)模式 http://www.cnblogs.com/zhenyulu/articles/55793.html
设计模式之享元模式 http://blog.csdn.net/wanghao72214/article/details/4046182
Flyweight模式的学习 http://supercrsky.iteye.com/blog/372714

1 0