C#設計模式(9)——裝飾者模式(Decorator Pattern)

一、引言

在軟件開發中,我們經常想要對一類對象添加不同的功能,例如要給手機添加貼膜,手機掛件,手機外殼等,如果此時利用繼承來實現的話,就需要定義無數的類,如StickerPhone(貼膜是手機類)、AccessoriesPhone(掛件手機類)等,這樣就會導致 ”子類爆炸“問題,為了解決這個問題,我們可以使用裝飾者模式來動態地給一個對象添加額外的職責。下面讓我們看看裝飾者模式。

二、裝飾者模式的詳細介紹

2.1 定義

裝飾者模式以對客戶透明的方式動態地給一個對象附加上更多的責任,裝飾者模式相比生成子類可以更靈活地增加功能。

2.2 裝飾者模式實現

這里以手機和手機配件的例子來演示裝飾者模式的實現,具體代碼如下:

/// <summary>/// 手機抽象類,即裝飾者模式中的抽象組件類/// </summary>public abstract class Phone{public abstract void Print();}/// <summary>/// 蘋果手機,即裝飾著模式中的具體組件類/// </summary>public class ApplePhone:Phone{/// <summary>/// 重寫基類方法/// </summary>public override void Print(){Console.WriteLine("開始執行具體的對象——蘋果手機");}}/// <summary>/// 裝飾抽象類,要讓裝飾完全取代抽象組件,所以必須繼承自Photo/// </summary>public abstract class Decorator:Phone{private Phone phone;public Decorator(Phone p){this.phone = p;}public override void Print(){if (phone != null){phone.Print();}}}/// <summary>/// 貼膜,即具體裝飾者/// </summary>public class Sticker : Decorator{public Sticker(Phone p): base(p){ }public override void Print(){base.Print();// 添加新的行為
            AddSticker();      }/// <summary>/// 新的行為方法/// </summary>public void AddSticker(){Console.WriteLine("現在蘋果手機有貼膜了");}}/// <summary>/// 手機掛件/// </summary>public class Accessories : Decorator{public Accessories(Phone p): base(p){}public override void Print(){base.Print();// 添加新的行為
            AddAccessories();          }/// <summary>/// 新的行為方法/// </summary>public void AddAccessories(){Console.WriteLine("現在蘋果手機有漂亮的掛件了");}}

此時客戶端調用代碼如下:

class Customer{static void Main(string[] args){// 我買了個蘋果手機Phone phone = new ApplePhone();// 現在想貼膜了Decorator applePhoneWithSticker = new Sticker(phone);// 擴展貼膜行為
            applePhoneWithSticker.Print();Console.WriteLine("----------------------\n");// 現在我想有掛件了Decorator applePhoneWithAccessories = new Accessories(phone);// 擴展手機掛件行為
            applePhoneWithAccessories.Print();Console.WriteLine("----------------------\n");// 現在我同時有貼膜和手機掛件了Sticker sticker = new Sticker(phone);Accessories applePhoneWithAccessoriesAndSticker = new Accessories(sticker);applePhoneWithAccessoriesAndSticker.Print();Console.ReadLine();}

從上面的客戶端代碼可以看出,客戶端可以動態地將手機配件增加到手機上,如果需要添加手機外殼時,此時只需要添加一個繼承Decorator的手機外殼類,從而,裝飾者模式擴展性也非常好。

2.3 裝飾者模式的類圖

實現完了裝飾者模式之后,讓我們看看裝飾者模式實現中類之間的關系,具體見下圖:

在裝飾者模式中各個角色有:

  • 抽象構件(Phone)角色:給出一個抽象接口,以規范準備接受附加責任的對象。
  • 具體構件(AppPhone)角色:定義一個將要接收附加責任的類。
  • 裝飾(Dicorator)角色:持有一個構件(Component)對象的實例,并定義一個與抽象構件接口一致的接口。
  • 具體裝飾(Sticker和Accessories)角色:負責給構件對象 ”貼上“附加的責任。

?

三、裝飾者模式的優缺點

看完裝飾者模式的詳細介紹之后,我們繼續分析下它的優缺點。

優點:

  1. 裝飾這模式和繼承的目的都是擴展對象的功能,但裝飾者模式比繼承更靈活
  2. 通過使用不同的具體裝飾類以及這些類的排列組合,設計師可以創造出很多不同行為的組合
  3. 裝飾者模式有很好地可擴展性

缺點:裝飾者模式會導致設計中出現許多小對象,如果過度使用,會讓程序變的更復雜。并且更多的對象會是的差錯變得困難,特別是這些對象看上去都很像。

四、使用場景

下面讓我們看看裝飾者模式具體在哪些情況下使用,在以下情況下應當使用裝飾者模式:

  1. 需要擴展一個類的功能或給一個類增加附加責任。
  2. 需要動態地給一個對象增加功能,這些功能可以再動態地撤銷。
  3. 需要增加由一些基本功能的排列組合而產生的非常大量的功能

五、.NET中裝飾者模式的實現

在.NET 類庫中也有裝飾者模式的實現,該類就是System.IO.Stream,下面看看Stream類結構:

上圖中,BufferedStream、CryptoStream和GZipStream其實就是兩個具體裝飾類,這里的裝飾者模式省略了抽象裝飾角色(Decorator)。下面演示下客戶端如何動態地為MemoryStream動態增加功能的。

 MemoryStream memoryStream = new MemoryStream(new byte[] {95,96,97,98,99});// 擴展緩沖的功能BufferedStream buffStream = new BufferedStream(memoryStream);// 添加加密的功能CryptoStream cryptoStream = new CryptoStream(memoryStream,new AesManaged().CreateEncryptor(),CryptoStreamMode.Write);// 添加壓縮功能GZipStream gzipStream = new GZipStream(memoryStream, CompressionMode.Compress, true);

六、總結

到這里,裝飾者模式的介紹就結束了,裝飾者模式采用對象組合而非繼承的方式實現了再運行時動態地擴展對象功能的能力,而且可以根據需要擴展多個功能,避免了單獨使用繼承帶來的 ”靈活性差“和”多子類衍生問題“。同時它很好地符合面向對象設計原則中 ”優先使用對象組合而非繼承“和”開放-封閉“原則

?

本專題所有源碼:設計模式之裝飾者模式

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/388545.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/388545.shtml
英文地址,請注明出處:http://en.pswp.cn/news/388545.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

北大青鳥c語言課后答案,北大青鳥C語言教程--第一章 C語言基礎.ppt

《北大青鳥C語言教程--第一章 C語言基礎.ppt》由會員分享&#xff0c;可在線閱讀&#xff0c;更多相關《北大青鳥C語言教程--第一章 C語言基礎.ppt(20頁珍藏版)》請在人人文庫網上搜索。1、第一章,C 語言基礎,2,課程地位,.Net ,以 # 開始的語句稱為預處理器指令,#include語句不…

nosql_探索NoSQL系列

nosql數據科學 (Data Science) Knowledge on NoSQL databases seems to be an increasing requirement in data science applications, yet, the taxonomy is so diverse and problem-centered that it can be a challenge to grasp them. This post attempts to shed light on…

C++TCP和UDP屬于傳輸層協議

TCP和UDP屬于傳輸層協議。其中TCP提供IP環境下的數據可靠傳輸&#xff0c;它事先為要發送的數據開辟好連接通道&#xff08;三次握手&#xff09;&#xff0c;然后再進行數據發送&#xff1b;而UDP則不為IP提供可靠性&#xff0c;一般用于實時的視頻流傳輸&#xff0c;像rtp、r…

程序員如何利用空閑時間掙零花錢

一&#xff1a; 私活 作為一名程序員&#xff0c;在上班之余&#xff0c;我們有大把的時間&#xff0c;不能浪費&#xff0c;這些時間其實都是可以用來挖掘自己潛在的創造力&#xff0c;今天要討論的話題就是&#xff0c;程序員如何利用空余時間掙零花錢&#xff1f;比如說周末…

編寫程序乘法口訣表C語言,陳廣川問:c語言編程九九乘法口訣表 怎樣用c語言寫九九乘法口訣表?...

怎樣用c語言寫九九乘法口訣表&#xff1f;哈哈&#xff0c;我剛剛用javascript寫好乘法口訣表。C語言&#xff0c;如何編寫程序輸出九九乘法表。形式如下 ********* ******** ******* ****** ***** **** *** ** *&#xff1f;兩個循環&#xff0c;一般用for循環 一個循環控制行…

PHP中文亂碼解決辦法

一&#xff0e;首先是PHP網頁的編碼 1. php文件本身的編碼與網頁的編碼應匹配 a. 如果欲使用gb2312編碼&#xff0c;那么php要輸出頭&#xff1a;header(“Content-Type: text/html; charsetgb2312")&#xff0c;靜態頁面添加<meta http-equiv"Content-T…

python中api_通過Python中的API查找相關的工作技能

python中api工作技能世界 (The World of Job Skills) So you want to figure out where your skills fit into today’s job market. Maybe you’re just curious to see a comprehensive constellation of job skills, clean and standardized. Or you need a taxonomy of ski…

欺詐行為識別_使用R(編程)識別欺詐性的招聘廣告

欺詐行為識別背景 (Background) Online recruitment fraud (ORF) is a form of malicious behaviour that aims to inflict loss of privacy, economic damage or harm the reputation of the stakeholders via fraudulent job advertisements.在線招聘欺詐(ORF)是一種惡意行為…

PE文件的感染C++源代碼

PE文件的感染C源代碼 PE文件規定了可執行文件的格式&#xff0c;凡是符合此格式的文件都能在windows系統上運行。PE文件的格式暫且不談&#xff0c;說一些感染PE文件的幾種途徑。 導入表感染。這個涉及比較復雜的操作&#xff0c;首先&#xff0c;要自行寫一個dll文件&#x…

c語言實驗四報告,湖北理工學院14本科C語言實驗報告實驗四數組

湖北理工學院14本科C語言實驗報告實驗四 數組.doc實驗四 數 組實驗課程名C語言程序設計專業班級 14電氣工程2班 學號 201440210237 姓名 熊帆 實驗時間 5.12-5.26 實驗地點 K4-208 指導教師 祁文青 一、實驗目的和要求1. 掌握一維數組和二維數組的定義、賦值和輸入輸出的方法&a…

c語言宏定義

一. #define是C語言中提供的宏定義命令&#xff0c;其主要目的是為程序員在編程時提供一定的方便&#xff0c;并能在一定程度上提高程序的運行效率&#xff0c;但學生在學習時往往不能理解該命令的本質&#xff0c;總是在此處產生一些困惑&#xff0c;在編程時誤用該命令&#…

rabbitmq channel參數詳解【轉】

1、Channel 1.1 channel.exchangeDeclare()&#xff1a; type&#xff1a;有direct、fanout、topic三種durable&#xff1a;true、false true&#xff1a;服務器重啟會保留下來Exchange。警告&#xff1a;僅設置此選項&#xff0c;不代表消息持久化。即不保證重啟后消息還在。原…

感染EXE文件代碼(C++)

C代碼#include <windows.h> #include <winnt.h> #include <stdio.h> #include <assert.h> #define DEBUG 1 #define EXTRA_CODE_LENGTH 18 #define SECTION_SIZE 0x1000 #define SECTION_NAME ".eViLhsU" #define F…

nlp gpt論文_GPT-3:NLP鎮的最新動態

nlp gpt論文什么是GPT-3&#xff1f; (What is GPT-3?) The launch of Open AI’s 3rd generation of the pre-trained language model, GPT-3 (Generative Pre-training Transformer) has got the data science fraternity buzzing with excitement!Open AI的第三代預訓練語言…

真實不裝| 阿里巴巴新人上路指北

新手上路&#xff0c;總想聽聽前輩們分享他們走過的路。橙子選取了阿里巴巴合伙人逍遙子&#xff08;阿里巴巴集團CEO&#xff09; 、Eric&#xff08;螞蟻金服董事長兼CEO&#xff09;、Judy&#xff08;阿里巴巴集團CPO&#xff09;的幾段分享&#xff0c;他們是如何看待職場…

小程序學習總結

上個周末抽空了解了一下小程序,現在將所學所感記錄以便日后翻看;需要指出的是我就粗略過了下小程序的api了解了下小程序的開發流程以及工具的使用,然后寫了一個小程序的demo;在我看來,如果有前端基礎學習小程序無異于錦上添花了,而我這個三年的碼農雖也寫過不少前端代碼但離專業…

tomcat java環境配置

jsp 環境變量配置 一、配置JDK 首先&#xff0c;從Sun網站上下載jdk。 雙擊jdk-1_5_0_04-windows-i586-p.exe開始安裝&#xff0c;默認安裝到C:/Program Files/Java/jdk1.5.0_04&#xff0c;你也可以更改路徑&#xff0c;但要記住最后選擇的路徑&#xff0c;設置環境變量的時候…

uber 數據可視化_使用R探索您在Uber上的活動:如何分析和可視化您的個人數據歷史記錄

uber 數據可視化Perhaps, dear reader, you are too young to remember that before, the only way to request a particular transport service such as a taxi was to raise a hand to make a signal to an available driver, who upon seeing you would stop if he was not …

java B2B2C springmvc mybatis電子商城系統(四)Ribbon

2019獨角獸企業重金招聘Python工程師標準>>> 一&#xff1a;Ribbon是什么&#xff1f; Ribbon是Netflix發布的開源項目&#xff0c;主要功能是提供客戶端的軟件負載均衡算法&#xff0c;將Netflix的中間層服務連接在一起。Ribbon客戶端組件提供一系列完善的配置項如…

c語言函數的形參有幾個,C中子函數最多有幾個形參

該樓層疑似違規已被系統折疊 隱藏此樓查看此樓C89 31個&#xff0c;C99 127個。ANSI C892.2.4.1 Translation limitsThe implementation shall be able to translate and execute at least one program that contains at least one instance of every one of the following lim…