1. 程式人生 > >設計模式—策略模式(用lol來分析)

設計模式—策略模式(用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行為。我們同樣用這種方式擴充套件。

這就是組合。

這種做法和繼承不同的地方在於,英雄的行為不是繼承來的,而是適當的行為物件組合來的。

這樣做有很大的彈性。

回到定義:

策略模式定義了演算法族(召喚師技能,或者說是符文),分別封裝起來,讓他們之間可以相互替換,此模式可以讓演算法的變化獨立於使用演算法的客戶(英雄)。