設計模式—策略模式(用lol來分析)
轉載註明出處:
首先關心的是什麼時候要用到策略模式!
我們來關心下面的例子。
英雄聯盟的英雄有四個基礎技能和兩個召喚師技能。
每個英雄的基礎技能都不相同(對應的是Q、W、E、R),
但是召喚師技能卻是在多個裡面選擇的,可以相同(對應D、F)。
我們先來考慮一種情況。
我們將超類設計成
我們會發現有以下的缺點:
1、程式碼在多個子類中重複(實現召喚師技能的D和F的程式碼重複)
2、執行時行為不容易改變(我們在選擇召喚師技能DF時需要重新的覆蓋D、F)
3、很難知道召喚師技能的全部行為(假如版本改變,召喚師技能DF也改變)
4、改變會牽一髮動全身,如果某個召喚師技能覆蓋錯誤會導致bug。
於是我們定義一個介面,至於有什麼好處可以看我的EffectiveJava學習筆記系列。
以下的就是定義了行為類。
定義一個召喚師技能的行為。裡面有傳送、閃現、治療等等。
我們來看一下完整的類uml圖
我們建立了EZ和VN兩個英雄來繼承超類。
在裡面分別的實現自己的qwer技能。
並選擇召喚師技能的行為作為自己的 d、f 技能。
下面是程式碼
Hero.java
package org.ligz.DesignPatterns.Strategy.hero; public abstract class Hero { DFBehavior dBehavior;//使用D技能 DFBehavior fBehavior;//使用F技能 public Hero() { System.out.println("建立英雄"); } public void DSkill() { dBehavior.skill(); } public void FSkill() { fBehavior.skill(); } public void fight() { System.out.println("打架"); } /** * 基礎技能 */ public abstract void Q(); public abstract void W(); public abstract void E(); public abstract void R(); }
DFBehavior.java
package org.ligz.DesignPatterns.Strategy.hero;
public interface DFBehavior {
public void skill();
}
Falsh.java
package org.ligz.DesignPatterns.Strategy.hero;
public class Falsh implements DFBehavior{
public void skill() {
System.out.println("使用閃現");
}
}
Cure.java
package org.ligz.DesignPatterns.Strategy.hero;
public class Cure implements DFBehavior{
public void skill() {
System.out.println("使用治療");
}
}
TP.java
package org.ligz.DesignPatterns.Strategy.hero;
public class TP implements DFBehavior{
public void skill() {
System.out.println("使用傳送");
}
}
EZ.java
package org.ligz.DesignPatterns.Strategy.hero;
public class EZ extends Hero{
public EZ() {
super();
System.out.println("是時候展示真正的技術了");
dBehavior = new Falsh();
fBehavior = new TP();
}
@Override
public void Q() {
System.out.println("EZ的q技能");
}
@Override
public void W() {
System.out.println("EZ的w技能");
}
@Override
public void E() {
System.out.println("EZ的e技能");
}
@Override
public void R() {
System.out.println("EZ的r技能");
}
}
VN.java
package org.ligz.DesignPatterns.Strategy.hero;
public class VN extends Hero{
public VN() {
super();
System.out.println("讓我們來獵殺那些陷入黑暗中的敵人");
dBehavior = new Falsh();
fBehavior = new Cure();
}
@Override
public void Q() {
System.out.println("VN的q技能");
}
@Override
public void W() {
System.out.println("VN的w技能");
}
@Override
public void E() {
System.out.println("VN的e技能");
}
@Override
public void R() {
System.out.println("VN的r技能");
}
}
下面是測試的程式碼
package org.ligz.DesignPatterns.Strategy.hero;
public class Test {
public static void main(String[] args) {
Hero ez = new EZ();
ez.fight();
ez.DSkill();
ez.FSkill();
System.out.println("+++++++++++++++");
Hero vn = new VN();
vn.fight();
vn.DSkill();
vn.FSkill();
}
}
建立英雄
是時候展示真正的技術了
打架
使用閃現
使用傳送
+++++++++++++++
建立英雄
讓我們來獵殺那些陷入黑暗中的敵人
打架
使用閃現
使用治療
我們脫離例子來分析程式碼,會發現 “有一個” 可能比 ”是一個” 更好。
英雄Hero類有一個行為是DF召喚師技能。
每一個英雄都有一個DFBehavior行為,好處是將召喚師技能委託給他代為處理,這裡可能是有多個行為。比如說符文也可以是一個Behavior行為。我們同樣用這種方式擴充套件。
這就是組合。
這種做法和繼承不同的地方在於,英雄的行為不是繼承來的,而是適當的行為物件組合來的。
這樣做有很大的彈性。
回到定義:
策略模式定義了演算法族(召喚師技能,或者說是符文),分別封裝起來,讓他們之間可以相互替換,此模式可以讓演算法的變化獨立於使用演算法的客戶(英雄)。