徹底理解:裝飾者模式
網站新聞模組中應用裝飾模式
本人現今對設計模式特別感興趣,因為它的功能實在是太吸引人啦.設計模式一般說來是為了增加系統的可擴充套件性及維護性。在一般的入口網站中都會有新聞展示這個功能模組。
具體案例:有儲存在資料庫中的新聞,有儲存在XML檔案中的新聞(一般都是推薦新聞,內容比較少)。
在沒有接觸設計模式時,都是針對具體實現程式設計,讀取資料庫新聞時直接寫一個基於資料庫的方法,讀取推薦新聞時再寫一個基於XML的方法,兩種方法之間沒有任何的關係.當時覺的這樣實現也沒什麼不好。可是你總會發現這兩種方法在實現上有很多相同的地方,都是讀取出一個新聞標題集合來繫結資料來源,只是取資料來源的方法不同而已。為了方便管理,我們可以定義一個統一的介面來約束這兩種方法。這種做法也足夠滿足讀取不同載體的新聞要求。可是如果在讀取新聞時要做其它的操作呢?例如:給讀取出來的新聞的人氣加一。這個時候我們就要修改原程式,這樣有背於"對擴充套件開放,對修改關閉"的程式設計原則。如何解決呢?這就是裝飾者模式出場的時候了。
裝飾者模式
裝飾者模式類圖:
問題: 說裝飾者模式比用繼承會更富有彈性,在類圖中不是一樣用到了繼承了嗎?
說明:裝飾者和被裝飾者之間必須是一樣的型別,也就是要有共同的超類。在這裡應用繼承並不是實現方法的複製,而是實現型別的匹配。因為裝飾者和被裝飾者是同一個型別,因此裝飾者可以取代被裝飾者,這樣就使被裝飾者擁有了裝飾者獨有的行為。根據裝飾者模式的理念,我們可以在任何時候,實現新的裝飾者增加新的行為。如果是用繼承,每當需要增加新的行為時,就要修改原程式了。
說了一些裝飾者模式的類圖和概念,下面就要講新聞模組和裝飾者模式的關聯了。
例如:
解決方案:這裡我應用裝飾者模式設計來滿足這種不斷新增業務的需求:
1:定義根據不同載體讀取新聞方法的抽象類:
publicabstractclass Component_News
{
///<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
{
get{ returnthis._news_title; }
set{ this._news_title = value; }
}
///<summary>
/// 新聞內容
///</summary>
privatestring _news_content;
publicstring news_content
{
get{ returnthis._news_content; }
set{ this._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 設計模式>>