1、繼承帶來的擴(kuò)展和復(fù)用問題
繼承作為面向?qū)ο蟮娜笠?封裝、繼承、多態(tài))之一為什么會(huì)帶來問題,問題如何解決然后形成一種設(shè)計(jì)模式,head
frist設(shè)計(jì)模式書中以鴨子作為例子講解什么情況下繼承的方式會(huì)帶來問題。首先有各種各樣的鴨子,那么自然想到各種鴨子繼承自一個(gè)父類:父類為Duck,現(xiàn)有綠頭鴨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(){ //外觀紅頭 } }
父類中所有鴨子都會(huì)呱呱叫(quack)和游泳(swin),外觀卻不一樣所以display為抽象方法,讓繼承它的子類重寫?,F(xiàn)在需要新增一種鴨子,但這個(gè)鴨子是一個(gè)玩具橡皮鴨,我們按照繼承的方式則橡皮鴨實(shí)現(xiàn)代碼如下
publci class RubberDuck:Duck{ public override void qucak(){ //覆蓋成吱吱吱叫 }
public override void display(){ //外觀是橡皮鴨 } }
因?yàn)橄鹌喪遣粫?huì)像其他鴨子那樣叫的,所以上面代碼我們需要重寫qucak覆蓋橡皮鴨叫的方式可。現(xiàn)在有一個(gè)需求,需要讓鴨子飛起來,按照繼承的方式我們給父類Duck添加
fly方法即可讓所有鴨子飛起來。但是問題出現(xiàn)了,橡皮鴨是不會(huì)飛的,于是我們可以像覆蓋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(){ //覆蓋,什么都不做 } }
到這里我們已經(jīng)發(fā)現(xiàn)了繼承帶來的問題:
1、代碼在多個(gè)子類中重復(fù)。
2、很難知道所有子類的行為。
3、運(yùn)行的子類行為不容易改變。
4、改變會(huì)牽一發(fā)動(dòng)全身,引起其他子類不想要的改變。
每當(dāng)有新的子類出現(xiàn),就要檢查是不是需要覆蓋父類方法。比如再加一種木頭玩具的鴨子
(DecoyDuck),那么木頭鴨子不會(huì)叫也不會(huì)飛,我們是不是需要覆蓋叫和飛的方法。
2、進(jìn)一步的改進(jìn),利用接口
由于quack和fly有可能變化,所以我們將quack 和fly抽象成接口,能夠叫和飛行的鴨子才按需求繼承接口自己實(shí)現(xiàn)方法。
利用接口可以解決一部分問題(不在需要重寫不需要的方法
),但是卻會(huì)造成代碼無法復(fù)用,因?yàn)榻涌诓痪哂袑?shí)現(xiàn),我們要在每種子類中寫fly和quack。比如要在紅頭綠頭中寫"呱呱叫",要在橡皮鴨中寫"吱吱叫"。
3、進(jìn)一步改進(jìn),策略模式
經(jīng)過上面的分析可以引出設(shè)計(jì)模式的兩個(gè)原則
1、把會(huì)變化的部分封裝起來,讓其他部分不會(huì)受到影響。
2、針對(duì)接口編程,而不是針對(duì)實(shí)現(xiàn)。
通過第一個(gè)設(shè)計(jì)原則我們可以取出易于變化的部分:鴨子的飛行行為(fly)和呱呱叫的行為(quack)。通過第二個(gè)設(shè)計(jì)原則我們知道需要利用接口代表每個(gè)行為,比如FlyBehavior與QuackBehavior,而行為的每個(gè)實(shí)現(xiàn)都將實(shí)現(xiàn)其中的一個(gè)接口。這樣鴨子類就不會(huì)負(fù)責(zé)實(shí)現(xiàn)FlyBehavior與QuackBehavior,而是由行為類來專門實(shí)現(xiàn),不會(huì)綁死在鴨子的子類中。
而"針對(duì)接口編程"的意思是"針對(duì)超類型編程"??梢悦鞔_地說明變量的聲明類型應(yīng)該是超類,這意味著我們?cè)贒uck父類中聲明的行為變量為?FlyBehavior,QuackBehavior,"針對(duì)接口編程"的關(guān)鍵就在于面向?qū)ο笕刂坏?quot;多態(tài)",由于多態(tài)我們才能在調(diào)用超類的方法時(shí)執(zhí)行的是實(shí)現(xiàn)類或子類的方法。所以我們改造之前寫的Duck類,刪除Fly()和Quack(),加入FlyBehavior和QuackBehavior變量,并用另外兩個(gè)方法PerFormFly()和PerFormQuack()來執(zhí)行兩個(gè)行為。
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(); } }
編寫相關(guān)類并測(cè)試:
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("不會(huì)叫");
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 }
如上測(cè)試我們?cè)邙喿幼宇愔型ㄟ^構(gòu)造方法實(shí)例化行為類,但建立了一堆動(dòng)態(tài)的功能沒有用到,是否可以動(dòng)態(tài)的設(shè)定行為而不是在構(gòu)造函數(shù)里面實(shí)例化。所以我們還可以加入兩個(gè)設(shè)置行為的方法
SetFlyBehavior和SetQuackBehavior,再次測(cè)試動(dòng)態(tài)設(shè)定行為。
/// <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(); } }
4、策略模式 類圖總結(jié)
策略模式:定義了算法簇,分別封裝起來,讓它們之間可以互相替換,此模式讓算法的變化獨(dú)立于使用算法的客戶
?通過這張類圖可以便于理解和記憶設(shè)計(jì)模式,后續(xù)會(huì)把其他模式也分享出自己的學(xué)習(xí)和理解。
熱門工具 換一換