我發現有不少人,都沒聽過設計模式,設計模式很重要,甚至面試的時候也會問你會不會設計模式。可見設計模式是每一個程序員必須具有的技能。那麼什麼是設計模式呢?設計模式是爲了解決項目中各類需求和問題的一種代碼編寫方式,面嚮對象語言均可以使用設計模式來編寫本身的代碼,面向過程語言如:C語言就不能使用設計模式,由於設計模式是針對面向對象的開發方式。想了解多的設計模式,點我。程序員
比較常見的設計模式:面試
也許還有不少比較經常使用的,但以上這些確定是使用頻率較高的。算法
以前說過,設計模式是由於需求而被髮明出來的,因此在說策略模式以前,我先來一個須要(例子來自**《Head First 設計模式》**)。設計模式
首先有一套至關成功的模擬鴨子游戲。遊戲中會出現各類鴨子,一邊游泳,一邊呱呱叫。此係統的內部使用了標準的OO技術(面向對象),並讓各類鴨子繼承超類。ide
代碼不是很難,都是最基礎的函數
代碼是這樣的:this
鴨子類(Duck):.net
/** * 鴨子類 */ abstract class Duck { public void quack(){ System.out.println("呱呱叫"); }; public void swim(){ System.out.println("游泳。。"); }; public abstract void display(); }
全部的鴨子都會呱呱叫(quack)也會游泳(Swim),全部超類負責處理這部分的實現代碼。由於每一種鴨子的外觀都不一樣,因此display()
方法是抽象的。設計
而後咱們建立兩個子類繼承**鴨子(Duck)**類吧代理
綠頭鴨(MallardDuck)
/** * 綠頭鴨 */ public class MallardDuck extends Duck{ [@Override](https://my.oschina.net/u/1162528) public void display() { System.out.print("我是綠頭鴨,不許笑!"); } }
紅頭鴨(RedheadDuck)
public class RedHeadDuck extends Duck { [@Override](https://my.oschina.net/u/1162528) public void display() { System.out.println("我是紅頭鴨!"); } }
需求:讓鴨子會飛!
實現需求
使用OO設計的話,很容易知足這個需求,只須要把超類添加一個方法,fly()
就可使用了。
代碼
/** * 鴨子類 */ abstract class Duck { public void quack(){ System.out.println("呱呱叫"); }; public void swim(){ System.out.println("游泳。。"); }; public abstract void display(); public void fly(){ System.out.println("飛行"); } }
其餘的代碼根本不須要作改變,當你剛剛嚐到面向對象的甜頭,而現實就會堅決果斷的打你一巴掌。過不了幾天,你的經理會給你打電話,告訴你被辭職了。
爲何?
橡皮鴨也是屬於鴨子,也會繼承鴨子類(Duck)
,可是你在超類中(鴨子類
)加了飛行方法fly()
,使得橡皮鴨也會飛了,而且橡皮鴨的叫聲也不是呱呱叫,這些問題,8害的經理在展現的時候很丟臉,可是你苦苦哀求,經理答應在給你一次機會,這時候你會怎麼作?
繼承
既然是面向對象作的系統,那麼我使用覆蓋,把橡皮鴨中的fly()
方法覆蓋,裏面什麼都不作,就像這樣:
/** * 橡皮橡鴨 */ public class MallardDuck extends Duck{ public void fly(){ //什麼都不作 } public void quack(){ System.out.println("吱吱叫"); } [@Override](https://my.oschina.net/u/1162528) public void display() { System.out.print("我是橡皮鴨"); } }
恩,這樣簡直完美,準備打電話告訴這個問題如已經解決的時候,又是一個問題出來了,若是之後加入誘餌鴨,不會叫也不會飛,難道也覆蓋嗎?之後維護怎麼辦?代碼是否出現大量沉餘。而且很難知道全部鴨子的所有行爲。
接口
既然繼承不能解決問題,那麼若是用接口呢?
咱們把不同的代碼,單獨的抽取出來,如
quack()
方法,fly()
方法,這兩種方法單獨的抽取出來,並將它們整合成兩接口,Flyable
和Quackable
接口。而後讓須要叫和須要飛的鴨子實現該接口這樣是否就能解決代碼沉餘了呢?
代碼
飛行接口:
public interface Flyable { public void fly(); }
叫接口:
public interface Quackable { public void quack(); }
而後在超類(鴨子類)中的代碼把飛行和發聲的代碼刪掉,子類須要飛行的就實現飛行接口,須要發生的就實現叫接口,二者都須要的就多實現,不都須要實現的則都不用實現。這裏就把子類和超類的代碼發一下。
鴨子類(Duck):
/** * 鴨子類 */ abstract class Duck { public abstract void display(); }
綠頭鴨(MallardDuck)
/** * 綠頭鴨 */ public class MallardDuck extends Duck implements Flyable,Quackable{ [@Override](https://my.oschina.net/u/1162528) public void quack() { System.out.println("呱呱叫。。"); } [@Override](https://my.oschina.net/u/1162528) public void fly() { System.out.println("綠鴨子飛了。"); } @Override public void display() { System.out.print("我是綠頭鴨,不許笑!"); } }
問題:
這個辦法雖然能解決,不能鴨子知足不一樣的需求,也只到全部鴨子的行爲,可是這並無解決代碼沉餘問題
若是換個想法,雖然是鴨子游戲,可是之後可能會更新,變的有青蛙或者其餘動物,那麼是否是也會有叫這個行爲
或者有飛這個行爲
?又或者有更多的行爲呢?
帶着這個問題,咱們使用策略模式
就能夠解決。
首先咱們把須要的行爲飛、叫、跑
單獨抽取出來,以便之後可以共享使用,避免代碼沉餘。這些行爲咱們在一次次改進後已經單獨的抽取出一個接口了。(若是你不知道已經改進成接口,請你往上面仔細看代碼),可是實現這些接口的不在是鴨子,而是特有的行爲,不懂意思?那就看代碼吧。
在該例子中,行爲有:叫、飛;而每隻鴨子叫的方式,和飛的方式不同,全部咱們把它們不同的地方單獨封裝成一個類。
呱呱叫類
public class Quack implements Quackable{ @Override public void quack() { System.out.println("呱呱叫。。"); } }
橡皮鴨是「吱吱叫」的,因此咱們在建立一個吱吱叫的類
吱吱叫類
public class Squeak implements Quackable { @Override public void quack() { System.out.println("吱吱叫。"); } }
木頭鴨,不會叫也不會飛,這裏就不用實現,也無法實現,就算實現了也沒有意義。好吧,居然作教程那麼就要作全套是吧!
不會叫類
public class MuteQuack implements Quackable { @Override public void quack() { //什麼都不作,不會叫 System.out.println("不會叫啊"); } }
叫的行爲,目前已經所有實現了,如今開始實現飛的行爲
用翅膀飛類
public class FlyWithWings implements Flyable { @Override public void fly() { System.out.println("翅膀扇動飛了。"); } }
不會飛類
public class FlyNoWay implements Flyable { @Override public void fly() { System.out.printf("不會飛啊。。"); } }
恩,全部的行爲已經所有實現了,如今該思考怎麼和子類組合在一塊兒。
既然全部的鴨子都繼承一個超類(鴨子類
Duck
),那麼咱們是否能夠在鴨子類上作手腳呢?
那就從超類作手腳吧,咱們知道全部的鴨子都有飛和叫的行爲,(不會叫的、不會飛的已經封裝成一個類了),那麼咱們就在超類中,添加飛和叫的方法。
鴨子類(Duck):
//請思考後在繼續往下面看 abstract class Duck { public void quack(){ 這裏怎麼寫? }; public void swim(){ System.out.println("游泳。。"); }; public abstract void display(); public void fly(){ 這裏怎麼寫? } }
若是看到這裏,就帶表明你已經思考過了,那麼每隻鴨子的飛、叫行爲都不同,可惟一能肯定的是這些行爲已經封裝了一個類,而且都實現了共同的接口,那麼咱們調用飛、叫方法還不手到勤來?
首先把飛、叫的接口當作鴨子類的屬性,而後在鴨子類中的fly()
和quack()
中調用接口中的方法就能夠了。
鴨子類(Duck):
abstract class Duck { //飛的接口 Flyable flyBehavior; //叫的接口 Quackable quackBehavior; public void quack(){ //調用 quackBehavior.quack(); }; public void performFly(){ //調用 flyBehavior.fly(); } public void swim(){ System.out.println("游泳。。"); }; public abstract void display(); }
咱們不須要飛和叫是怎麼實現的,咱們只須要知道這個接口有這個方法,全部咱們只管調用就行,具體怎麼實現,就交給子類啦。
紅頭鴨
/** * 紅頭鴨翅膀飛,發出呱呱叫的聲音 */ public class RedHeadDuck extends Duck { public RedHeadDuck() { flyBehavior=new FlyWithWings(); quackBehavior=new Quack(); } @Override public void display() { System.out.println("我是紅頭鴨!"); } }
橡皮鴨
/** * 橡皮鴨不會飛,發出吱吱叫的聲音 */ public class RubberDuck extends Duck{ public RubberDuck() { flyBehavior=new FlyNoWay(); quackBehavior=new Squeak(); } @Override public void display() { System.out.println("這是橡皮鴨"); } }
其餘鴨子只須要在構造函數中建立一個行爲就能夠了,本身動手寫綠頭鴨,和木頭鴨的代碼吧。
疑問
雖然解決了問題,可是若是要給木頭鴨一個飛的行爲呢?雖然木頭鴨不會飛,可是咱們能夠給它裝個火箭裝置讓它飛,或者開始不讓它飛,等客戶充錢購買了火箭裝置在讓它飛。怎麼作?
解答
不要忘了,既然咱們使用了設計模式那麼如今的代碼是頗有彈性的,咱們能夠給超類(鴨子類)一個Set
方法,這樣問題不就解決了嗎?並且全部的子類也無需修改代碼。
鴨子類(Duck):
abstract class Duck { Flyable flyBehavior; Quackable quackBehavior; public void setFlyBehavior(Flyable flyBehavior) { this.flyBehavior = flyBehavior; } public void setQuackBehavior(Quackable quackBehavior) { this.quackBehavior = quackBehavior; } public void quack(){ quackBehavior.quack(); }; public void performFly(){ flyBehavior.fly(); } public void swim(){ System.out.println("游泳。。"); }; public abstract void display(); }
看看是如何調用的吧:
//橡皮鴨 RubberDuck rubberDuck = new RubberDuck(); rubberDuck.performFly(); //用戶衝了200美圓,購買了火箭配置 //這裏沒有寫火箭配置的代碼,大家能夠試着寫,就和寫飛行類同樣 rubberDuck.setFlyBehavior(new FlyWithWings()); rubberDuck.performFly();
運行結果:
我不會飛。。 翅膀扇動飛了。
好了,策略模式終於寫完, 若是有錯誤 請私信給我,哦,對了,策略模式的正式定義在下面,若是能理解它,那麼你就學到了策略模式,若是沒有理解,那麼請認真重複閱讀幾遍吧,而後本身多動手敲代碼,仍是不理解?OMG,我也沒辦法了。
策略模式定義了算法簇,分別封裝起來,讓它們之間能夠相互替換,此模式讓算法的變化獨立於使用算法的客戶