Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

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

$
0
0

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

一、什么是享元模式

这里写图片描述

  享元模式(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

作者:o279642707 发表于2017/2/7 16:01:42 原文链接
阅读:8 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>