裝飾模式(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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...