Head First設計模式讀書筆記一 策略模式

Head First設計模式讀書筆記一 策略模式

本文示例程式碼材料源自Head First設計模式
以前整理自己整理的策略模式的連結:
https://blog.csdn.net/u011109881/article/details/60478840

策略模式思想

核心思想,分離變與不變。
例如原先我們設計了一個動物園系統,這個是個動物園是個不正經的動物園,只有鴨子,比如綠頭鴨,塑料玩具鴨,木頭鴨子等等。原先的系統已經設計好了,系統中有各種鴨子繼承了Duck,並有一個方法swim(假設所有鴨子都會游泳)
類圖如下:
這裡寫圖片描述
要知道,作為程式設計師,肯定知道需求的變動是很頻繁的,現在發生了需求變動。要給原來的鴨子新增飛行的方法。有Java基礎的會立馬想到在Duck類中新增一個Fly方法。但是,請聽我說完需求。現在所需要的飛行方法不是單一的,比如綠頭鴨和野鴨等“真正的”鴨子是用翅膀飛的;木頭鴨,塑料玩具鴨是不能飛的。馬達鴨(一種玩具鴨,可以裝電池,利用馬達飛行)可以利用馬達飛行。這樣一來,繼承的方式就不太好了。如果硬要使用繼承,那麼需要在每個子類鴨子重寫父類的fly方法。
那麼,使用介面呢?比如寫出介面基類FlyBehavior。
然後用FlyNoWay介面表示不會飛的鴨子,FlyNoWay繼承了FlyBehavior,不會飛的鴨子實現FlyNoWay介面;
用FlyWithWings介面表示會用翅膀飛的鴨子,FlyWithWings繼承了FlyBehavior,會用翅膀飛的鴨子實現FlyWithWings介面;
以此類推。這樣乍看很好啊,沒有什麼問題。但是很快我們會發現如果是木頭鴨和塑料鴨,它們都實現了FlyNoWay介面,並且都在自己的類中將該方法重寫一遍。因此,出現了重複程式碼。
那麼,還有其他什麼更好的方式來新增飛行方法嗎?有,就是委託。委託這個詞看起來很難懂,但是我們可以把委託者和被委託者想成has-a(有一個)的關係。比如上面這個例子,鴨子(被委託者)有一個飛行行為(委託者)
把飛行想成Duck的一個功能,Duck具有該功能,即Duck本身含有飛行行為的例項,可以呼叫飛行行為的飛行方法。
詳細設計方式可以參見類圖

示例思路(規劃類圖)

類結構:
這裡寫圖片描述
UML類圖:
這裡寫圖片描述

實際程式碼

  • Duck基類
public abstract class Duck {
String name;
FlyBehavior flyBehavior;
public void swim() {
System.out.println("通常,所有鴨子都會游泳");
}
public String getName() {
System.out.println("this is " name);
return name;
}
public void myFlyBehavior() {
if(null == flyBehavior){
System.out.println("你還沒有給該鴨子賦予飛行方式");
return;
}
flyBehavior.fly();
}
public void setFlyBehavior(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
}
}
  • 實體類MallardDuck
public class MallardDuck extends Duck{
public MallardDuck(String n){
this.name =n;
}
}
  • 實體類ToyDuck
public class ToyDuck extends Duck{
public ToyDuck(String n){
this.name =n;
}
}
  • 介面FlyBehavior
public interface FlyBehavior {
public void fly();
}
  • 飛行實現類FlyNoWay
public class FlyNoWay implements FlyBehavior {
public void fly() {
System.out.println("這是個假的飛行方法,我根本不會飛");
}
}
  • 飛行實現類FlyWithWings

public class FlyWithWings implements FlyBehavior{
public void fly() {
System.out.println("我用翅膀飛翔");
}
}
  • 測試類
public class DuckTest {
public static void main(String[] args) {
Duck toyDuck = new ToyDuck("Tim--ToyDuck");
toyDuck.setFlyBehavior(new FlyNoWay());
toyDuck.getName();
toyDuck.myFlyBehavior();
Duck mallardDuck = new MallardDuck("Jerry--mallardDuck");
mallardDuck.setFlyBehavior(new FlyWithWings());
mallardDuck.getName();
mallardDuck.myFlyBehavior();
}
}

測試結果:

this is Tim--ToyDuck
這是個假的飛行方法,我根本不會飛
this is Jerry--mallardDuck
我用翅膀飛翔

總結,再看例項

思路:分離變與不變。回憶之前的需求,可以發現,不變的是swim,變的是fly。雖然swim和fly都是鴨子的行為,但是swim不怎麼變化,它可以利用繼承來實現程式碼複用,而飛行方式卻有很多種,無法繼承,因此需要將飛行行為抽出來,各自實現,讓Duck實現類擁有飛行行為例項,達到操作飛行行為的目的。這種思想就像零件的組裝,主體是不變的,零件有各種相同功能不同效能的各種款式,這樣可以做出不同的產品。
另外策略模式還有一個好處是可以動態變化行為,比如木頭鴨子原來是不會飛的,它用setFlyBehavior設定不會飛行,後來設計師給他裝上了引擎翅膀,它可以再次通過setFlyBehavior在執行時動態變更飛行行為。
說到這裡,回顧之前寫的策略模式(https://blog.csdn.net/u011109881/article/details/60478840
在之前的例子中,看樣子類圖和本文的差很多,其實不然。我們對比一下:
Strategy就是本例中的FlyBehavior
Context就是本例中的Duck
之前的文章其實沒有本文的類結構複雜,因為前文“只有一種鴨子”,因此也就省去“鴨子的各種實現類了”,所以看上去是比較簡單的,但本質一樣。另外,個人覺得之前的文章例子雖然可以解釋策略模式,但是實際開發完全可以使用switch case來實現,因為那個例子沒有明顯的變與不變的地方。

其他策略模式的例子:

出行方式:比如一個角色character,他出門可以有不同的交通方式,如果目的地近,可以選擇步行,如果距離遠,可以選擇開車,如果路上塞車,可以選擇公交地鐵。那麼出行方式的變化可以抽出來形成一種策略。拿本文中的例子類比的話,角色相當於鴨子,各種交通方式相當於飛行行為。
遊戲角色和武器裝備:遊戲中角色有不同的種類,戰士法師弓箭手盜賊等等,角色可以穿著不同的武器裝備,甚至還有角色限定,繼續類比,則角色相當於不同種類的鴨子,武器套裝則相當於不同的飛行方式,遊戲角色可以隨時更換武器裝備。