六.结构型——享元模式

0.《设计模式》摘录

  • 问题:
     在一个文档编辑器中可以把文档中的每个字符描述成一个对象:
     太消耗内存,一个小文档都可能包含成千上万个字符对象。

  • 解决方案:
     共享对象,每个字符用一个对象表示,文档中出现的所有这个字符都共享这个对象。
     字符全部引用flyweight pool,避免重复
     状态信息怎么办?把每个字符的状态信息(字体、颜色等)用一个外部对象表示
     这些状态也可以共享,例如整个段落的文字采用的是相同的字体和颜色。

一.针对的问题

​  不同的客户有相似的需要,比如做网站,最开始做法是给每个客户分配一个空间,复制一份代码。也就是说,多一个网站就多一个类的实例,浪费了很多资源。

​  解决:共享代码核心和数据库,利用用户ID来区分不同的用户,具体数据和模板可以不同。

二.享元模式(只有共享部分的实现)

2.1 结构图和代码

  • 享元模式:运用共享技术有效地支持大量细粒度的对象。

在这里插入图片描述

  • Flyweight:所有具体享元类的超类或接口,通过这个接口,Flyweight可以接受并作用于外部状态。

    abstract class Flyweight
    {
    	public abstract void Operation (int extrinsicstate);   
    }
    
  • ConcreteFlyweight:继承Flyweight超类或实现Flyweight接口,并为内部状态增加存储空间。

    class ConcreteFlyweight extends Flyweight
    {
        public override void Operation(int extrinsicstate)
        {
            Console.WriteLine ("具体 Flyweight:" +extrinsicstate);
        }
    }
    
    
  • UnsharedConcreteFlyweight:不需要共享的Flyweight子类。因为Flyweight接口共享成为可能,但它并不强制共享。

    class UnsharedConcreteFlyweight extends Flyweight
    {
     	public override void Operation(int extrinsicstate)
        {
    		Console.Writeine("不共享的具体 Flyweight:"+ extrinsicstate);   
        }
    }
    
  • FlyweightFactory:享元工厂,用来创建并管理Flyweight对象。它主要是用来确保合理地共享Flyweight。当用户请求一个Flyweight时,FlyweightFactory对象提供一个己创建的实例或者创建一个

    class FlyweightFactory
    {
     	private Hashtable flyweights = new Hashtable();
        
        //初始化工厂时,先生成三个实例
        flyweights.Add("X", new ConcreteFlyweight());
        flyweights.Add("Y", new ConcreteFlyweight());
        flyweights.Add("Z", new ConcreteFlyweight()); 
        
        //根据客户端请求,获得已生成的实例
        public Flyweight GetFlyweight(String key)
        {
            return ((Flyweight)flyweights[key])
        }
    }
    
  • 客户端:client是关联享元工厂FlyweightFactory、ConcreteFlyweight、UnsharedConcreteFlyweight,就是说他知道有那些共享Flyweight和不共享的Flyweight

    static void Main(string[] args)
    {
        int extrinsicstate = 22; //代码外部状态
        FlyweightFactory f = new FlyweightFactory();
        Flyweight fx = f.GetFlyweight("x");
        fx.Operation(--extrinsicstate);
        Flyweight fy = f.GetFlyweight("Y");
        fy.Operation(--extrinsicstate);
        Flyweight fz = f.GetFlyweight("z");
        fz.Operation(--extrinsicstate);
        UnsharedConcreteFlyweight uf = new UnsharedConcreteFlyweight();
        uf.Operation(--extrinsicstate);
        Console.Read();
    }
    
    /*
    结果表示
    具体 Flyweight: 21
    具体 Flyweight: 20
    具体 Flyweight: 19
    不共享的具体Flyweight: 18
    */
    
  • 注意:也可以懒汉式,初始化时什么也不做,用户调用时再去判断对象是否null来决定是否实例化。

2.2. 应用到网站的代码

网站应该有一个抽象类和一个具体网站类,然后通过网站工厂来产生对象。

  • 网站抽象类

    abstract class WebSite
    {
    	public abstract void Use();
    }
    
  • 具体网站类

    class ConcreteWebSite extends WebSite
    {
    	private string name = "";
        public ConcreteWebSite(string name)
        {
    		this.name = name;
            @override
            public void Use()
            {
            	Console.WriteLine ("网站分类:" + name)
            }
        }
    }
    
  • 网站工厂类

    class WebSiteFactory
    {
    	private Hashtable flyweights = new Hashtable();
        //获得网站分类 
        public WebSite GetWebSiteCategory(string key)
        {
            if(!flyweights.ContainsKey(key)) 
                flyweights.Add(key,new ConcreteWebSite(key)); 
            return ((WebSite)flyweights[key]);
        }
        //获得网站分类总数
        public int GetWebSiteCount()
        {
        	return flyweights.Count;
        }
    }
    
  • 客户端代码

    static void Main(string[] args)
    {
        WebSiteFactory f = new WebSiteFactory();
        WebSite fx = f.GetWebSiteCategory ("产品展示”);
        fx.Use();
                                           
        WebSite fy = f.GetWebSiteCategory("产品展示”);
        fy.Use();
                                          
        WebSite fz = f.GetWebSiteCategory("产品展示");
        fz.Use();
                                          
        WebSite fl = f.GetWebSiteCategory("博客");
        fl.Use();
                                          
        WebSite fm = f.GetWebSiteCategory("博客")	;
        fm.Use();
                                          
        WebSite fn = f.GetWebSiteCategory("博客");
        fn.Use();
                                          
        Console.WriteLine ("网站分类总数为{0}",f.GetWebSiteCount());
        Console.Read();
    }
    

    结果:
    网站分类:产品展示

    网站分类:产品展示

    网站分类:产品展示

    网站分类:博客

    网站分类:博客

    网站分类:博客

    网站分类总数为 2

    实际上这样写没有体现对象间的不同,只体现了它们共享的部分。

三.内部状态与外部状态

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

    ​  外部状态:而随环境改变而改变的、不可以共享的状态
      ​ 适用场景:有时需要生成大量细粒度的类实例来表示数据。如果发现这些实例除了几个参数外基本上都是相同的,有时候能减少需要实例化的类的数量:把那些参数移到类实例的外面,在方法调用时将它们传递进来,就可以通过共享减少单个实例的数目。

    ​  内部状态存储于ConcreteFlyweight对象之中,而外部对象由客户端对象存储或计算,调用Flyweight对象的操作时传递给它。也就是说,客户账号就是外部状态,应该由专门的对象处理

  • 代码结构图:

    在这里插入图片描述

    • 用户类:用于网站的客户账号,是“网站”类的外部状态

      public class User
      {
       	private string name; 
          public User(string name)
          {
              this.name = name;
          }
          
          public string get()
          { 
              return name; 
          }   
        
      }
      
    • 网站抽象类

      abstract class WebSite
      {
          public abstract void Use(User user);
      }
      
    • 具体网站类

      class ConcreteWebSite extends WebSite
      {
          private string name = "";
          public ConcreteWebSite(String name)
          {
              this.name = name;	
          }
          
      	@override
          public void Use(User user)//具体实现Use,传入了参数User来获得外部状态
          {
          	Console.WriteLine("网站分类:" + name + "用户:" + user.Name);
          }
      }
      
    • 网站工厂类

      class WebSiteFactory
      {
          private Hashtable flyweights = new Hashtable();
          //获得网站分类
          public WebSite GetWebSiteCategory(String key)
          {
              if (!flyweights.ContainsKey(key))
                  flyweights.Add(key, new ConcreteWebSite(key)); 
              return ((WebSite)flyweights[key]);
          }
          //获得网站分类总数 
          public int GetWebSiteCount()
          {
              return flyweights.Count;
          }
      }
      
    • 客户端代码

      static void Main(string[] args)
      {
          WebSiteFactory f = new WebSiteFactory();
          WebSite fx = f.GetWebSiteCategory("产品展示");
          fx=f.Use(new User("小菜"));
          
          WebSiteFactory f = new WebSiteFactory();
          WebSite fx = f.GetWebSiteCategory("产品展示");
          fx=f.Use(new User("大鸟"));
             
          WebSiteFactory f = new WebSiteFactory();
          WebSite fx = f.GetWebSiteCategory("产品展示");
          fx=f.Use(new User("娇娇"));
          
          WebSiteFactory f = new WebSiteFactory();
          WebSite fx = f.GetWebSiteCategory("博客");
          fx=f.Use(new User("桃谷六仙"));
          
          WebSiteFactory f = new WebSiteFactory();
          WebSite fx = f.GetWebSiteCategory("博客");
          fx=f.Use(new User("南海鳄神"));
          
          WebSiteFactory f = new WebSiteFactory();
          WebSite fx = f.GetWebSiteCategory("博客");
          fx=f.Use(new User("老顽童"));
      
          Console.WriteLine ("得到网站分类总数为{0}",f.GetWebSiteCount());
         	Console.Read();
      }
      

      结果显示:尽管给六个不同用户使用网站,但实际上只有两个网站实例。
      网站分类:产品展示 用户:小菜

      网站分类:产品展示 用户:大鸟

      网站分类:产品展示 用户:娇娇

      网站分类:博客用户:南海鳄神

      网站分类:博客 用户:桃谷六仙

      网站分类:博客 用户:老顽童

      得到网站分类总数为2

四.一些问题

  • 为什么要有UnsharedConcreteFlyweight的存在呢?
      因为尽管我们大部分时间都需要共享对象来降低内存的损耗,但个别时候也有可能不需要共享的,它可以解决那些不需要共享对象的问题

  • 什么时候用享元模式?
     如果一个应用程序使用了大量的对象,而大量的这些对象造成了很大的存储开销时就应该考虑使用;
     还有就是对象的大多数状态可以外部状态,如果删除对象的外部状态, 那么可以用相对较少的共享对象取代很多组对象,此时可以考虑使用享元模式

  • 缺点:
      使用享元模式需要维护一个记录了系统已有的所有享元的列表,而这本身需要耗费资源,
     另外享元模式使得系统更加复杂。
     为了使对象可以共享,需要将一些状态外部化,这使得程序的逻辑复杂化。
     因此,应当在有足够多的对象实例可供共享时 才值得使用享元模式

  • 应用:

    1. String A=“a”;

      String B =“a”;

      两个地址是一样的,只有一个实例。

    2. 围棋不变的状态是两种颜色——内部状态,变的是方位——外部状态,所以只需要两个实例就可以了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值