繼承做爲面向對象的三大要素(封裝、繼承、多態)之一爲何會帶來問題,問題如何解決而後造成一種設計模式,head frist設計模式書中以鴨子做爲例子講解什麼狀況下繼承的方式會帶來問題。首先有各類各樣的鴨子,那麼天然想到各類鴨子繼承自一個父類:父類爲Duck,現有綠頭鴨GreenHeadDuck和紅頭鴨RedHeadDuck算法
public abstract Class Duck{ public void quack(){} public void swin(){} public abstract void display(); } publci class GreenHeadDuck:Duck{ public overrid void display(){ //外觀綠頭 } } publci class ReadHeadDuck:Duck{ public overrid void display(){ //外觀紅頭 } }
父類中全部鴨子都會呱呱叫(quack)和游泳(swin),外觀卻不同因此display爲抽象方法,讓繼承它的子類重寫。如今須要新增一種鴨子,但這個鴨子是一個玩具橡皮鴨,咱們按照繼承的方式則橡皮鴨實現代碼以下編程
publci class RubberDuck:Duck{ public override void qucak(){ //覆蓋成吱吱吱叫 } public override void display(){ //外觀是橡皮鴨 } }
由於橡皮鴨是不會像其餘鴨子那樣叫的,因此上面代碼咱們須要重寫qucak覆蓋橡皮鴨叫的方式可。如今有一個需求,須要讓鴨子飛起來,按照繼承的方式咱們給父類Duck添加 fly方法便可讓全部鴨子飛起來。可是問題出現了,橡皮鴨是不會飛的,因而咱們能夠像覆蓋qucak方法同樣在RubberDuck中覆蓋fly方法。設計模式
public abstract class Duck{ public void quack(){} public void swin(){} public abstract void display(); public void fly(){} } publci class RubberDuck:Duck{ public override void qucak(){ //覆蓋成吱吱吱叫 } public override void display(){ //外觀是橡皮鴨 } public override void fly(){ //覆蓋,什麼都不作 } }
到這裏咱們已經發現了繼承帶來的問題:ide
一、代碼在多個子類中重複。函數
二、很難知道全部子類的行爲。學習
三、運行的子類行爲不容易改變。測試
四、改變會牽一髮動全身,引發其餘子類不想要的改變。this
每當有新的子類出現,就要檢查是否是須要覆蓋父類方法。好比再加一種木頭玩具的鴨子(DecoyDuck),那麼木頭鴨子不會叫也不會飛,咱們是否是須要覆蓋叫和飛的方法。spa
因爲quack和fly有可能變化,因此咱們將quack 和fly抽象成接口,可以叫和飛行的鴨子才按需求繼承接口本身實現方法。設計
利用接口能夠解決一部分問題(不在須要重寫不須要的方法),可是卻會形成代碼沒法複用,由於接口不具備實現,咱們要在每種子類中寫fly和quack。好比要在紅頭綠頭中寫"呱呱叫",要在橡皮鴨中寫"吱吱叫"。
通過上面的分析能夠引出設計模式的兩個原則
一、把會變化的部分封裝起來,讓其餘部分不會受到影響。
二、針對接口編程,而不是針對實現。
經過第一個設計原則咱們能夠取出易於變化的部分:鴨子的飛行行爲(fly)和呱呱叫的行爲(quack)。經過第二個設計原則咱們知道須要利用接口表明每一個行爲,好比FlyBehavior與QuackBehavior,而行爲的每一個實現都將實現其中的一個接口。這樣鴨子類就不會負責實現FlyBehavior與QuackBehavior,而是由行爲類來專門實現,不會綁死在鴨子的子類中。
而"針對接口編程"的意思是"針對超類型編程"。能夠明確地說明變量的聲明類型應該是超類,這意味着咱們在Duck父類中聲明的行爲變量爲 FlyBehavior,QuackBehavior,"針對接口編程"的關鍵就在於面向對象三要素之一的"多態",因爲多態咱們才能在調用超類的方法時執行的是實現類或子類的方法。因此咱們改造以前寫的Duck類,刪除Fly()和Quack(),加入FlyBehavior和QuackBehavior變量,並用另外兩個方法PerFormFly()和PerFormQuack()來執行兩個行爲。
public abstract Class Duck{ protected FlyBehavior flyBehavior; protected QuackBehavior quackBehavior; public void swin(){} public abstract void display(); public void PerFormFly(){ flyBehavior.fly(); } public void PerFormQuack(){ quackBehavior.quack(); } }
編寫相關類並測試:
1 //封裝飛行行爲 2 public interface FlyBehavior 3 { 4 public void fly(); 5 } 6 7 public class FlyWithWings : FlyBehavior 8 { 9 public void fly() 10 { 11 Console.WriteLine("用翅膀飛"); 12 } 13 } 14 public class FlyNoWay : FlyBehavior 15 { 16 public void fly() 17 { 18 Console.WriteLine("不飛,什麼也不作"); 19 } 20 } 21 22 //封裝叫聲行爲 23 public interface QuackBehavior 24 { 25 public void quack(); 26 } 27 28 public class Quack : QuackBehavior 29 { 30 public void quack() 31 { 32 Console.WriteLine("呱呱叫"); 33 } 34 } 35 public class Squack : QuackBehavior 36 { 37 public void quack() 38 { 39 Console.WriteLine("吱吱叫"); 40 } 41 } 42 public class MuteQuack : QuackBehavior 43 { 44 public void quack() 45 { 46 Console.WriteLine("不會叫"); 47 } 48 } 49 50 /// <summary> 51 /// 鴨子超類 52 /// </summary> 53 public abstract class Duck { 54 55 protected FlyBehavior flyBehavior; 56 protected QuackBehavior quackBehavior; 57 58 public void swin() { } 59 public abstract void display(); 60 public void PerFormFly() 61 { 62 flyBehavior.fly(); 63 } 64 public void PerFormQuack() 65 { 66 quackBehavior.quack(); 67 } 68 } 69 70 /// <summary> 71 /// 綠頭鴨 72 /// </summary> 73 public class GreenHeadDuck : Duck 74 { 75 public GreenHeadDuck() { 76 flyBehavior = new FlyWithWings(); 77 quackBehavior = new Quack(); 78 } 79 public override void display() 80 { 81 Console.WriteLine("綠頭鴨,個人頭頂有一片草原(*^_^*)"); 82 } 83 } 84 85 /// <summary> 86 /// 橡皮鴨 87 /// </summary> 88 public class RubberDuck : Duck 89 { 90 public RubberDuck() 91 { 92 flyBehavior = new FlyNoWay(); 93 quackBehavior = new Squack(); 94 } 95 public override void display() 96 { 97 Console.WriteLine("橡皮鴨"); 98 } 99 } 100 101 static void Main(string[] args) 102 { 103 //綠頭鴨 104 Duck greenHeadDuck = new GreenHeadDuck(); 105 greenHeadDuck.display(); 106 greenHeadDuck.PerFormQuack(); 107 greenHeadDuck.PerFormFly(); 108 Console.WriteLine("--------------------------"); 109 //橡皮鴨 110 Duck rubberDuck = new RubberDuck(); 111 rubberDuck.display(); 112 rubberDuck.PerFormQuack(); 113 rubberDuck.PerFormFly(); 114 Console.ReadKey(); 115 }
如上測試咱們在鴨子子類中經過構造方法實例化行爲類,但創建了一堆動態的功能沒有用到,是否能夠動態的設定行爲而不是在構造函數裏面實例化。因此咱們還能夠加入兩個設置行爲的方法 SetFlyBehavior和SetQuackBehavior,再次測試動態設定行爲。
/// <summary> /// 鴨子超類 /// </summary> public abstract class Duck { protected FlyBehavior flyBehavior; protected QuackBehavior quackBehavior; public void swin() { } public abstract void display(); public void SetFlyBehavior(FlyBehavior flyBehavior) { this.flyBehavior = flyBehavior; } public void SetQuackBehavior(QuackBehavior quackBehavior) { this.quackBehavior = quackBehavior; } public void PerFormFly() { flyBehavior.fly(); } public void PerFormQuack() { quackBehavior.quack(); } }
策略模式:定義了算法簇,分別封裝起來,讓它們之間能夠互相替換,此模式讓算法的變化獨立於使用算法的客戶
經過這張類圖能夠便於理解和記憶設計模式,後續會把其餘模式也分享出本身的學習和理解。