1. 程式人生 > >徹底理解:裝飾者模式

徹底理解:裝飾者模式

     網站新聞模組應用裝飾模式 

     本人現今對設計模式特別感興趣,因為它的功能實在是太吸引人啦.設計模式一般說來是為了增加系統的可擴充套件性及維護性。在一般的入口網站中都會有新聞展示這個功能模組。

     具體案例:有儲存在資料庫中的新聞,有儲存在XML檔案中的新聞(一般都是推薦新聞,內容比較少)。

     在沒有接觸設計模式時,都是針對具體實現程式設計,讀取資料庫新聞時直接寫一個基於資料庫的方法,讀取推薦新聞時再寫一個基於XML的方法,兩種方法之間沒有任何的關係.當時覺的這樣實現也沒什麼不好。可是你總會發現這兩種方法在實現上有很多相同的地方,都是讀取出一個新聞標題集合來繫結資料來源,只是取資料來源的方法不同而已。為了方便管理,我們可以定義一個統一的介面來約束這兩種方法。這種做法也足夠滿足讀取不同載體的新聞要求。可是如果在讀取新聞時要做其它的操作呢?例如:給讀取出來的新聞的人氣加一。這個時候我們就要修改原程式,這樣有背於"對擴充套件開放,對修改關閉"的程式設計原則。如何解決呢?這就是裝飾者模式出場的時候了。

     裝飾者模式 

:動態地將責任附加到物件上.若要擴充套件功能,裝飾者提供了比繼承更有彈性的替代方案。

裝飾者模式類圖:
          
  
  

    問題:  說裝飾者模式比用繼承會更富有彈性,在類圖中不是一樣用到了繼承了嗎? 

    說明:裝飾者和被裝飾者之間必須是一樣的型別,也就是要有共同的超類。在這裡應用繼承並不是實現方法的複製,而是實現型別的匹配。因為裝飾者和被裝飾者是同一個型別,因此裝飾者可以取代被裝飾者,這樣就使被裝飾者擁有了裝飾者獨有的行為。根據裝飾者模式的理念,我們可以在任何時候,實現新的裝飾者增加新的行為。如果是用繼承,每當需要增加新的行為時,就要修改原程式了。

     說了一些裝飾者模式的類圖和概念,下面就要講新聞模組和裝飾者模式的關聯了。

    例如:

在展示新聞的同時給讀取出來的新聞的人氣加一,同時把新聞標題加入到RSS中,如果是用繼承,就要往超類中增加相應的方法,如果是一個新增行為還是可以忍受的,如果此模組有不斷增加業務的可能,那是不是要每次都要修改原程式呢?我想所有的朋友都不希望這樣做。我們希望當有新的行為時才往舊物件上加,是在執行時加,並不是一開始就加。
    
    解決方案:這裡我應用裝飾者模式設計來滿足這種不斷新增業務的需求:

    
    1:定義根據不同載體讀取新聞方法的抽象類:
   


publicabstractclass Component_News
    
{
       
///<summary>
        
/// 取新聞
        
///</summary>
        
///<returns></returns>

publicabstract List<News> getListOfNews();
    }

    2:基於資料庫讀取新聞的類:


publicclass DB_News : Component_News
    
{
       
///<summary>
       
/// 從資料庫中取新聞
       
///</summary>
       
///<returns></returns>

publicoverride  List<News> getListOfNews()
       
{
           List
<News> _list =new List<News>();
           
//從資料庫中取得資料填充到_list 中
           Console.WriteLine("從資料庫中取得資料填充到_list 中");
           
return _list;
       
       }

    }

    3:基於XML讀取新聞:


///<summary>
        
/// 從XML中取新聞
        
///</summary>
        
///<returns></returns>

publicoverride List<News> getListOfNews()
        
{
            List
<News> _list =new List<News>();
            
//從XML中取得資料填充到_list 中
            Console.WriteLine("從XML中取得資料填充到_list 中");
            
return _list;

        }

    4:新聞實體類:


publicclass News
    
{
       
///<summary>
       
/// 新聞標題
       
///</summary>

privatestring _news_title;
       
publicstring news_title
       
{
           
getreturnthis._news_title; }
           
setthis._news_title = value; }
       
       }

       
///<summary>
       
/// 新聞內容
       
///</summary>

privatestring _news_content;
       
publicstring news_content
       
{
           
getreturnthis._news_content; }
           
setthis._news_content = value; }

       }

    }

    5:下面是抽象裝飾類:


publicabstractclass Decorator_News :Component_News 
    
{
        
///<summary>
        
/// 取新聞
        
///</summary>
        
///<returns></returns>

publicoverride List<News> getListOfNews()
       
{
           
returnthis._Component_News.getListOfNews();

       }

       
private Component_News _Component_News;
       
public Decorator_News(Component_News _Component_News2)
       
{
           
this._Component_News = _Component_News2;
       
       }


       
    }

    6:擴充套件新聞元件: 給新聞新增人氣的方法類:


publicclass ConcreteDecortor_AddAmount:Decorator_News 
    
{
       
publicstring    AddAmount()
       
{
           
//新聞人氣加一
           Console.WriteLine("新聞人氣已經加一");
           
return"新聞人氣已經加一";
       
       }

       
public ConcreteDecortor_AddAmount(Component_News _Component_News): base(_Component_News)
       
{


       }

       
///<summary>
       
/// 重寫父類中讀取新聞的方法
       
///</summary>
       
///<returns></returns>

publicoverride List<News> getListOfNews()
       
{
           
//此處加入擴充套件程式碼:新聞人氣加一
           AddAmount();
           
returnbase.getListOfNews();          
       
       }


    


    }

    7:擴充套件新聞元件: 把新聞加入到RSS中的方法類:


publicclass ConcreteDecortor_AddRss : Decorator_News 
    
{
       
///<summary>
       
/// 將新聞標題加入到RSS中
       
///</summary>
       
///<returns></returns>

publicstring AddRss()
       
{
           
//新聞標題已經被RSS收錄
           Console.WriteLine("新聞標題已經加入到RSS中");
           
return"";
       
       }

       
public ConcreteDecortor_AddRss(Component_News _Component_News): base(_Component_News)
       
{


       }

       
///<summary>
       
/// 重寫父類中讀取新聞的方法
       
///</summary>
       
///<returns></returns>

publicoverride List<News> getListOfNews()
       
{
           
//此處加入擴充套件程式碼:將新聞標題加入到RSS中
           AddRss();
           
returnbase.getListOfNews();

       }

    }

    8:客戶端呼叫:為了說明問題,本人就選用控制檯程式來說明.


staticvoid 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();
        }

    9:執行效果:這樣就可以為斷的新增新的裝飾者來裝飾我們的新聞元件了,並不需要修改新聞元件,而只要新增新的類.Decorator模式採用物件組合大大的降低了系統的耦合度。



  
      裝飾者模式的問題及解決:用裝飾者例項化元件時,將增加程式碼的複雜度,一旦應用了裝飾者模式,不只需要例項化元件,還要把元件包裝進裝飾者,而這樣的裝飾者有多少個是不確定的。這裡可以應用工廠模式來例項化元件來簡化操作。

     注:
        本文參考:<<Head First 設計模式>>