老生常谈:享元模式

      享元模式:

         以共享的方式高效地支持大量的细粒度对象。

      享元对象的状态:

        1:内蕴状态(Internal State)内蕴状态存储在享元对象内部且不会随环境改变而改变。因此内蕴状态并可以共享。

        2:外蕴状态(External State)。外蕴状态是随环境改变而改变的、不可以共享的状态。享元对象的外蕴状态必须由客户端保存,并在享元对象被创建之后,在需要使用的时候再传入到享元对象内部。外蕴状态与内蕴状态是相互独立的。

      享元模式的应用条件:

        1: 一个系统有大量的对象。

        2:这些对象耗费大量的内存。

        3:这些对象的状态中的大部分都可以外部化。

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

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

      .NET的享元模式

         .NET中的String类型就是运用了享元模式。.NET中如果第一次创建了一个字符串对象s1,下次再创建相同的字符串s2时只是把它的引用指向s1所引用的具体对象,这就实现了相同字符串在内存中的共享。下面的程序来演示s1和s2的引

用是否一致: 输出的结果为True。

string  s1  =   " 测试字符串一 " ;
string  s2  =   " 测试字符串一 " ;
Console.WriteLine(Object.ReferenceEquals(s1, s2));

         注意:如果再有一个字符串s3,它的初始值为“测试字符串”,再对它进行操作s3 = s3 + “一”,这时虽然s1和s3的值相同,但是它们的引用是不同的。

      享元模式的分类:

         1:单纯享元模式;

         2:复合享元模式。

      第一:单纯享元模式。

             1: 单纯享元模式结构图:

         

             2:单纯享元模式构成说明:

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

///   <summary>
    
///  "Flyweight"
    
///   </summary>
     abstract   class  Flyweight
    {
        
//  Methods
         ///   <summary>
        
///  抽象享元对象的商业方法
        
///   </summary>
        
///   <param name="extrinsicstate"> 外蕴状态 </param>
         abstract   public   void  Operation( int  extrinsicstate);
    }


                 2>:具体享元(ConcreteFlyweight)角色:实现抽象享元角色所规定的接口。如果有内蕴状态的话,必须负责为内蕴状态提供存储空间。享元对象的内蕴状态必须与对象所处的周围环境无关,从而使得享元对象可以在系统内共享的。

///   <summary>
    
///  "ConcreteFlyweight"
    
///   </summary>
     class  ConcreteFlyweight : Flyweight
    {
        
private   string  intrinsicstate  =   " A " ;
        
//  Methods
         override   public   void  Operation( int  extrinsicstate)
        {
            Console.WriteLine(
" ConcreteFlyweight: intrinsicstate {0}, extrinsicstate {1} " ,
              intrinsicstate, extrinsicstate);
        }
    }

                

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

                  注意:客户端不可以直接实例化享元类,必须通过享元工厂类来创建,因为享元工厂类在系统中只能有一个,所以可以结合单例模式来改善。当客户端需要单纯享元对象时,需要调用享元工厂的Singleton()方法,此时工厂会取得所有的单纯享元对象,然后传入所需的单纯享元对象的内蕴状态,工厂方法负责产生所需要的享元对象。


  ///   <summary>
    
///  "FlyweightFactory"
    
///   </summary>
     class  FlyweightFactory
    {
        
//  Fields
         private  Dictionary < string , Flyweight >  flyweights  =   new  Dictionary < string , Flyweight > ();
        
private   static   readonly  FlyweightFactory instance  =   new  FlyweightFactory();
        
///   <summary>
        
///  Constructors
        
///   </summary>
         private   FlyweightFactory()
        {         
        }

        
//  Methods
         ///   <summary>
        
///  从享元工厂中生产出一个具体的享元对象
        
///   </summary>
        
///   <param name="key"> 内蕴状态 </param>
        
///   <returns></returns>
         public  Flyweight GetFlyweight( string  key)
        {
            
return  ((Flyweight)flyweights[key]);
        }
        
///   <summary>
        
///  享元工厂单例方法
        
///   </summary>
        
///   <returns></returns>
         public   static   FlyweightFactory Singleton()
        {
            
return  FlyweightFactory.instance;
        } 
        
///   <summary>
        
///  向享元工厂对象增加一个享元对象
        
///   </summary>
        
///   <param name="sKey"> 内蕴状态 </param>
        
///   <param name="_Flyweight"> 具体享元对象 </param>
         public   void  AddFlyweight( string  sKey, Flyweight _Flyweight)
        {
            flyweights.Add(sKey , _Flyweight);
        }
        
public  Flyweight factory( string  sKey)
        {
            
if  (flyweights.ContainsKey(sKey))
            {
                
return   this .GetFlyweight(sKey);
            }
            
else
            {
                
this .AddFlyweight(sKey,  new  ConcreteFlyweight());
                
return   this .GetFlyweight(sKey);
            }     
        }
    }

                4>:客户端(Client)角色:需要维护一个对所有享元对象的引用;需要自行存储所有享元对象外蕴状态。

//  初始化外蕴状态值
             int  extrinsicstate  =   22 ;
            
// 享元工厂对象使用单例
            FlyweightFactory f  =  FlyweightFactory.Singleton () ;
         
            
// 调用过程
            
// 向享元工厂对象请求一个内蕴状态为"X"的单纯享元对象
            Flyweight fx  =  f.factory( " X " );
            
// 调用X的商业方法,X的外蕴状态值为21
            fx.Operation( -- extrinsicstate);

            Flyweight fy 
=  f.factory( " Y " );
            fy.Operation(
-- extrinsicstate);

            Flyweight fz 
=  f.factory( " Z " );
            fz.Operation(
-- extrinsicstate);

      第二:复合享元模式。

               复合享元模式结构图:

 

            结构对象说明:

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

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

           3:复合享元(UnsharableFlyweight)角色:复合享元角色所代表的对象是不可以共享的,但是一个复合享元对象可以分解成为多个本身是单纯享元对象的组合。复合享元角色又称做不可共享的享元对象。

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

           5:客户端(Client)角色:本角色还需要自行存储所有享元对象的外蕴状态。

      享元模式的优点:

          大幅度地降低内存中对象的数量。

      享元模式的缺点:

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

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

      总结:

            享元模式一般是解决系统性能问题的,所以经常用于底层开发,在项目开发中并不常用。
注:
   本文参考:《Java与模式》
                   http://www.cnblogs.com/zhenyulu/articles/55793.html

转载于:https://www.cnblogs.com/ASPNET2008/archive/2009/02/12/1387230.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
单例模式懒汉线程安全的实现可以通过在getInstance方法上加锁来实现。在懒汉式的单例模式中,单例对象的初始化是延迟到第一次调用getInstance方法时才进行的。为了确保线程安全,一种常见的做法是在getInstance方法上加上synchronized关键字,使得每次只有一个线程可以进入该方法。这样可以避免多个线程同时创建实例的问题。以下是一个懒汉线程安全的单例模式示例代码: ```java public class Singleton { private static Singleton instance; private Singleton() { // 私有化构造方法,防止外部直接实例化 } public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } ``` 在上述示例代码中,getInstance方法被声明为synchronized,确保了线程安全,但也会导致效率较低,因为每次调用getInstance方法时都需要获取锁。因此,在实际开发中,如果不是特别需要懒加载的特性,可以考虑使用饿汉式单例模式或者双重检查锁定单例模式。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [单例模式之懒汉式(线程安全)](https://blog.csdn.net/qq_44119625/article/details/123523408)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [老生常谈C++的单例模式与线程安全单例模式(懒汉/饿汉)](https://download.csdn.net/download/weixin_38741966/13783415)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值