1. 程式人生 > >1.Strategy Pattern(策略模式)

1.Strategy Pattern(策略模式)

color span adb behavior 具體類 hid pad mage form

策略模式(Strategy Pattern):

  我的理解,將代碼中每個變化之處抽出,提煉成一個一個的接口或者抽象類,讓這些變化實現接口或繼承抽象類成為具體的變化類。再利用多態的功能,可將變化之處用接口或抽象類的對象代替,再用其子類為對象賦值,這樣就可以將對象隨時更換成具體的變化類。

  枯燥的文字描述總是沒有實際的例子淺顯易懂。

舉例:(我是基於notepad++和cmd命令編譯運行的JAVA代碼

  現在有個鴨子俱樂部,裏面有各式各樣的鴨子(只有想不到,沒有做不到的鴨子)。

  我們來一步一步實現這個鴨子類:

  1.定義一個抽象鴨子類(Duck):

技術分享圖片
 1 public abstract
class Duck{//抽象鴨子類 2 public void fly(){//行為:飛行 3 System.out.println("I‘m flying!"); 4 } 5 public void quack(){//行為:呱呱叫 6 System.out.println("Gua Gua!"); 7 } 8 public void swim(){//行為:遊泳 9 System.out.println("I‘m swimming!"); 10 } 11 }
Duck

  2.實現一個正常的鴨子類(GreenDuck):

技術分享圖片
1 public class GreenDuck extends Duck{//GreenDuck直接繼承Duck,什麽都不做
2     
3     public GreenDuck(){
4     }
5 }
GreenDuck

  3.測試類(DuckTest):

技術分享圖片
1 public class DuckTest{//測試類
2     public static void main(String args[]){
3         GreenDuck greenDuck = new GreenDuck();//實例化一只GreenDuck
4         greenDuck.fly();
5 greenDuck.quack(); 6 greenDuck.swim(); 7 } 8 }
DuckTest

  編譯運行+結果:

技術分享圖片

  大功告成!我們去慶祝一下。

  正在慶祝,鴨子俱樂部來電話說,我們不僅只要一只GreenDuck,還要RedDuck...不管什麽顏色的鴨子都要。你一口應承下來,沒問題只是多寫幾個繼承的類而已。

  鴨子俱樂部繼續說道,我們還要不會飛,不會叫的橡皮鴨,所有顏色的橡皮鴨來一套。你很心虛的答應了,可能今晚要加班加點了,讓各種顏色的橡皮鴨繼承Duck但是要覆蓋其中的fly和quack行為。

  鴨子俱樂部沒完沒了的繼續說道,我們還要各種顏色的鴨鳴器,它們只會叫不會飛。現在你心裏一定恨死各種顏色這個單詞了。

  鴨子俱樂部嘴停不下來說各種顏色的...

  .......

  第二天,哭暈在廁所中。

  現在,來看看到底是什麽問題導致我們要不停的重復寫大量的代碼:各種顏色

  沒錯就是這個單詞讓我們不停地去寫各種各樣的鴨子實現類(都繼承自Duck抽象類),並且有的鴨子不會飛,有的會飛不會叫...

  現在有沒有感覺到繼承帶來的恐懼感?我們可以讓所有的同類鴨子都繼承自Duck抽象類,但是每種鴨子都有自己獨特的行為,導致我們要不停地去覆蓋Duck抽象類中的行為。

  問題找到了。就是繼承自抽象類的行為不符合每種鴨子獨特的行為導致我們不停地去手動改寫或添加行為。我們寫這麽多的重復代碼,沒有將代碼復用,比如,有的鴨子會飛,有的鴨子會叫,有的鴨子會遊泳,有的鴨子不會叫...這麽多的行為都寫在鴨子實現類中,導致代碼冗余,沒有將它們復用。

下面讓我們的救星:策略模式(Strategy Pattern)登場:

  1.首先,fly()和quack()兩個方法是一直在變化的,所以我們將這兩個變化之處從Duck抽象類中提煉出來變成FlyBehavior接口和QuackBehavior接口,並在Duck抽象類中定義flyBehavior和quackBehavior兩個對象。

技術分享圖片
 1 public abstract class Duck{//抽象鴨子類
 2 
 3     /*增加兩個接口對象*/
 4     FlyBehavior flyBehavior;//飛行類對象
 5     QuackBehavior quackBehavior;//呱呱叫類對象
 6     
 7     public Duck(){
 8     }
 9     
10     //去除下面兩個方法
11     /*public void fly(){//行為:飛行
12         System.out.println("I‘m flying!");
13     }
14     public void quack(){//行為:呱呱叫
15         System.out.println("Gua Gua!");
16     }*/
17     
18     /*增加下面兩個方法,這就是將Duck類的行為委托給兩個接口對象實現*/
19     public void performFly(){//將fly()委托給flyBehavior對象實現
20         flyBehavior.fly();
21     }
22     public void performQuack(){//將quack()委托給quackBehavior對象實現
23         quackBehavior.quack();
24     }
25     
26     
27     public void swim(){//行為:遊泳
28         System.out.println("I‘m swimming!");
29     }
30 }
Duck 技術分享圖片
1 public interface FlyBehavior{//從Duck抽象類中抽出的fly()方法變成了FlyBehavior接口
2     public void fly();
3 }
FlyBehavior 技術分享圖片
1 public interface QuackBehavior{//從Duck抽象類中抽出的quack()方法變成了QuackBehavior接口
2     public void quack();
3 }
QuackBehavior

  其次,將變化具體類分別繼承FlyBehavior和QuackBehavior兩個接口:

  兩個飛行具體變化類:

技術分享圖片
1 public class FlyWithWings implements FlyBehavior{
2     public void fly(){
3         System.out.println("I‘m flying!");
4     }
5 }
FlyWithWings 技術分享圖片
1 public class FlyNoWay implements FlyBehavior{
2     public void fly(){
3         System.out.println("I can‘t fly!");
4     }
5 }
FlyNoWay

  兩個呱呱叫具體變化類:

技術分享圖片
1 public class Quack implements QuackBehavior{
2     public void quack(){
3         System.out.println("Quack quack!");
4     }
5 }
Quack 技術分享圖片
1 public class MuteQuack implements QuackBehavior{
2     public void quack(){
3         System.out.println("<< Silence >>");
4     }
5 }
MuteQuack

  最後,實現一個具體類和測試類:

技術分享圖片
 1 public class GreenDuck extends Duck{//GreenDuck直接繼承Duck
 2     
 3     public GreenDuck(){
 4         flyBehavior = new FlyWithWings();
 5         quackBehavior = new Quack();
 6     }
 7     
 8     /*增加一個展示自己是什麽鴨子的方法*/
 9     public void display(){
10         System.out.println("I‘m GreenDuck!");
11     }
12 }
GreenDuck 技術分享圖片
1 public class DuckTest{//測試類
2     public static void main(String args[]){
3         GreenDuck greenDuck = new GreenDuck();//實例化一只GreenDuck
4         greenDuck.performFly();
5         greenDuck.performQuack();
6         greenDuck.swim();
7         greenDuck.display();
8     }
9 }
DuckTest

編譯運行,結果:
技術分享圖片

上面的結果,我們可以隨時隨地的實現不同的具體的鴨子類了,只要在具體的鴨子類中為flyBehavior和quackBehavior實現不同的變化類就好。

  2.動態的實現具體變化類的改變:

  在Duck類中添加兩個新方法(setFlyBehavior(Flybehavior fb)和 setQuackBehavior(QuackBehavior qb) ):

技術分享圖片
 1 public abstract class Duck{//抽象鴨子類
 2 
 3     /*增加兩個接口對象*/
 4     FlyBehavior flyBehavior;//飛行類對象
 5     QuackBehavior quackBehavior;//呱呱叫類對象
 6     
 7     public Duck(){
 8     }
 9     
10     //去除下面兩個方法
11     /*public void fly(){//行為:飛行
12         System.out.println("I‘m flying!");
13     }
14     public void quack(){//行為:呱呱叫
15         System.out.println("Gua Gua!");
16     }*/
17     
18     /*增加下面兩個方法,這就是將Duck類的行為委托給兩個接口對象實現*/
19     public void performFly(){//將fly()委托給flyBehavior對象實現
20         flyBehavior.fly();
21     }
22     public void performQuack(){//將quack()委托給quackBehavior對象實現
23         quackBehavior.quack();
24     }
25     
26     /*添加兩個新方法,可以動態的改變具體變化類*/
27     public void setFlyBehavior(FlyBehavior fb){
28         flyBehavior = fb;
29     }
30     public void setQuackBehavior(QuackBehavior qb){
31         quackBehaior = qb;
32     }
33     
34     
35     public void swim(){//行為:遊泳
36         System.out.println("I‘m swimming!");
37     }
38 }
Duck

  改造測試類:

技術分享圖片
 1 public class DuckTest{//測試類
 2     public static void main(String args[]){
 3         GreenDuck greenDuck = new GreenDuck();//實例化一只GreenDuck
 4         greenDuck.performFly();//一開始GreenDuck會飛
 5         greenDuck.performQuack();//一開始GreenDuck會叫
 6         
 7         /*動態改變greenDuck的行為*/
 8         greenDuck.setFlyBehavior(new FlyNoWay());
 9         greenDuck.setQuackBehavior(new MuteQuack());
10         
11         greenDuck.performFly();//現在不會飛了
12         greenDuck.performQuack();//現在不會叫了
13     }
14 }
DuckTest

編譯運行,結果:

技術分享圖片

代碼下載網址:

https://github.com/lanshanxiao/Head-First-Design-Pattern/tree/master/1.%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F(StrategyPattern)%E8%AE%B2%E8%A7%A3%E4%BB%A3%E7%A0%81/%E7%AE%80%E5%8D%95%E5%AE%9E%E7%8E%B0

提煉一下思想:

1.封裝

2.“有一個” 比 “是一個” 好(has-a 比 is-a好)

3.多用組合少用繼承

4.封裝變化

5.正對接口編程,不針對實現編程

1.Strategy Pattern(策略模式)