[設計模式]-策略模式

前言

這是我的第一篇讀書筆記.

今天拿起了《Head First 設計模式》,讀完了第一章"設計模式入門",這篇博客用來記錄對這一章的理解.

首先吹一波這本書,他確實成功的讓我沒有煩躁,安靜的讀並且思考了下來.這可能得益於裏面大量的插圖,以及時不時的提問,讓我比較有參與感.此外偶爾會有一些"幽默"的元素穿插在裏面,雖然不太好笑,但是總歸是一些趣味.

第一章主要是引導用戶一步一步設計一個簡單的系統,在系統的一步步優化過程中,使用了策略模式,來讓系統變得更好.

文中舉例是"鴨子應用",在這裏我會其中的"設計謎題"提到的冒險遊戲來展開,一步步記錄策略模式.

首先上一些理論性的東西.

定義

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

這話聽着又是很拗口,看完下面的例子就會明白了.

類圖

圖來自於維基百科

主要有三個類:

  1. 客戶,即使用"策略"的人.
  2. 抽象的策略類主要是定義策略.
  3. 具體的實現策略,可以有多種不同的策略實現.

具體場景

現在我們來設計一個系統,動作冒險遊戲.

要求有:

  1. 有多個角色
  2. 角色有武器.每一個角色只能使用一個武器攻擊,但是可以替換(就是經常玩遊戲時的切換武器啦).

好,簡單的想了一下,我們着手開始設計了.

第一版本

我們粗略的設計了一下,Character是個父類,有屬性-名字,和方法-攻擊.

騎士皇后繼承自Character,分別實現了attack().

看起來很美好,這個時候我們發現,不能只會攻擊啊!加上防守!

一想,這簡單,直接在Character類裏面加上一個defense()方法就好了,多簡單.

但是有個問題,我們的騎士是個愣頭青,不會防守,他相信進攻是最好的防守.

這個時候問題就來了,如果給Character裏面加上防守方法,會讓子類獲得不屬於他的能力,在每個子類裏面單獨加防守的方法,是不科學的.

那麼就想到了面向接口編程了,我們把攻擊和防守定義爲接口,你有啥能力就實現哪個接口唄.

試試看.

第二版本

在這個版本中,我們將攻擊防禦定義爲了兩個接口,可以自己選擇是否實現.

這樣設計已經不錯了,也是日常編碼中較爲常用的一種,因爲簡單方便.

但是仍然有一個問題,那就是在某些情況下重複代碼較多.

比如:騎士武士大將軍都用劍進行攻擊,攻擊一模一樣,這時候相同的代碼需要寫三份.

第三版本

圖中由於偷懶,AttackAble的子類只畫了一個

思想就是,攻擊有多個子類,用劍,用刀,用爪子.

然後各類角色分類去實現.

這樣子也有一個問題,沒有辦法實現中途換武器,也就是說,如果騎士實現了用劍,那麼他這輩子只能用劍了.不符合要求啊.

這個時候就要用到多態了.

即:聲明變量的時候,只聲明爲超類,這樣不用關心具體的實現,直接調用超類方法即可.

第四版本

在這個版本中,將攻擊能力Attackable抽象爲武器(Weapon),防守能力Defensable抽象爲Armor

在這個設計裏,每一個Character持有一個武器一個防具,在想要攻擊時,調用自己武器的攻擊方法,想要防守時,調用自己武器的防守方法.而不用關心持有的是什麼武器,以怎樣的方式去攻擊.

接下來用代碼來粗略的實現這一個設計.

實現代碼

Character類:

package com.huyan.demo.strategy;

/**
 * created by huyanshi on 2019/1/7
 */
public class Character {

  String name;

  private Weapon weapon;

  public Character() {
    weapon = new Sword();
  }

  public void attack() {
    weapon.attack();
  }

  public void setWeapon(Weapon weapon) {
    this.weapon = weapon;
  }

}

Weapon接口:

package com.huyan.demo.strategy;

/**
 * created by huyanshi on 2019/1/7
 */
public interface Weapon {

  void attack();
}

Sword類:

package com.huyan.demo.strategy;

/**
 * created by huyanshi on 2019/1/7
 */
public class Sword implements Weapon {


  @Override
  public void attack() {
    System.out.println("I'm using a sword!");
  }
}

Spear類:

package com.huyan.demo.strategy;

/**
 * created by huyanshi on 2019/1/7
 */
public class Spear implements Weapon {

  @Override
  public void attack() {
    System.out.println("I'm using a Spear!");
  }
}

最後是Knight類:

package com.huyan.demo.strategy;

/**
 * created by huyanshi on 2019/1/7
 */
public class Knight extends Character {

  public Knight (){
    this.name = "Knight";
  }

  @Override
  public void attack() {
    System.out.print("I'm a knight. -----");
    super.attack();
  }
}

測試代碼:

@Test
public void test(){

  Knight knight = new Knight();
  knight.attack();
  knight.setWeapon(new Spear());
  knight.attack();

}

輸出結果:

可以看到,在測試代碼中,我們首先new了一個騎士,然後調用他的攻擊方法,由於在Character中我們設置的默認武器爲Sword,因此打印了knight使用sword攻擊,之後我們調用setWeapon()方法,給騎士更換了武器,換爲了長毛Spear,再次調用attack(),打印了knight使用spear進行了攻擊.

策略模式的優缺點

優點

  • 提供了對“開閉原則”的完美支持,用戶可以在不修改原有系統的基礎上選擇算法或行爲,也可以靈活地增加新的算法或行爲。
  • 提供了管理相關的算法族的辦法。
  • 提供了可以替換繼承關係的辦法。
  • 可以避免使用多重條件轉移語句。

缺點

  • 客戶必須知道所有的策略類,並自行決定使用哪一個策略類。
  • 策略模式將造成產生很多策略類

適用環境

在以下情況下可以使用策略模式:

  • 如果在一個系統裏面有許多類,它們之間的區別僅在於它們的行爲,那麼使用策略模式可以動態地讓一個對象在許多行爲中選擇一種行爲。
  • 一個系統需要動態地在幾種算法中選擇一種。
  • 如果一個對象有很多的行爲,使用多重的條件選擇語句來實現。
  • 不希望客戶端知道複雜的、與算法相關的數據結構,在具體策略類中封裝算法和相關的數據結構,提高算法的保密性與安全性。

總結

一句話總結策略模式:

準備一組算法,並將每一個算法封裝起來,方便客戶端調用,替換,新增

一點小思考

最近工作上在做一些和推薦相關的事情,推薦嘛,不上線之前誰也不知道效果是好還是壞,我們是否可以提供多種的推薦算法,入參爲用戶屬性以及所有item,出參爲排序好的若干個item,然後使用策略模式,在線上使用時,動態的切換幾種推薦算法,並分別記錄與之對應的用戶留存以及平均停留時長等指標,用來判斷哪個算法更加靠譜一些?

完。





ChangeLog

2019-01-06 完成

以上皆爲個人所思所得,如有錯誤歡迎評論區指正。

歡迎轉載,煩請署名並保留原文鏈接。

聯繫郵箱:[email protected]

更多學習筆記見個人博客------>呼延十

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章