策略模式就是定義了一系列的的算法,將它們都單獨封裝起來,讓他們之間能夠相互替換,可讓算法的變化獨立於使用算法的客戶。
首先建立一個Dog父類,有run方法控制跑,jump方法控制跳,color方法控制顏色。算法
public class Dog { public void run(){ System.out.println("狗在跑"); } public void jump(){ System.out.println("狗在跳"); } public void color(){ } }
建立兩個子類,分別是WhiteDog和BlackDog,都重寫了父類的color方法,運行一下。編程
public class WhiteDog extends Dog { @Override public void color() { System.out.println("白顏色的狗"); } } public class BlackDog extends Dog { @Override public void color() { System.out.println("黑顏色的狗"); } } public class Test { public static void main(String[] args){ WhiteDog whiteDog = new WhiteDog(); whiteDog.run(); whiteDog.jump(); whiteDog.color(); BlackDog blackDog = new BlackDog(); blackDog.run(); blackDog.jump(); blackDog.color(); } }
結果:
狗在跑
狗在跳
白顏色的狗
狗在跑
狗在跳
黑顏色的狗設計模式
這個時候看上去好像很完美沒有任何問題,可是有一天咱們又新建了一個公仔狗的對象Doll dog,這個時候咱們發現好像有點不對,公仔狗是不會跳也不會跑的。能夠若是繼承了Dog父類他就自帶跑和跳的功能了,那就不對了。ide
public class DollDog extends Dog { @Override public void color() { System.out.println("五光十色"); } } DollDog dollDog = new DollDog(); dollDog.run(); dollDog.jump(); dollDog.color();
結果:
狗在跑
狗在跳
五光十色的玩具狗this
這個時候咱們靈機一動想到可讓子類覆蓋父類run方法和jump方法,讓DollDog中的跑和跳功能失效,咱們再運行一下。設計
@Override public void run() { System.out.println("玩具狗不會跑"); } @Override public void jump() { System.out.println("玩具狗不會跳"); }
結果:
玩具狗不會跑
玩具狗不會跳
五光十色的玩具狗code
看起來是解決了問題,可是若是一天要增長100個各類各樣的狗的話難道咱們要讓100個新建的子類都重寫父類的方法嗎?這個時候父類裏面還只有3個方法,若是是30個呢?若是咱們都靠子類重寫的話那效率該多低呢?有沒有別的方法來解決呢?答案是確定的。下面咱們就來介紹怎麼用策略模式來解決這個問題。
首先咱們要知道設計模式中第一個原則,要把代碼中常常須要修改的部分獨立抽取出來,不要和其餘代碼混在一塊兒,這樣更便於咱們擴展要修改的部分 。目前來看最常變化的是run和jump方法。因此咱們能夠將這兩個方法抽取出來,這裏就要說到設計模式中第二個原則,針對接口編程,而不是現實編程。好比run和jump有不一樣的種類,咱們能夠聲明一個接口裏面定義run和jump方法,而後建立許多類去實現,調用的時候動態選擇類型。這種類被稱爲行爲類。行爲類中的代碼能夠進行復用,卻不會有繼承帶來的那些麻煩。對象
定義方法的接口:繼承
public interface RunBehavior { void run(); } public interface JumpBehavior { void jump(); }
實現方法的行爲類:接口
public class RunNoWay implements RunBehavior { @Override public void run() { System.out.println("不會跑"); } } public class RunFast implements RunBehavior { @Override public void run() { System.out.println("很快的跑"); } } public class RunSlow implements RunBehavior { @Override public void run() { System.out.println("很慢的跑"); } } public class JumpNoWay implements JumpBehavior { @Override public void jump() { System.out.println("不會跳"); } } public class JumpFast implements JumpBehavior { @Override public void jump() { System.out.println("很快的跳"); } }
如今咱們將變化的部分抽取出來了,因此Dog父類就會把run和jump的操做委託給行爲類處理,那麼具體要怎麼使用這些行爲類?這裏就須要在Dog父類中定義兩個實例變量,聲明類型爲RunBehavior和JumpBehavior,因此在代碼運行的時候會用多態的方式引用正確的類型。而後在父類中的run和jump方法中委託行爲類去執行功能。
public class Dog { public RunBehavior runBehavior; public JumpBehavior jumpBehavior; public void run(){ runBehavior.run(); } public void jump(){ jumpBehavior.jump(); } public void color(){ } }
最後當咱們沒出現一個新的類型的狗狗的時候,咱們爲它建立一個新類而後繼承Dog父類,而後咱們在子類的構造方法中獲取父類中兩個接口的引用,根據本身的須要經過多態指定不一樣的行爲類。
public class SuperDog extends Dog { public SuperDog(){ runBehavior = new RunFast(); jumpBehavior = new JumpFast(); } @Override public void color() { System.out.println("紅藍相間的超人狗"); } } Dog dog = new SuperDog(); dog.jump(); dog.run(); dog.color();
結果:
很快的跳
很快的跑
紅藍相間的超人狗
最後咱們還有一個小問題,每次指定選擇類的時候都是在子類的構造方法中指定,可不能夠動態的指定呢?固然能夠,咱們只須要爲聲明的接口引用添加兩個set方法。而後在外部調用便可。
public void setRunBehavior(RunBehavior runBehavior) { this.runBehavior = runBehavior; } public void setJumpBehavior(JumpBehavior jumpBehavior) { this.jumpBehavior = jumpBehavior; } Dog dog = new SuperDog(); dog.jump(); dog.run(); dog.color(); dog.setJumpBehavior(new JumpNoWay()); dog.setRunBehavior(new RunNoWay()); dog.jump(); dog.run(); dog.color();
結果:
很快的跳
很快的跑
紅藍相間的超人狗
不會跳
不會跑
紅藍相間的超人狗
總結:策略模式就是把全部的可變的行爲都抽取出來放到接口中,而後定義不少的行爲類去實現接口。在父類中聲明瞭接口的引用利用多態去動態的選擇本身須要的行爲類,避免了之前由於單純的繼承形成的每次的新變更都須要寫大量的重複代碼,而如今只須要定義好行爲類進行復用便可,不須要修改本來的代碼。