zoukankan      html  css  js  c++  java
  • 装饰者模式 (Decorator Pattern)

    装饰者模式:动态地给一个对象添加一些额外的职责,就增加功能来说,Decorator模式比生成子类更为灵活。

    Decorator模式的工作原理是:可以创建始于Decorator对象(负责新的功能的对象)终于原对象的一个对象“链”。

                                                             图1装饰者链

    装饰者模式隐含的是通过一条条装饰链去实现具体对象,每一条装饰链都始于一个Componet对象,每个装饰者对象后面紧跟着另一个装饰者对象,而对象链终于ConcreteComponet对象。

      装饰者模式 :动态地将责任附加到对象上.若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

      装饰者模式类图:

     问题:  说装饰者模式比用继承会更富有弹性,在类图中不是一样用到了继承了吗? 

     说明:装饰者和被装饰者之间必须是一样的类型,也就是要有共同的超类。在这里应用继承并不是实现方法的复制,而是实现类型的匹配。因为装饰者和被装饰者是同一个类型,因此装饰者可以取代被装饰者,这样就使被装饰者拥有了装饰者独有的行为。根据装饰者模式的理念,我们可以在任何时候,实现新的装饰者增加新的行为。如果是用继承,每当需要增加新的行为时,就要修改原程序了。

      具体案例:有存储在数据库中的新闻,有存储在XML文件中的新闻(一般都是推荐新闻,内容比较少)。

         在没有接触设计模式时,都是针对具体实现编程,读取数据库新闻时直接写一个基于数据库的方法,读取推荐新闻时再写一个基于XML的方法,两种方法之间没有任何的关系.当时觉的这样实现也没什么不好。可是你总会发现这两种方法在实现上有很多相同的地方,都是读取出一个新闻标题集合来绑定数据源,只是取数据源的方法不同而已。为了方便管理,我们可以定义一个统一的接口来约束这两种方法。这种做法也足够满足读取不同载体的新闻要求。可是如果在读取新闻时要做其它的操作呢?例如:给读取出来的新闻的人气加一。这个时候我们就要修改原程序,这样有背于"对扩展开放,对修改关闭"的编程原则。如何解决呢?这就是装饰者模式出场的时候了。

     为了说明一些装饰者模式的类图和概念,下面就要讲新闻模块和装饰者模式的关联了。

    例如:在展示新闻的同时给读取出来的新闻的人气加一,同时把新闻标题加入到RSS中,如果是用继承,就要往超类中增加相应的方法,如果是一个新增行为还是可以忍受的,如果此模块有不断增加业务的可能,那是不是要每次都要修改原程序呢?我想所有的朋友都不希望这样做。我们希望当有新的行为时才往旧对象上加,是在运行时加,并不是一开始就加。

    解决方案:这里我应用装饰者模式设计来满足这种不断新增业务的需求:

    1:定义根据不同载体读取新闻方法的抽象类:

    public abstract  class Component_News
        {
           /// <summary>
            /// 取新闻
            /// </summary>
            /// <returns></returns>
           public abstract List<News> getListOfNews();
        }
    View Code

    2:基于数据库读取新闻的类:

    public class DB_News : Component_News
        {
           /// <summary>
           /// 从数据库中取新闻
           /// </summary>
           /// <returns></returns>
           public override  List<News> getListOfNews()
           {
               List<News> _list = new List<News>();
               //从数据库中取得数据填充到_list 中
               Console.WriteLine("从数据库中取得数据填充到_list 中");
               return _list;
           
           }
        }
    View Code

    3:基于XML读取新闻:

    /// <summary>
            /// 从XML中取新闻
            /// </summary>
            /// <returns></returns>
            public override List<News> getListOfNews()
            {
                List<News> _list = new List<News>();
                //从XML中取得数据填充到_list 中
                Console.WriteLine("从XML中取得数据填充到_list 中");
                return _list;
    
            }
    View Code

    4:新闻实体类:

    public  class News
        {
           /// <summary>
           /// 新闻标题
           /// </summary>
           private string _news_title;
           public string news_title
           {
               get { return this._news_title; }
               set { this._news_title = value; }
           
           }
           /// <summary>
           /// 新闻内容
           /// </summary>
           private string _news_content;
           public string news_content
           {
               get { return this._news_content; }
               set { this._news_content = value; }
    
           }
        }
    View Code

    5:下面是抽象装饰类:

    public abstract class Decorator_News :Component_News 
        {
            /// <summary>
            /// 取新闻
            /// </summary>
            /// <returns></returns>
           public override List<News> getListOfNews()
           {
               return this._Component_News.getListOfNews();
    
           }
           private Component_News _Component_News;
           public Decorator_News(Component_News _Component_News2)
           {
               this._Component_News = _Component_News2;
           
           }
    
           
        }
    View Code

    6:扩展新闻组件: 给新闻添加人气的方法类:

    public  class ConcreteDecortor_AddAmount:Decorator_News 
        {
           public string    AddAmount()
           {
               //新闻人气加一
               Console.WriteLine("新闻人气已经加一");
               return "新闻人气已经加一";
           
           }
           public ConcreteDecortor_AddAmount(Component_News _Component_News): base(_Component_News)
           {
    
    
           }
           /// <summary>
           /// 重写父类中读取新闻的方法
           /// </summary>
           /// <returns></returns>
           public override List<News> getListOfNews()
           {
               //此处加入扩展代码:新闻人气加一
               AddAmount();
               return  base.getListOfNews();          
           
           }
    
        
    
    
        }
    View Code

    7:扩展新闻组件: 把新闻加入到RSS中的方法类:

    public  class ConcreteDecortor_AddRss : Decorator_News 
        {
           /// <summary>
           /// 将新闻标题加入到RSS中
           /// </summary>
           /// <returns></returns>
           public string AddRss()
           {
               //新闻标题已经被RSS收录
               Console.WriteLine("新闻标题已经加入到RSS中");
               return "";
           
           }
           public ConcreteDecortor_AddRss(Component_News _Component_News): base(_Component_News)
           {
    
    
           }
           /// <summary>
           /// 重写父类中读取新闻的方法
           /// </summary>
           /// <returns></returns>
           public override List<News> getListOfNews()
           {
               //此处加入扩展代码:将新闻标题加入到RSS中
               AddRss();
               return base.getListOfNews();
    
           }
        }
    View Code

    8:客户端调用:为了说明问题,本人就选用控制台程序来说明.

    static void Main(string[] args)
            {
                //数据库方式取新闻
                Component_News _Component_News = new DB_News();
                //扩展了展示新闻标题的同时增加此新闻人气的方法
                Decorator_News _Decorator_News = new ConcreteDecortor_AddAmount(_Component_News);
                //扩展了展示新闻标题的同时同时将新闻标题加入RSS中的方法
                _Decorator_News = new ConcreteDecortor_AddRss(_Decorator_News);
                _Decorator_News.getListOfNews();
                Console.ReadKey();
            }
    View Code

    9:运行效果:这样就可以为断的添加新的装饰者来装饰我们的新闻组件了,并不需要修改新闻组件,而只要添加新的类.Decorator模式采用对象组合大大的降低了系统的耦合度。

    装饰者模式的问题及解决:用装饰者实例化组件时,将增加代码的复杂度,一旦应用了装饰者模式,不只需要实例化组件,还要把组件包装进装饰者,而这样的装饰者有多少个是不确定的。这里可以应用工厂模式来实例化组件来简化操作。

         注:
         本文参考:<<Head First 设计模式>>

  • 相关阅读:
    装饰器的初识
    闭包
    匿名函数lambda
    内置函数Ⅱ
    Java生鲜电商平台-API接口设计之token、timestamp、sign 具体架构与实现(APP/小程序,传输安全)
    Java生鲜电商平台-电商中"再来一单"功能架构与详细设计(APP/小程序)
    Java生鲜电商平台-商品中心的架构设计与源码解析(小程序/APP)
    Java生鲜电商平台-生鲜电商小程序如何做好代码设计?(微信小程序/APP)
    Java生鲜电商平台-生鲜电商订单结算系统的深入解析与反思总结
    Java生鲜电商平台-生鲜电商高并发下的接口幂等性实现与代码讲解
  • 原文地址:https://www.cnblogs.com/gzskys/p/5738803.html
Copyright © 2011-2022 走看看