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 设计模式>>

  • 相关阅读:
    轻重搭配
    EF的优缺点
    使用bootstrap-select有时显示“Nothing selected”
    IIS发布 HTTP 错误 500.21
    js添加的元素无法触发click事件
    sql server查看表是否死锁
    sql server把一个库表的某个字段更新到另一张表的相同字段
    SQLSERVER排查CPU占用高的情况
    SQL server中如何按照某一字段中的分割符将记录拆成多条
    LINQ to Entities does not recognize the method 'System.DateTime AddDays(Double)' method, and this method cannot be translated into a store expression.
  • 原文地址:https://www.cnblogs.com/gzskys/p/5738803.html
Copyright © 2011-2022 走看看