策略模式

定義了算法家族,分別封裝起來,讓它們之間可以互相替換,此模式讓算法的變化,不會影響到使用算法的客戶。

例子:商場收銀,營業員根據客戶所購買商品的單價和數量,向客戶收費,但有時候會有打折或者滿減活動,eg:打八折,滿300送100

先用上個學習到的簡單工廠實現一下

建立一個現金收費的抽象類

package celv;

abstract class CashSuper {
//	現金收取超類的抽象方法,收取現金,參數爲原價,返回僞當前價
	public abstract double acceptCash(double money);
}

正常收費子類

package celv;

public class CashNormal extends CashSuper{

	@Override
	public double acceptCash(double money) {
		// TODO Auto-generated method stub
		return money;
	}

}

折扣收費子類

package celv;

public class CashRebate extends CashSuper{
	public double moneyRebate=1;
	public CashRebate(String moneyRebate){
		this.moneyRebate=Double.parseDouble(moneyRebate);
	}

	@Override
	public double acceptCash(double money) {
		// TODO Auto-generated method stub
		return money*moneyRebate;
	}

}

滿減收費子類

package celv;

public class CashReturn extends CashSuper{
	public double moneyCondition=0.0;
	public double moneyReturn=0.0;
	public CashReturn(String moneyCondition,String moneyReturn){
		this.moneyCondition=Double.parseDouble(moneyCondition);
		this.moneyReturn=Double.parseDouble(moneyReturn);
	}

	@Override
	public double acceptCash(double money) {
		// TODO Auto-generated method stub
		double result=money;
		if(money>=moneyCondition){
			result=money-Math.floor(money/moneyCondition)*moneyReturn;
		}
		return result;
	}

}

一個工廠類來生成不同實例

package celv;

public class CashFactory {
	public static CashSuper createCashAccept(String type){
		CashSuper cs=null;
		switch(type){
		case "收費正常":
			CashNormal cs0=new CashNormal();
			cs=cs0;
			break;
		case "滿300返100":
			CashReturn cr1=new CashReturn("300","100");
			cs=cr1;
			break;
		case "打八折":
			CashRebate cr2=new CashRebate("0.8");
			cs=cr2;
			break;
		}
		return cs;
	}
}

測試

package celv;

public class Test {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		double price=50;
		double number=20;
		double total=0;
		String type="滿300返100";
//		簡單工廠模式
		CashSuper csuper=CashFactory.createCashAccept(type);
	        total=csuper.acceptCash(price*number);

		System.out.println(total);
		
	}

}

上述可以完成基本功能要求,但是,工廠本身包括了所有的收費方式,商場是可能經常性地更改打折額度和返利額度,每次維護或擴展收費方式都要改動這個工廠,以至於代碼需要重新編譯部署。

策略模式:商場收銀時如何促銷,打折還是返利,其實都是一些算法,用工廠來生成算法對象沒有錯,但算法本身只是一種策略,最重要的是這些算法是隨時都可能互相替換的,這就是變化點,封裝變化點是面向對象的一種很重要的思維方式。

於是,可以不用工廠,建立一個Context上下文,用一個ConcreteStrategy來配置,維護一個對Strategy對象的應用,即:

package celv;

public class CashContext {
	public CashSuper cs;
//	通過構造方法,傳入具體的收費策略
	public CashContext(CashSuper cs){
		this.cs=cs;
	}
	
//	根據收費策略的不同,獲得計算結果
	public double getResult(double money){
	    return cs.acceptCash(money);
	}
	
}

此時,客戶端的測試可以寫爲:

package celv;

public class celvTest {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		double price=50;
		double number=20;
		double total=0;
		String type="滿300返100";
		
		CashContext cc=null;
		switch(type){
		case "收費正常":
			cc=new CashContext(new CashNormal());
			break;
		case "滿300返100":
			cc=new CashContext(new CashReturn("300","100"));
			break;
		case "打八折":
			cc=new CashContext(new CashRebate("0.8"));
			break;
		}
	    total=cc.getResult(price*number);
		System.out.println(total);
		
	}
}

但是也存在缺點,即讓客戶端去判斷用哪一個算法,可以試着將簡單工工廠和策略模式進行結合,將CashContext類修改

package celv;

public class CashContext {
	public CashSuper cs;
//	通過構造方法,傳入具體的收費策略
	public CashContext(CashSuper cs){
		this.cs=cs;
	}
	
	public CashContext(String type){
		switch(type){
		case "收費正常":
			CashNormal cs0=new CashNormal();
			cs=cs0;
			break;
		case "滿300返100":
			CashReturn cr1=new CashReturn("300","100");
			cs=cr1;
			break;
		case "打八折":
			CashRebate cr2=new CashRebate("0.8");
			cs=cr2;
			break;
		}
	}
//	根據收費策略的不同,獲得計算結果
	public double getResult(double money){
	    return cs.acceptCash(money);
	}
	
}

那麼,客戶端代碼就是這樣子

package celv;

public class Test {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		double price=50;
		double number=20;
		double total=0;
		String type="滿300返100";
		
//		策略模式與簡單工廠結合
		CashContext csuper=new CashContext(type);
	        total=csuper.getResult(price*number);
		System.out.println(total);
		
	}

}

由此可以看出,簡單工廠模式讓客戶端認識兩個類,即CashSuper和CashFactory,而策略模式與簡單工廠結合,客戶端只需要認識一個類CashContext,耦合度更低。

總結:策略模式是一種定義一系列算法的方法,從概念上看,所有這些算法完成的都是相同的工作,只是實現不同,它可以以相同的方式調用所有的算法,減少了各種算法類與使用算法類之間的耦合。簡化了單元測試,因爲每個算法都有自己的類,可以通過自己的接口單獨測試。

參考自《大話設計模式》,代碼自己有所修改,持續更新。

發佈了92 篇原創文章 · 獲贊 14 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章