程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA編程入門知識 >> 設計模式之策略模式

設計模式之策略模式

編輯:JAVA編程入門知識

《Head First 設計模式》,好書!

策略模式經典的例子:鴨子模型

假設我們主要研究鴨子(DUCK)的"叫(quack)"和鴨子的飛(fly)。

public abstract class Duck{
  //呱呱叫
  quack();
  //飛
   fly();
}

 

      1.假如,我們想要很多種鴨子在屏幕上飛和叫,想到了設計一個DUCK父類,讓許許多多的鴨子子類繼承fly()和quack()。這樣鴨子們就可以自由的在屏幕飛和叫了。

      2.問題來了,屏幕上有個RubberDuck(橡皮鴨)在飛和呱呱叫,這怎麼行!RubberDuck不會飛並且叫聲是"吱吱"而不是"呱呱"。於是靈機一動,把RubberDuck

的fly()和quack()重寫,覆蓋父類方法。屏幕看上去舒服多了~

     3.屏幕上怎麼又多出來了一個DecoyDuck(誘餌鴨,木頭制作),再重寫父類方法,讓它不會飛也不會叫。

     4.....不一會就感到厭煩了,老是重寫了父類方法怎麼行。

直接繼承有太多的缺點:

  • 代碼在多個子類中重復(一直重寫父類方法有木有)
  • 運行時行為不容易改變(我的DecoyDuck現在會飛了,能不能在運行的時候改變它)
  • 很難知道所有鴨子的全部行為(總不會把所有子類數一遍吧)
  • 牽一發而動全身

於是,我們要改變設計方法。

這些行為(fly和quack)繼承起來太難控制,用接口把。嗯~把它們從DUCK父類中提取出來,分別定義一個接口。

設計原則:找出應用中可能需要變化之處,把它們獨立出來,不要和那些不需要變化的代碼混合在一起。


鴨子發出的聲音不一樣,可以"呱呱Quack"、"吱吱Squack"、"無聲MuteQuack",分別實現fly的接口(FlyBehavior)

這樣,鴨子發出quack的時候就可以調用特定的方法。

因為分離出來,這些行為和鴨子完全無關。實現青蛙的時候也可以拿走"呱呱叫"的方法

設計原則:針對接口編程,不針對實現編程。

/*
  Dog繼承Animal
*/

//針對實現編程
Dog d = new Dog;
d.bark();

//針對接口編程,這裡就是多態。
Animal animal = new Dog();
animal.bark();

//用animal調用bark()方法,至於animal是什麼,我們並不關心。

 

那麼還有一個問題,每個鴨子都去實現特定的行為方法(如:Quack),這些行為是固定的。可是我想在運行時動態的改變鴨子的行為怎麼辦?

比如,DecoyDuck突然可以飛了。

貌似可以用多態,針對接口編程。"叫"定義了接口QuackBehavior,"飛"定義了接口FlyBehavior。

然後Quack(呱呱叫),SQuack(吱吱叫),MuteQuack(無聲)實現了QuackBehavior。

同理,FlyBehavior也有具體實現。

那麼,父類Duck:

public abstract class Duck{

//聲明QuackBehavior的接口
QuackBehavior quackBehavior;

//鴨子對象不直接執行叫的行為,委托給quackBehavior對象。
public void performQuack(){
   quackBehavior.quack();
}
//可以動態的修改行為,用quackBehavior來什麼行為要什麼行為。
//比如傳入MuteQuack,然後在調用performQuack,是不是就相當於 MuteQuack.quack(),不發聲了。
public void setQuackBehavior(QuackBehavior qb){
quackBehavior = qb;
} }

有一個performQuack()方法,這個方法代替被刪除的quack方法(直接繼承的quack方法,在最上面)。

作用就是去使用接口對象 quackBehavior調用quack方法。(或許畫上類圖更清楚)

//叫的接口
public interface QuackBehacior{
   public void quack();
}

------------------------------------具體實現--------------------------------------
public class Quack implements QuackBehavior{
   public void quack(){
   System.out.println("我會呱呱叫");
}
}
----------            ------------
public class Squack implements QuackBehavior{
   public void quack(){
   System.out.println("我會吱吱叫");
}
}
----------            ------------
public class MuteQuack implements QuackBehavior{
   public void quack(){
   System.out.println("我不會發聲");
}
}

 

像不像上面anmial和dog的調用,不需要知道具體是誰,委托給接口對象(可以這樣理解吧)。

 

 

那麼,開始了

寫一個鴨子類繼承Duck

public MallardDuck extends Duck{

public MallardDuck(){
//quackBehavior是繼承下來的
quackBehavior = new Quack();
}

}

開始測試:

public static void main(String [] args){

Duck mallard = new MallardDuck();
mallard.performQuack(); //輸出呱呱叫
mallard.setQuackBehavior(new MuteQuack());
mallard.performQuack();//輸出無聲

}

/*
可能你還有些迷糊,那逐一解釋代碼
1.Duck mallard = new MallardDuck()
執行:
        quackBehavior = new Quack()//就像實例化了quackBehavior
2.mallard.peiformQuack();
執行:      
        quackBehavior.quack();//該對象調用Quack類裡的quack()方法

3.mallard.setQuackBehavior(new MuteQuack());
         quackBehavior = qb; //qb是不是 new MuteQuack();

是不是所有的都是圍繞QuackBehavior 接口,針對接口編程。
*/

設計原則:多用組合(composition),少用繼承

  上文中的fly(實現方式和quack相同)、quack行為就是組合。鴨子的行為不是繼承來的,而是多個行為 組合 來的。

這些行為完全可以被青蛙(呱呱),麻雀(飛)使用,提升了代碼的復用程度。

 

把"行為"抽象一下,不再叫做行為而是算法族。然後把他們封裝起來(嗯,封裝),可以動態的相互替換(鴨子的行為動態替換)。並且這些算法和要使用這些算法的鴨子沒有任何關系。

 

總結:

 策略模式定義了算法族,分別封裝起來,讓他們之間可以相互替換,此模式讓算法的變化獨立於使用算法的客戶。

這樣看起來是不是很高大上~

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved