裝飾模式(5)

来源:http://www.cnblogs.com/xiaomowang/archive/2017/01/09/6249040.html
-Advertisement-
Play Games

本篇博文,給大家講解一下裝飾模式,還是老樣子,有一個簡單的例子逐步演繹 一、舉例 用一個簡單的控制台實現 一個人穿各種各樣衣服 的功能 然後我們會很自然的寫出一下代碼: 先寫一個Person類 然後客戶端調用這個Person類 這樣就寫完了。 二、演繹 ①現在,我各種裝扮都寫到了Person類中,有 ...


本篇博文,給大家講解一下裝飾模式,還是老樣子,有一個簡單的例子逐步演繹

一、舉例

用一個簡單的控制台實現 一個人穿各種各樣衣服 的功能

然後我們會很自然的寫出一下代碼:

先寫一個Person類

 1  class Person
 2     {
 3         private string name;
 4         public Person(string name)
 5         {
 6             this.name = name;
 7         }
 8         public void WearTshirts()
 9         {
10             Console.WriteLine("大T恤");
11         }
12         public void WearBigTrouser()
13         {
14             Console.WriteLine("跨褲");
15         }
16         public void WearSneakers()
17         {
18             Console.WriteLine("破球鞋");
19         }
20         public void WearSuit()
21         {
22             Console.WriteLine("西裝");
23         }
24         public void WearTie()
25         {
26             Console.WriteLine("領帶");
27         }
28         public void WearLeatherShoes()
29         {
30             Console.WriteLine("皮鞋");
31         }
32         public void Show()
33         {
34             Console.WriteLine($"{name}裝扮完畢!");
35         }
36     }

然後客戶端調用這個Person類

 1  static void Main(string[] args)
 2         {
 3             Person xmw = new Person("小魔王");
 4             Console.WriteLine("\n第一種裝扮");
 5             xmw.WearTshirts();
 6             xmw.WearBigTrouser();
 7             xmw.WearSneakers();
 8             xmw.Show();
 9             Console.WriteLine("\n第二種裝扮");
10             xmw.WearSuit();
11             xmw.WearTie();
12             xmw.WearLeatherShoes();
13             xmw.Show();
14             Console.ReadKey();
15         }

這樣就寫完了。

二、演繹

①現在,我各種裝扮都寫到了Person類中,有這樣一個問題,假如,有一天,我新加了一件衣服,比如"皮褲"哈哈,那麼,我該怎麼辦呢?

很多小伙伴就說了,這很容易啊,直接在Person類中增加一個“皮褲”的方法不就完事了嗎。

這的確可以解決這個問題,這也是大多數人解決這個問題的方法。但是,真正的高手可以看出裡面的弊端。

首先,給大家介紹程式設計中的一個重要原則——開放-封閉原則,這個原則講的是:軟體實體(類,模塊,函數等等)應該可以擴展,但是不可修改。也就是說對於擴展是開放的,對於修改是封閉的。

那麼,用 修改Person類來解決上述的問題就違背了開放-封閉原則。

那我們該如何解決上述這個問題呢?

於是,我們想到這樣一個設計方法:

 1  /// <summary>
 2     /// Person類
 3     /// </summary>
 4     class Person
 5     {
 6         private string name;
 7         public Person(string name)
 8         {
 9             this.name = name;
10         }
11         public void Show()
12         {
13             Console.WriteLine($"{name}裝扮完畢");
14         }
15     }
16     /// <summary>
17     /// 服飾抽象類
18     /// </summary>
19     abstract class Finery
20     {
21         public abstract void Show();
22     }
23     //各種服飾子類
24     class TShirts : Finery
25     {
26         public override void Show()
27         {
28             Console.WriteLine("大T恤");
29         }
30     }
31     class BigTrouser : Finery
32     {
33         public override void Show()
34         {
35             Console.WriteLine("跨褲");
36         }
37     }
38     class Sneakers : Finery
39     {
40         public override void Show()
41         {
42             Console.WriteLine("破球鞋");
43         }
44     }
45     class Surt : Finery
46     {
47         public override void Show()
48         {
49             Console.WriteLine("西裝");
50         }
51     }
52     class Tie : Finery
53     {
54         public override void Show()
55         {
56             Console.WriteLine("領帶");
57         }
58     }
59     class LeatherShoes : Finery
60     {
61         public override void Show()
62         {
63             Console.WriteLine("皮鞋");
64         }
65     }

上述,我們把各種各樣的服飾寫成單個的類。

客戶端:

 1  static void Main(string[] args)
 2         {
 3             Person xmw = new Person("小魔王");
 4             Console.WriteLine("\n第一種裝扮");
 5             Finery dtx = new TShirts();
 6             Finery kk = new BigTrouser();
 7             Finery pqx = new Sneakers();
 8             dtx.Show();
 9             kk.Show();
10             pqx.Show();
11             xmw.Show();
12             Console.WriteLine("\n第二種裝扮");
13             Finery xz = new Surt();
14             Finery ld = new Tie();
15             Finery px = new LeatherShoes();
16             xz.Show();
17             ld.Show();
18             px.Show();
19             xmw.Show();
20             Console.ReadKey();
21         }

這樣的話,如果要增加新的服飾,我只需要增加一個Finery 的子類就可以了。

在這裡,我們用到了繼承,用到了抽象,也做到了‘服飾’類與‘人’類的分離,的確進步不少,但還存在一些其他的問題。

什麼問題呢?我們來看一下這段代碼:

1             dtx.Show();
2             kk.Show();
3             pqx.Show();
4             xmw.Show();

怎麼看怎麼彆扭呢。在客戶端把穿的各種服飾一個一個的顯示出來。這就好比,在客戶面前,光著身子把各種服飾一件一件的穿上一樣。這豈不是太丟人?我總不能當著客戶的面一件一件的穿衣服吧,我應該在別的地方一件一件的穿好了衣服,然後再去見客戶。

所以,針對上述問題,我們應該在內部組裝完畢之後,在整體展現出來。(類似於建造者模式,但有所不同,最大的區別在於,建造者模式中的建造過程是穩定的,而我們這個例子中穿各種服飾的過程是不穩定的。在之後的博文中我會講到建造者模式。)

我們可以想象一下穿衣服打扮的場景過程:我們從一大堆服飾中挑選自己習慣的來穿上,但是,穿衣服也是有順序的,比如,先穿內褲,再穿毛褲。。。。但是,你可以玩個性的,先穿毛褲,再穿內褲。。。額,想想下麵就刺撓。。

如果我們想要實現上述場景的功能,該如何設計呢? 這貌似很難哦。

不要怕,接下來我們要講的裝飾模式可以很好的解決上述問題。

 首先,我們來看一下裝飾模式的基本構成和作用。

 1     /// <summary>
 2     /// 此介面可以對這些對象動態的添加職責
 3     /// </summary>
 4     abstract class Component
 5     {
 6         public abstract void Operation();
 7     }
 8     /// <summary>
 9     /// 具體的對象,可以給這個對象添加一些職責
10     /// </summary>
11     class ConcreteComponent : Component
12     {
13         public override void Operation()
14         {
15             Console.WriteLine("具體對象的操作");
16         }
17     }
18     /// <summary>
19     /// 裝飾類,繼承Component,目的是從外類來擴展繼承Component類的功能
20     /// 對於Component來說,無須知道Decorator的存在。
21     /// </summary>
22     abstract class Decorator : Component
23     {
24         protected Component component;
25         //設置Componnet
26         public void SetComponent(Component component)
27         {
28             this.component = component;
29         }
30         //重寫Operation(),實際執行的是Component的Operation()
31         public override void Operation()
32         {
33             if (component != null)
34             {
35                 component.Operation();
36             }
37         }
38     }
39     /// <summary>
40     /// 具體的裝飾對象,起到給Component添加功能的作用。
41     /// </summary>
42     class ConcreteDecoratorA : Decorator
43     {
44         private string addedState;//本類獨有的方法,以區別與ConcreteDecoratorB
45         public override void Operation()
46         {
47             //首先運行原Component的Operation(),再執行本類的功能addedSate,相當於對原Component進行了裝飾
48             base.Operation();
49             addedState = "New State";
50             Console.WriteLine("具體裝飾對象A的操作");
51         }
52     }
53     class ConcreteDecoratorB : Decorator
54     {
55         public override void Operation()
56         {
57             //首先運行原Component的Operation(),再執行本類的功能 AddedBehavior(),相當於對原Component進行了裝飾
58             base.Operation();
59             AddedBehavior();
60             Console.WriteLine("具體裝飾對象B的操作");
61         }
62 
63         private void AddedBehavior()
64         {
65             //本類獨有的方法,以區別與ConcreteDecoratorA
66         }
67     }

根據功能的擴展需要,可能還有ConcreteDecoratorC,ConcreteDecoratorD,等等這樣的添加功能作用的類。

上述代碼就是裝飾模式的基本形式。一定要理解哦,註釋已經很明確了哦。

 客戶端:

 1   static void Main(string[] args)
 2         {
 3             ConcreteComponent c = new ConcreteComponent();
 4             ConcreteDecoratorA d1 = new ConcreteDecoratorA();
 5             ConcreteDecoratorB d2 = new ConcreteDecoratorB();
 6             //裝飾的方法是:
 7             //首先用ConcreteComponent實例化對象c,
 8             //然後用ConcreteDecoratorA的實例化對象d1來包裝c,
 9             //再用ConcreteDecoratorB的對象d2包裝d1.最終執行d2的Operation()
10             d1.SetComponent(c);
11             d2.SetComponent(d1);
12             d2.Operation();
13             Console.ReadKey();
14         }

 下麵,我們將裝飾模式運用到一開始舉的案例中。

 1    class Person
 2     {
 3         //構造函數
 4         public Person()
 5         {
 6 
 7         }
 8         private string name;
 9         //構造函數
10         public Person(string name)
11         {
12             this.name = name;
13         }
14         public virtual void Show()
15         {
16             Console.WriteLine($"{name}裝扮完畢");
17         }
18     }
19 
20     class Finery : Person
21     {
22         protected Person component;
23         //設置Component
24         public void Decorate(Person component)
25         {
26             this.component = component;
27         }
28         //重寫Show()方法,實際執行的是Component中的Show()
29         public override void Show()
30         {
31             if (component!=null)
32             {
33                 component.Show();
34             }
35         }
36     }
37 
38     class TShirts:Finery
39     {
40        
41         public override void Show()
42         {
43             //先執行本類中的功能
44             Console.WriteLine("大T恤");
45             //在執行 Component 中的Show();
46             base.Show();
47 
48             //相當於對原Component進行裝飾
49         }
50     }
51     class BigTrouser:Finery
52     {
53         //同TShirts類
54 
55         public override void Show()
56         {
57             Console.WriteLine("垮褲");
58             base.Show();
59         }
60     }
61     
62     //***************其餘類似裝扮,省略**********

相比裝飾模式模板的代碼,我們少了兩個抽象類,這裡我麽你要學會變通,一些不必要的類,我們可以省略,思路對即可。沒有必要照葫蘆畫瓢,只有對設計模式達到靈活運用的時候,我們才能接近高手。

客戶端調用

 1   static void Main(string[] args)
 2         {
 3             Person xmw = new Person("小魔王");
 4             Console.WriteLine("第一種裝扮");
 5             BigTrouser bg = new BigTrouser();
 6             TShirts ts = new TShirts();
 7             bg.Decorate(xmw);
 8             ts.Decorate(bg);
 9             ts.Show();
10             Console.ReadKey();
11         }

ok,裝飾模式講完了,一句話總結一下:裝飾模式是為已有功能動態添加更多功能的一種方式

好了,今天就到這吧,下一篇,會講 代理模式


 

本系列將持續更新,喜歡的小伙伴可以點一下關註和推薦,謝謝大家的支持。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言 一切準備工作就緒時就先實現一個關註公眾號後向客戶端推送一條消息。關註後推送消息需要一個get請求、一個post請求,get請求主要是為了向微信伺服器驗證,post請求主要就是處理微信消息了。 調介面時傳遞的appid和appsecret請傳遞自己公眾號對應的參數。 微信事件交互 微信事件交互主 ...
  • 淺談類和對象的概念 一、什麼是類?什麼是對象? 學習一門面向對象編程語言,我們必須得知道什麼是類?什麼是對象? 類(Class)實際上是對某種類型的對象定義變數和方法的原型。它表示對現實生活中一類具有共同特征的事物的抽象,是面向對象編程的基礎。 簡單地說,類是一種抽象的數據類型,是對一類對象的統一描 ...
  • 前言 最初打算熟悉下微信開發打算用node.js開發,結果底氣不足先用C#開發,先踩了踩坑。 準備工作 微信公眾平臺開發者文檔。 這個先多看幾遍。 測試公眾號,申請開通後會看到微信號,appID,appsecret。開通後可以看到掃描下方的二維碼關註該測試公眾號 功能變數名稱。功能變數名稱和伺服器的話大家可以買阿裡 ...
  • 靜態、非靜態 先來看一段代碼來區分靜態與非靜態: 可以看出靜態與非靜態的區別在於 關鍵字 : static 靜態和非靜態的區別: 1)、在非靜態類中,既可以有實例成員,也可以有靜態成員。 2)、在調用實例成員的時候,需要使用對象名.實例成員; 在調用靜態成員的時候,需要使用類名.靜態成員名; 3)、 ...
  • 一.方法: ContainerFromIndex:返回 ItemCollection 中指定索引處的項的容器。 ContainerFromItem:返回與制定的項對應的容器(ComboxItem等條目控制項)。 Equals(Object):確定製定的Object是否等於當前的Object。 Fina ...
  • Linq之Expression進階 Lambda表達式 "Lambda表達式"是一個匿名函數,是一種高效的類似於函數式編程的表達式,Lambda簡化了開發中需要編寫的代碼量。它可以包含表達式和語句,並且可用於創建委托或表達式目錄樹類型,支持帶有可綁定到委托或表達式樹的輸入參數的內聯表達式。所有Lam ...
  • 委托是一個類,它定義了方法的類型,使得可以將方法當作另一個方法的參數來進行傳遞。事件是一種特殊的委托。 1.委托的聲明 (1). delegate delegate我們常用到的一種聲明 Delegate至少0個參數,至多32個參數,可以無返回值,也可以指定返回值類型。 例:public delega ...
  • APS.NET MVC中(以下簡稱“MVC”)的每一個請求,都會分配給相應的控制器和對應的行為方法去處理,而在這些處理的前前後後如果想再加一些額外的邏輯處理。這時候就用到了過濾器。 MVC支持的過濾器類型有四種,分別是:Authorization(授權),Action(行為),Result(結果)和 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...