「補課」進行時:設計模式(11)——遊戲中的策略模式
阿新 • • 發佈:2020-11-17
![](https://cdn.geekdigging.com/DesignPatterns/java_design_pattern.jpg)
## 1. 前文彙總
[「補課」進行時:設計模式系列](https://www.geekdigging.com/category/%e8%ae%be%e8%ae%a1%e6%a8%a1%e5%bc%8f/)
## 2. 遊戲中的策略模式
我是一個很喜歡玩遊戲的人,週末在家打打遊戲是真的很開心。
回想起來當年上大學的往昔崢嶸歲月,那時候基本上是一個人在玩遊戲,背後圍著好幾個人看,一個個的充當著狗頭軍師的作用。
![](https://cdn.geekdigging.com/DesignPatterns/11/weiguan.jpg)
時間長了就能發現,喜歡看別人打遊戲的人,往往自己玩的都不怎麼樣,但是當起狗頭軍師來那是一套一套的,難道這就是旁觀者清?
當年在大學宿舍玩的最多還是「英雄聯盟」,當年還是 AP 劍聖橫行天下,然而每次排位遇到的都是別人家的劍聖和我方劍聖。
這時候,一般就是狗頭軍師上線的時候,你出這個 xxx ,保證你如何如何牛皮,哎呀,你先打誰誰誰啊,為啥老要追著一個肉砍。
如果把上面這個場景轉化成寫程式,基本上是這樣的:
首先定義一個 LOL 的介面:
```java
public interface LOL {
void playMethod();
}
```
然後再來兩個狗頭軍師實現這個介面,每個狗頭軍師都有自己的玩法:
```java
public class DogStrategistA implements LOL{
@Override
public void playMethod() {
System.out.println("先出攻擊裝,剛正面,不慫");
}
}
public class DogStrategistB implements LOL {
@Override
public void playMethod() {
System.out.println("先出防禦裝,站得住才有輸出");
}
}
```
接著,我們開啟一局遊戲:
```java
public class LOLGame {
private LOL lol;
public LOLGame(LOL lol) {
this.lol = lol;
}
public void play() {
this.lol.playMethod();
}
}
```
然後下面是一個測試類:
```java
public class Test {
public static void main(String[] args) {
LOLGame game;
System.out.println("狗頭軍師A的點子--------------");
game = new LOLGame(new DogStrategistA());
game.play();
System.out.println("狗頭軍師B的點子--------------");
game = new LOLGame(new DogStrategistB());
game.play();
}
}
```
最後的執行結果如下:
```java
狗頭軍師A的點子--------------
先出攻擊裝,剛正面,不慫
狗頭軍師B的點子--------------
先出防禦裝,站得住才有輸出
```
是不是感覺上面這串程式碼好像和平時寫的沒啥區別,然而你並沒有猜錯,這就是策略模式。
## 3. 策略模式
### 3.1 定義
策略模式(Strategy Pattern)是一種比較簡單的模式,也叫做政策模式(PolicyPattern)。其定義如下:
Define a family of algorithms,encapsulate each one,and make theminterchangeable.(定義一組演算法,將每個演算法都封裝起來,並且使它們之間可以互換。)
![](https://cdn.geekdigging.com/DesignPatterns/11/strategy_uml.png)
- Context: 封裝角色,起承上啟下封裝作用,遮蔽高層模組對策略、演算法的直接訪問,封裝可能存在的變化。
- Strategy: 抽象策略角色,策略、演算法家族的抽象,通常為介面,定義每個策略或演算法必須具有的方法和屬性。
- ConcreteStrategy: 具體策略角色。
通用程式碼如下:
```java
public interface Strategy {
void doSomethinging();
}
public class ConcreteStrategy1 implements Strategy {
@Override
public void doSomethinging() {
System.out.println("具體策略1");
}
}
public class ConcreteStrategy2 implements Strategy {
@Override
public void doSomethinging() {
System.out.println("具體策略2");
}
}
public class Context {
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public void doAnything() {
this.strategy.doSomethinging();
}
}
public class Test {
public static void main(String[] args) {
Strategy strategy = new ConcreteStrategy1();
Context context = new Context(strategy);
context.doAnything();
}
}
```
### 3.2 優點
- 演算法可以自由切換:這是策略模式本身定義的,只要實現抽象策略,它就成為策略家族的一個成員,通過封裝角色對其進行封裝。
- 避免使用多重條件判斷:如果沒有策略模式,那麼我們只能選擇使用多重條件判斷語句,多重條件語句不易維護,而且出錯的概率大大增強。使用策略模式後,可以由其他模組決定採用何種策略,策略家族對外提供的訪問介面就是封裝類,簡化了操作,同時避免了條件語句判斷。
- 擴充套件性良好:這甚至都不用說是它的優點,在現有的系統中增加一個策略太容易了,只要實現介面就可以了。
### 3.3 缺點
- 策略類數量增多:每一個策略都是一個類,複用的可能性很小,類數量增多。
- 所有的策略類都需要對外暴露:上層模組必須知道有哪些策略,然後才能決定使用哪一個策略,這與迪米特法則是相違背的,我只是想使用了一個策略,我憑什麼就要了解這個策略呢?那要你的封裝類還有什麼意義?這是原裝策略模式的一個缺點,幸運的是,我們可以使用其他模式來修正這個缺陷,如工廠方法模式、代理模式或享元模式。
> 如果系統中的一個策略家族的具體策略數量超過 4 個,則需要考慮使用混合模式,解決策略類膨脹和對外暴露的