【13】享元模式(FlyWeight Pattern)

  一、引言

  在软件开发过程,如果我们需要重复使用某个对象的时候,若重复地使用new创建这个对象的话,就需要多次地去申请内存空间了,这样可能会出现内存使用越来越多的情况,这样的问题是非常严重。享元模式可以解决这个问题,下面具体看看享元模式是如何去解决这个问题的。

  二、享元模式的详细介绍

  在前面说了,享元模式可以解决上面的问题了。在介绍享元模式之前,让我们先要分析下如果去解决上面那个问题。上面的问题就是重复创建了同一个对象,如果让我们去解决这个问题肯定会这样想:“既然都是同一个对象,能不能只创建一个对象,然后下次需要创建这个对象的时候,让它直接用已经创建好了的对象就好了“。也就是说——让一个对象共享。不错,这个也是享元模式的实现精髓所在。

  2.1 定义

  介绍完享元模式的精髓之后,让我们具体看看享元模式的正式定义:

  享元模式——运用共享技术有效地支持大量细粒度的对象。享元模式可以避免大量相似类的开销。在软件开发中如果需要生成大量细粒度的类实例来表示数据,这些实例除了几个参数外基本上都是相同的,这时候就可以使用享元模式来大幅度减少需要实例化类的数量。如果能把这些参数(指的这些类实例不同的参数)移动类实例外面,在方法调用时将他们传递进来,这样就可以通过共享,达到大幅度地减少单个实例的数目。(这个也是享元模式的实现要领),然而我们把类实例外面的参数称为享元对象的外部状态,把在享元对象内部定义称为内部状态。

  具体享元对象的内部状态与外部状态的定义为:

  内部状态:在享元对象的内部并且不会随着环境的改变而改变的共享部分。

  外部状态:随环境改变而改变的,不可以共享的状态。

  2.2 享元模式实现

  分析完享元模式的实现思路之后,相信大家实现享元模式肯定没什么问题了,下面以一个实际的应用来实现下享元模式。这个例子是:一个文本编辑器中会出现很多字面,使用享元模式去实现这个文本编辑器的话,会把每个字面做成一个享元对象。享元对象的内部状态就是这个字面,而字母在文本中的位置和字体风格等其他信息就是它的外部状态。下面就以这个例子来实现下享元模式,具体实现代码如下:

  /// <summary>
    /// 客户端调用
    /// </summary>
    class Client
    {
        static void Main(string[] args)
        {
            // 定义外部状态,例如字母的位置等信息
            int externalstate = 10;
            // 初始化享元工厂
            FlyweightFactory factory = new FlyweightFactory();

            // 判断是否已经创建了字母A,如果已经创建就直接使用创建的对象A
            Flyweight fa = factory.GetFlyweight("A");
            if (fa != null)
            {
                // 把外部状态作为享元对象的方法调用参数
                fa.Operation(--externalstate);
            }
            // 判断是否已经创建了字母B
            Flyweight fb = factory.GetFlyweight("B");
            if (fb != null)
            {
                fb.Operation(--externalstate);
            }
            // 判断是否已经创建了字母C
            Flyweight fc = factory.GetFlyweight("C");
            if (fc != null)
            {
                fc.Operation(--externalstate);
            }
            // 判断是否已经创建了字母D
            Flyweight fd= factory.GetFlyweight("D");
            if (fd != null)
            {
                fd.Operation(--externalstate);
            }
            else
            {
                Console.WriteLine("驻留池中不存在字符串D");
                // 这时候就需要创建一个对象并放入驻留池中
                ConcreteFlyweight d = new ConcreteFlyweight("D");
                factory.flyweights.Add("D", d);
            }

            Console.Read();
        }
    }

    /// <summary>
    /// 享元工厂,负责创建和管理享元对象
    /// </summary>
    public class FlyweightFactory
    {
        // 最好使用泛型Dictionary<string,Flyweighy>
        //public Dictionary<string, Flyweight> flyweights = new Dictionary<string, Flyweight>();
        public Hashtable flyweights = new Hashtable();

        public FlyweightFactory()
        {
            flyweights.Add("A", new ConcreteFlyweight("A"));
            flyweights.Add("B", new ConcreteFlyweight("B"));
            flyweights.Add("C", new ConcreteFlyweight("C"));
        }

        public Flyweight GetFlyweight(string key)
        {
        // 更好的实现如下
            //Flyweight flyweight = flyweights[key] as Flyweight;
            //if (flyweight == null)
            //{
            //    Console.WriteLine("驻留池中不存在字符串" + key);
            //    flyweight = new ConcreteFlyweight(key);
            //}
            //return flyweight;
        return flyweights[key] as Flyweight;
        }
    }

    /// <summary>
    ///  抽象享元类,提供具体享元类具有的方法
    /// </summary>
    public abstract class Flyweight
    {
        public abstract void Operation(int extrinsicstate);
    }

    // 具体的享元对象,这样我们不把每个字母设计成一个单独的类了,而是作为把共享的字母作为享元对象的内部状态
    public class ConcreteFlyweight : Flyweight
    {
        // 内部状态
        private string intrinsicstate ;

        // 构造函数
        public ConcreteFlyweight(string innerState)
        {
            this.intrinsicstate = innerState;
        }

        /// <summary>
        /// 享元类的实例方法
        /// </summary>
        /// <param name="extrinsicstate">外部状态</param>
        public override void Operation(int extrinsicstate)
        {
            Console.WriteLine("具体实现类: intrinsicstate {0}, extrinsicstate {1}", intrinsicstate, extrinsicstate);
        }
    }

  在享元模式的实现中,我们没有像之前一样,把一个细粒度的类实例设计成一个单独的类,而是把它作为共享对象的内部状态放在共享类的内部定义,具体的解释注释中都有了,大家可以参考注释去进一步理解享元模式。

  2.3 享元模式的类图

  看完享元模式的实现之后,为了帮助大家理清楚享元模式中各类之间的关系,下面给出上面实现代码中的类图,如下所示:

  

  在上图中,涉及的角色如下几种角色:

  1)抽象享元角色(Flyweight):此角色是所有的具体享元类的基类,为这些类规定出需要实现的公共接口。那些需要外部状态的操作可以通过调用方法以参数形式传入。

  2)具体享元角色(ConcreteFlyweight):实现抽象享元角色所规定的接口。如果有内部状态的话,可以在类内部定义。

  3)享元工厂角色(FlyweightFactory):本角色复杂创建和管理享元角色。本角色必须保证享元对象可以被系统适当地共享,当一个客户端对象调用一个享元对象的时候,享元工厂角色检查系统中是否已经有一个符合要求的享元对象,如果已经存在,享元工厂角色就提供已存在的享元对象,如果系统中没有一个符合的享元对象的话,享元工厂角色就应当创建一个合适的享元对象。

  4)客户端角色(Client):本角色需要存储所有享元对象的外部状态。

  三、享元模式的优缺点

  分析完享元模式的实现之后,让我们继续分析下享元模式的优缺点:

  优点:

  1)降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。

  缺点:

  1)为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑更复杂,使系统复杂化。

  2)享元模式将享元对象的状态外部化,而读取外部状态使得运行时间稍微变长。

  四、使用场景

  在下面所有条件都满足时,可以考虑使用享元模式:

  1)一个系统中有大量的对象;

  2)这些对象耗费大量的内存;

  3)这些对象中的状态大部分都可以被外部化;

  4)这些对象可以按照内部状态分成很多的组,当把外部对象从对象中剔除时,每一个组都可以仅用一个对象代替;

  5)软件系统不依赖这些对象的身份;

  6)满足上面的条件的系统可以使用享元模式。但是使用享元模式需要额外维护一个记录子系统已有的所有享元的表,而这也需要耗费资源,所以,应当在有足够多的享元实例可共享时才值得使用享元模式。

  注:在.NET类库中,string类的实现就使用了享元模式,更多内容可以参考字符串驻留池的介绍,同时也可以参考这个博文深入理解.NET中string类的设计——http://www.cnblogs.com/artech/archive/2010/11/25/internedstring.html

  五、总结

  到这里,享元模式的介绍就结束了,享元模式主要用来解决由于大量的细粒度对象所造成的内存开销的问题,它在实际的开发中并不常用,可以作为底层的提升性能的一种手段。

   备注:

  既然已经到享元模式了,那对于了解单例模式的同学,就会产生一个问题,这两种模式有什么区别:

  1、单例模式是类级别的。

    一个类只能有一个对象实例,没有办法创建多个对象实例的。

    此种模式主要是为了共享数据或状态的目的。

  2、享元模式是对象级别的。

    话说一个类可以创建多个对象,但是为了提高系统性能,降低内存消耗,所以才采用的享元模式。

    可以将此模式理解成一组共享的对象集合。

    享元模式可以再次创建对象,也可以取缓存对象。所以可以在需要的时候创建更多的对象。

    此种模式虽然节省了很多的内存空间,避免了资源浪费。但是,对于高并发的情况不是线程安全的。试想一下,同一时间获取到了对象,传入了两个不同内容的外部实例,这时候会产生内容覆盖的情况。所以,对于涉及到如数据库操作的类库,应该避免使用享元模式。

  3、两种模式的目的是一样的,都是为了减少内存的开销。享元模式也可以看成是单例模式的一种扩展。

参考链接:http://www.cnblogs.com/artech/archive/2010/11/25/internedstring.html

原文地址:https://www.cnblogs.com/supersnowyao/p/9191620.html

时间: 2024-08-28 09:01:18

【13】享元模式(FlyWeight Pattern)的相关文章

二十四种设计模式:享元模式(Flyweight Pattern)

享元模式(Flyweight Pattern) 介绍运用共享技术有效地支持大量细粒度的对象. 示例有一个Message实体类,某些对象对它的操作有Insert()和Get()方法,现在要运用共享技术支持这些对象. MessageModel using System; using System.Collections.Generic; using System.Text; namespace Pattern.Flyweight { /// <summary> /// Message实体类 ///

设计模式(结构型)之享元模式(Flyweight Pattern)

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbober] 阅读前一篇<设计模式(结构型)之外观模式(Facade Pattern)>http://blog.csdn.net/yanbober/article/details/45476527 概述 当一个软件系统在运行时产生的对象数量太多,将导致运行代价过高,带来系统性能下降等问题.所以需要采用一

12.享元模式(Flyweight Pattern)

using System; using System.Collections; namespace ConsoleApplication5 { class Program { /// <summary> /// 在软件开发过程,如果我们需要重复使用某个对象的时候, /// 如果我们重复地使用new创建这个对象的话,这样我们在内存就需要多次地去申请内存空间了, /// 这样可能会出现内存使用越来越多的情况,这样的问题是非常严重,然而享元模式可以解决这个问题, /// 下面具体看看享元模式是如何去

享元模式Flyweight

享元模式 享元模式Flyweight,布布扣,bubuko.com

享元模式-Flyweight(Java实现)

享元模式-Flyweight 享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用. 本文中的例子如下: 使用享元模式: 小明想看编程技术的书, 就到家里的书架上拿, 如果有就直接看, 没有就去买一本, 回家看. 看完了就放到家里的书架上, 以后再想看了直接就在书架上拿, 不需要再去买同样的这本书了. 不适用享元模式: 小明想看编程技术的书, 就去书店里买一本回家看, 买完后看完了, 书就不知道丢到了哪里去了. 下次想看的时候就找不到了,

享元模式-Flyweight

享元模式:运用共享技术有效地支持大量细粒度的对象. 内部状态和外部状态: 享元模式可以避免大量的非常相似的类的开销.在程序设计中,有时需要生成大量细粒度的类实例来表示数据.如果能发现这些实例除了几个参数外基本上都相同,有时就能大幅度减少需要实例化的类的数量.如果能把那些参数移到类实例的外部,在方法调用时将它们传递进来,就可以通过共享,大幅度地减少单个实例的数目. 何时使用享元模式: 如果一个应用程序使用了大量的对象,而大量的这些对象造成了存储开销时就应该考虑使用;还有就是对象的部分状态可已放到外

【设计模式】—— 享元模式Flyweight

前言:[模式总览]——————————by xingoo 模式意图 享元模式,也叫[轻量级模式]或者[蝇量级模式].主要目的就是为了减少细粒度资源的消耗.比如,一个编辑器用到大量的字母数字和符号,但是不需要每次都创建一个字母对象,只需要把它放到某个地方共享使用,单独记录每次创建的使用上下文就可以了. 再比如餐馆的桌子,餐具,这些都是享元模式的体现.客户是流动的,每次吃饭都是用饭店固定的那些餐具,而饭店也不需要每次新来顾客,就买新的盘子餐具. 应用场景 1 一个系统应用到了大量的对象,而且很多都是

设计模式之享元模式(Flyweight)摘录

23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于怎样创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化托付给还有一个对象.创建型模式有两个不断出现的主旋律.第一,它们都将关于该系统使用哪些详细的类的信息封装起来.第二,它们隐藏了这些类的实例是怎样被创建和放在一起的.整个系统关于这些对象所知道的是由抽象类所定义的接口.因此,创建型模式在什么被创建,谁创建它,它是怎样被创建的,

设计模式(十)享元模式Flyweight(结构型)

说明: 相对于其它模式,Flyweight模式在PHP实现似乎没有太大的意义,因为PHP的生命周期就在一个请求,请求执行完了,php占用的资源都被释放.我们只是为了学习而简单做了介绍. 1. 概述 面向对象技术可以很好地解决系统一些灵活性或可扩展性或抽象性的问题,但在很多情况下需要在系统中增加类和对象的个数.当对象数量太多时,将导致运行代价过高,带来性能下降等问题.比如:例子1:图形应用中的图元等对象.字处理应用中的字符对象等. 2.解决方案: 享元模式(Flyweight):对象结构型模式运用

设计模式 笔记 享元模式 Flyweight

//---------------------------15/04/20---------------------------- //Flyweight 享元模式------对象结构型模式 /* 1:意图: 运用共享技术有效地支持大量细粒度的对象. 2:动机: 3:适用性: 以下条件都成立时才能使用. 1>一个应用程序使用了大量的对象. 2>完全由于使用大量的对象,造成很大的存储开销. 3>对象的大多数状态都可变为外部状态. 4>如果删除对象的外部状态,那么可以用相对较少的共享对