Java设计模式(学习整理)---策略模式
? ?1. 模式定義
??????? ?把會(huì)變化的內(nèi)容取出并封裝起來,以便以后可以輕易地改動(dòng)或擴(kuò)充部分,而不影響不需要變化的其他部分;
2.模式本質(zhì):
?少用繼承,多用組合,簡(jiǎn)單地說就是:固定不變的信息封裝在一個(gè)類中,變化的信息我們使用接口,抽象定義,那么使用的時(shí)候:繼承不變的,自定義實(shí)現(xiàn)變化的那 ? ? ? ? ? ? ?部分!
3.舉例分析:
示例:
一個(gè)鴨子類型: 抽象類型
? /*抽象的鴨子類*/1 public abstract class Duck { 2 //所有的鴨子均會(huì)叫以及游泳,所以父類中處理這部分代碼 3 public void quack() { 4 System.out.println("嘎嘎嘎....."); 5 } 6 7 public void swim() { 8 System.out.println("游啊游......."); 9 } 10 11 //因?yàn)槊糠N鴨子的外觀是不同的,所以父類中該方法是抽象的,由子類型自己完成。 12 public abstract void display(); 13 }
?
? 具體鴨子的繼承子類:
1 public class MallardDuck extends Duck { 2 //野鴨外觀顯示為綠頭 3 public void display() { 4 System.out.println("我是綠頭鴨子......"); 5 } 6 } 7 8 public class RedHeadDuck extends Duck { 9 //紅頭鴨顯示為紅頭 10 public void display() { 11 System.out.println("我是紅頭鴨子......"); 12 } 13 } 14 15 public class RubberDuck extends Duck { 16 //橡皮鴨叫聲為嘎嘎嘎叫,所以重寫父類以改寫行為 17 public void quack() { 18 System.out.println("嘎嘎嘎......."); 19 } 20 21 //橡皮鴨顯示為黃頭 22 public void display() { 23 System.out.println("我是黃頭鴨子......."); 24 } 25 }?
??
/***************************【分析說明】***************************************/ 上述代碼,初始實(shí)現(xiàn)得非常好。現(xiàn)在我們?nèi)绻oDuck.java中加入fly()方法的話,那么在子類型中均有了該方法,于是我們看到了 會(huì)飛的橡皮鴨子,你看過嗎?當(dāng)然,我們可以在子類中通過空實(shí)現(xiàn)重寫該方法以解決該方法對(duì)于子類型的影響。但是父類中再增加其它的方法呢?通過繼承在父類中提供行為,會(huì)導(dǎo)致以下缺點(diǎn):a. 代碼在多個(gè)子類中重復(fù);b. 運(yùn)行時(shí)的行為不容易改變;c. 改變會(huì)牽一發(fā)動(dòng)全身,造成部分子類型不想要的改變;/******************************************************************************/?
? 好啦,還是剛才鴨子的例子,你也許想到使用接口,將飛的行為、叫的行為定義為接口,然后讓Duck的各種子類型實(shí)現(xiàn)這些接口。
這時(shí)侯代碼類似于:
?
1 public abstract class Duck { 2 //將變化的行為 fly() 以及quake()從Duck類中分離出去定義形成接口,有需求的子類中自行去實(shí)現(xiàn) 3 4 public void swim() { 5 System.out.println("游啊游......."); 6 } 7 8 public abstract void display(); 9 } 10 11 //變化的 fly() 行為定義形成的接口 12 public interface FlyBehavior { 13 void fly(); 14 } 15 16 //變化的 quack() 行為定義形成的接口 17 public interface QuackBehavior { 18 void quack(); 19 } 20 21 //野鴨子會(huì)飛以及叫,所以實(shí)現(xiàn)接口FlyBehavior, QuackBehavior 22 public class MallardDuck extends Duck implements FlyBehavior, QuackBehavior{ 23 public void display() { 24 System.out.println("我是綠頭鴨......."); 25 } 26 27 public void fly() { 28 System.out.println("Fly."); 29 } 30 31 public void quack() { 32 System.out.println("嘎嘎嘎:綠鴨子叫........"); 33 } 34 } 35 36 //紅頭鴨子會(huì)飛以及叫,所以也實(shí)現(xiàn)接口FlyBehavior, QuackBehavior 37 public class RedHeadDuck extends Duck implements FlyBehavior, QuackBehavior{ 38 public void display() { 39 System.out.println("我是紅頭鴨......"); 40 } 41 42 public void fly() { 43 System.out.println("飛呀飛......."); 44 } 45 46 public void quack() { 47 System.out.println("嘎嘎嘎:紅鴨子叫"); 48 } 49 } 50 51 //橡皮鴨不會(huì)飛,但會(huì)嘎嘎嘎叫,所以只實(shí)現(xiàn)接口QuackBehavior 52 public class RubberDuck extends Duck implements QuackBehavior{ 53 //橡皮鴨叫聲為嘎嘎嘎叫 54 public void quack() { 55 System.out.println("嘎嘎嘎:橡皮鴨叫......"); 56 } 57 58 //橡皮鴨顯示為黃頭 59 public void display() { 60 System.out.println("我是黃頭鴨......"); 61 } 62 }?
?
?
?分析:
上述代碼雖然解決了一部分問題,讓子類型可以有選擇地提供一些行為(例如 fly() 方法將不會(huì)出現(xiàn)在橡皮鴨中).但我們也看到,野鴨子MallardDuck.java和紅頭鴨子RedHeadDuck.java的一些相同行為代碼不能得到重復(fù)使用。很大程度上這是從一個(gè)火坑跳到另一個(gè)火坑。在一段程序之后,讓我們從細(xì)節(jié)中跳出來,關(guān)注一些共性問題。不管使用什么語(yǔ)言,構(gòu)建什么應(yīng)用,在軟件開發(fā)上,一直伴隨著的不變的真理是:需要一直在變化。不管當(dāng)初軟件設(shè)計(jì)得多好,一段時(shí)間之后,總是需要成長(zhǎng)與改變,否則軟件就會(huì)死亡。我們知道,繼承在某種程度上可以實(shí)現(xiàn)代碼重用,但是父類(例如鴨子類Duck)的行為在子類型中是不斷變化的,讓所有子類型都有這些行為是不恰當(dāng)?shù)摹N覀兛梢詫⑦@些行為定義為接口,讓Duck的各種子類型去實(shí)現(xiàn),但接口不具有實(shí)現(xiàn)代碼,所以實(shí)現(xiàn)接口無法達(dá)到代碼復(fù)用。這意味著,當(dāng)我們需要修改某個(gè)行為,必須往下追蹤并在每一個(gè)定義此行為的類中修改它,一不小心,會(huì)造成新的錯(cuò)誤。?
?設(shè)計(jì)原則:?
設(shè)計(jì)原則:把應(yīng)用中變化的地方獨(dú)立出來,不要和那些不需要變化的代碼混在一起。這樣代碼變化引起的不經(jīng)意后果變少,系統(tǒng)變得更有彈性。按照上述設(shè)計(jì)原則,我們重新審視之前的Duck代碼。1) 分開變化的內(nèi)容和不變的內(nèi)容Duck類中的行為 fly(), quack(), 每個(gè)子類型可能有自己特有的表現(xiàn),這就是所謂的變化的內(nèi)容。Duck類中的行為 swim() 每個(gè)子類型的表現(xiàn)均相同,這就是所謂不變的內(nèi)容。我們將變化的內(nèi)容從Duck()類中剝離出來單獨(dú)定義形成接口以及一系列的實(shí)現(xiàn)類型。將變化的內(nèi)容定義形成接口可實(shí)現(xiàn)變化內(nèi)容和不變內(nèi)容的剝離。其實(shí)現(xiàn)類型可實(shí)現(xiàn)變化內(nèi)容的重用。這些實(shí)現(xiàn)類并非Duck.java的子類型,而是專門的一組實(shí)現(xiàn)類,稱之為"行為類"。由行為類而不是Duck.java的子類型來實(shí)現(xiàn)接口。這樣,才能保證變化的行為獨(dú)立于不變的內(nèi)容。
?
于是我們有:
1 //變化的 fly() 行為定義形成的接口 2 public interface FlyBehavior { 3 void fly(); 4 } 5 6 //變化的 fly() 行為的實(shí)現(xiàn)類之一 7 public class FlyWithWings implements FlyBehavior { 8 public void fly() { 9 System.out.println("飛呀飛......"); 10 } 11 } 12 13 //變化的 fly() 行為的實(shí)現(xiàn)類之二 14 public class FlyNoWay implements FlyBehavior { 15 public void fly() { 16 System.out.println("我翅膀受傷了,飛不了......"); 17 } 18 }?
1 //變化的 quack() 行為定義形成的接口 2 public interface QuackBehavior { 3 void quack(); 4 } 5 6 //變化的 quack() 行為實(shí)現(xiàn)類之一 7 public class Quack implements QuackBehavior { 8 public void quack() { 9 System.out.println("嘎嘎嘎......"); 10 } 11 } 12 13 //變化的 quack() 行為實(shí)現(xiàn)類之二 14 public class Squeak implements QuackBehavior { 15 public void quack() { 16 System.out.println("吱吱吱......."); 17 } 18 } 19 20 //變化的 quack() 行為實(shí)現(xiàn)類之三 21 public class MuteQuack implements QuackBehavior { 22 public void quack() { 23 System.out.println("啞巴,不會(huì)叫......."); 24 } 25 }?
通過以上設(shè)計(jì),fly()行為以及quack()行為已經(jīng)和Duck.java沒有什么關(guān)系,可以充分得到復(fù)用。而且我們很容易增加新的行為, 既不影響現(xiàn)有的行為,也不影響Duck.java。但是,大家可能有個(gè)疑問,就是在面向?qū)ο笾行袨椴皇求w現(xiàn)為方法嗎?為什么現(xiàn)在被定義形成類(例如Squeak.java)?在OO中,類代表的"東西"一般是既有狀態(tài)(實(shí)例變量)又有方法。只是在本例中碰巧"東西"是個(gè)行為。既使是行為,也有屬性及方法,例如飛行行為,也需要一些屬性記錄飛行的狀態(tài),如飛行高度、速度等。?
?整合變化的內(nèi)容和不變的內(nèi)容:
1 public abstract class Duck { 2 //將行為類聲明為接口類型,降低對(duì)行為實(shí)現(xiàn)類型的依賴 3 FlyBehavior flyBehavior; 4 QuackBehavior quackBehavior; 5 6 public void performFly() { 7 //不自行處理fly()行為,而是委拖給引用flyBehavior所指向的行為對(duì)象 8 flyBehavior.fly(); 9 } 10 11 public void performQuack() { 12 quackBehavior.quack(); 13 } 14 15 public void swim() { 16 System.out.println("游啊游......"); 17 } 18 19 public abstract void display(); 20 }?
?Duck.java不關(guān)心如何進(jìn)行 fly()以及quack(), 這些細(xì)節(jié)交由具體的行為類完成。
?
1 public class MallardDuck extends Duck{ 2 public MallardDuck() { 3 flyBehavior=new FlyWithWings(); 4 quackBehavior=new Quack(); 5 } 6 7 public void display() { 8 System.out.println("Green head."); 9 } 10 }?
?
?
???測(cè)試類:
?
1 public class DuckTest { 2 public static void main(String[] args) { 3 Duck duck=new MallardDuck(); 4 duck.performFly(); 5 duck.performQuack(); 6 } 7 }?
?
?
4.思想重點(diǎn):
(1)策略思想:繼承,可以實(shí)現(xiàn)靜態(tài)代碼的復(fù)用;
組合,可以實(shí)現(xiàn)代碼的彈性維護(hù);
使用組合代替繼承,可以使代碼更好地適應(yīng)軟件開發(fā)完后的需求變化。
? (2)策略模式的本質(zhì):
少用繼承,多用組合
?
轉(zhuǎn)載于:https://www.cnblogs.com/newwind/p/5653097.html
總結(jié)
以上是生活随笔為你收集整理的Java设计模式(学习整理)---策略模式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java NIO使用及原理分析(二)
- 下一篇: redis sentinel 主从切换(