設計模式-策略模式簡介

引言

前段時間再看 《阿里巴巴Java編程規範 》時,看到這麼一條推薦規範:

超過3層的if-else的邏輯判斷代碼可以使用衛語句、策略模式、狀態模式等來實現

好奇這策略模式(Strategy Pattern)是一種什麼樣的解決方案,我決定去看看。

定義:

策略模式定義了一系列的算法,並將每一個算法封裝起來,而且使他們可以相互替換,策略模式讓算法獨立於使用它的客戶而獨立變化。

結構

策略(Strategy)

定義所有支持的算法的公共接口。 Context 使用這個接口來調用某 ConcreteStrategy 定義的算法。

策略實現(ConcreteStrategy)

以 Strategy 接口實現某具體算法
上下文(Context)
用一個 ConcreteStrategy 對象來裝配
維護一個 Strategy 對象的引用
可定義一個接口讓 Strategy 訪問它的數據

案例

假如,公司要你設計一款遊戲中的角色,於是你這樣設計:
創建了一個 角色接口,

/**
* @author hobart
* 角色接口
*/
public interface IRole {
    // 所有角色 都可以 戰鬥
    public void fight(); 
}

建立了一個 武當的角色:

/**
 * @author hobart
 * 武當派L角色
 */
public class WudangRole implements IRole {
	public void fight() {
		System.out.println("我是武當派角色,我用劍戰鬥!");
	}
}

再建立了一個 少林的角色:

/**
 * @author hobart
 * 少林派角色
 */
public class ShaolinRole implements IRole {
	public void fight() {
		System.out.println("我是少林派角色,我用棍戰鬥!");
	}
}

還有野怪角色:

/**
 * @author hobart
 * 野怪角色
 */
public class WildMonsterRole implements IRole{
	public void fight() {
		System.out.println("我是野怪角色,我用爪子戰鬥!");
	}
}

完事,問題解決。
但是後來你們領導說,這些角色只會戰鬥,讓遊戲缺乏趣味性,想讓門派的角色可以學習和使用武功祕籍**(技能),這時候你發現你的野怪角色不能學習和使用武功祕籍(技能),要實現學習和使用武功祕籍**這個功能,你就要去WudangRole類和ShaolinRole類中各自添加 `` learningEsoterica()`, 則變成這樣了

WudangRole類

/**
 * @author hobart
 * 武當派角色
 */
public class WudangRole implements IRole {
	public void fight() {
		System.out.println("我是武當派角色,我用劍戰鬥!");
	}
	
	public void learningEsoterica() {
		System.out.println("武當派角色學習了太極劍法!");
	}
}

ShaolinRole類

/**
 * @author hobart
 * 少林派角色
 */
public class ShaolinRole implements IRole {
	public void fight() {
		System.out.println("我是少林派角色,我用棍戰鬥!");
	}
	
	public void learningEsoterica() {
		System.out.println("少林派角色學習了易筋經!");
	}
}

可是,現在你的 角色 已經 不止這個兩個了。還有許多其他門派的角色,也實現了IRole,他們也需要學習技能, 你的工作量就大大增加了。
解決辦法
這時候,你就需要使用策略模式,將他們的行爲抽取出來,則Fight爲:

/**
 * @author hobart
 * 戰鬥接口
 */
public interface IFight {
	void fight();
}
/**
 * @author hobart
 * 少林戰鬥
 */
public class ShaolinFight implements IFight {
	public void fight() {
		System.out.println("我是少林派角色,我用棍戰鬥!");
	}
}
/**
 * @author hobart
 * 武當戰鬥
 */
public class WudangFight implements IFight {
	public void fight() {
		System.out.println("我是武當派角色,我用劍戰鬥!");
	}
}
/**
 * @author hobart
 * 野怪戰鬥
 */
public class WildMonsterFight implements IFight {
	public void fight() {
		System.out.println("我是野怪角色,我用爪子戰鬥!");
	}
}

learningEsoterica

public class ShaolinFight implements IFight {
	public void fight() {
		System.out.println("我是少林派角色,我用棍戰鬥!");
	}
}
/**
 * @author hobart
 * 武當戰鬥
 */
public class WudangFight implements IFight {
	public void fight() {
		System.out.println("我是武當派角色,我用劍戰鬥!");
	}
}
/**
 * @author hobart
 * 野怪戰鬥
 */
public class WildMonsterFight implements IFight {
	public void fight() {
		System.out.println("我是野怪角色,我用爪子戰鬥!");
	}
}

學習技能

/**
 * @author hobart
 * 少林學習技能
 */
public class ShaolinEsoterica implements ILearningEsoterica{
	public void learningEsoterica() {
		System.out.println("少林派角色學習了易筋經!");
	}
}
/**
 * @author hobart
 * 武當學習技能
 */
public class WudangEsoterica implements ILearningEsoterica{
	public void learningEsoterica() {
		System.out.println("武當派角色學習了太極劍法!");
	}
}

** 應用上下文(Context)**

public class ContextRole {
	private IFight fight = null;
	private ILearningEsoterica esoterica = null;
    public ContextRole(IFight fight){
        this.fight = fight;
    }
    public ContextRole(IFight fight, ILearningEsoterica esoterica){
    	this.fight = fight;
    	this.esoterica = esoterica;
    }
    public void fight(){
    	if(fight != null) {
    		this.fight.fight();
        }
    }
    public void learningEsoterica(){
    	if(esoterica != null) {
    		this.esoterica.learningEsoterica();
    	}
    }
}

使用

public class StrategyUse {
	public static void main(String[] args) {
		ContextRole wudangRole = new ContextRole(new WudangFight(), new WudangEsoterica());
		wudangRole.fight();
		wudangRole.learningEsoterica();
		ContextRole wildMonster = new ContextRole(new WildMonsterFight());
		wildMonster.fight();
	}
}

總結

策略模式用於算法的自由切換和擴展,它是應用較爲廣泛的設計模式之一。策略模式對應於解決某一問題的一個算法族,允許用戶從該算法族中任選一個算法來解決某一問題,同時可以方便地更換算法或者增加新的算法。只要涉及到算法的封裝、複用和切換都可以考慮使用策略模式。
1. 主要優點
策略模式的主要優點如下:
(1) 策略模式提供了對“開閉原則”的完美支持,用戶可以在不修改原有系統的基礎上選擇算法或行爲,也可以靈活地增加新的算法或行爲。
(2) 策略模式提供了管理相關的算法族的辦法。策略類的等級結構定義了一個算法或行爲族,恰當使用繼承可以把公共的代碼移到抽象策略類中,從而避免重複的代碼。
(3) 策略模式提供了一種可以替換繼承關係的辦法。如果不使用策略模式,那麼使用算法的環境類就可能會有一些子類,每一個子類提供一種不同的算法。但是,這樣一來算法的使用就和算法本身混在一起,不符合“單一職責原則”,決定使用哪一種算法的邏輯和該算法本身混合在一起,從而不可能再獨立演化;而且使用繼承無法實現算法或行爲在程序運行時的動態切換。
(4) 使用策略模式可以避免多重條件選擇語句。多重條件選擇語句不易維護,它把採取哪一種算法或行爲的邏輯與算法或行爲本身的實現邏輯混合在一起,將它們全部硬編碼(Hard Coding)在一個龐大的多重條件選擇語句中,比直接繼承環境類的辦法還要原始和落後。
(5) 策略模式提供了一種算法的複用機制,由於將算法單獨提取出來封裝在策略類中,因此不同的環境類可以方便地複用這些策略類。
2. 主要缺點
策略模式的主要缺點如下:
(1) 客戶端必須知道所有的策略類,並自行決定使用哪一個策略類。這就意味着客戶端必須理解這些算法的區別,以便適時選擇恰當的算法。換言之,策略模式只適用於客戶端知道所有的算法或行爲的情況。
(2) 策略模式將造成系統產生很多具體策略類,任何細小的變化都將導致系統要增加一個新的具體策略類。
(3) 無法同時在客戶端使用多個策略類,也就是說,在使用策略模式時,客戶端每次只能使用一個策略類,不支持使用一個策略類完成部分功能後再使用另一個策略類來完成剩餘功能的情況。
3. 適用場景
在以下情況下可以考慮使用策略模式:
(1) 一個系統需要動態地在幾種算法中選擇一種,那麼可以將這些算法封裝到一個個的具體算法類中,而這些具體算法類都是一個抽象算法類的子類。換言之,這些具體算法類均有統一的接口,根據“里氏代換原則”和麪向對象的多態性,客戶端可以選擇使用任何一個具體算法類,並只需要維持一個數據類型是抽象算法類的對象。
(2) 一個對象有很多的行爲,如果不用恰當的模式,這些行爲就只好使用多重條件選擇語句來實現。此時,使用策略模式,把這些行爲轉移到相應的具體策略類裏面,就可以避免使用難以維護的多重條件選擇語句。
(3) 不希望客戶端知道複雜的、與算法相關的數據結構,在具體策略類中封裝算法與相關的數據結構,可以提高算法的保密性與安全性。

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