本篇博文,給大家講解一下裝飾模式,還是老樣子,有一個簡單的例子逐步演繹 一、舉例 用一個簡單的控制台實現 一個人穿各種各樣衣服 的功能 然後我們會很自然的寫出一下代碼: 先寫一個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,裝飾模式講完了,一句話總結一下:裝飾模式是為已有功能動態添加更多功能的一種方式
好了,今天就到這吧,下一篇,會講 代理模式
本系列將持續更新,喜歡的小伙伴可以點一下關註和推薦,謝謝大家的支持。