詳解Java如何優(yōu)雅的使用策略模式
最近這段時間,想給大家分享一下設(shè)計模式的一些用法以及在項目中怎么運(yùn)用。
設(shè)計模式是軟件設(shè)計中常見問題的典型解決方案。 它們就像能根據(jù)需求進(jìn)行調(diào)整的預(yù)制藍(lán)圖, 可用于解決代碼中反復(fù)出現(xiàn)的設(shè)計問題。
今天就拿其中一個問題來分析,使用策略模式來解決問題,沒有了解過策略模式或者長時間不用已經(jīng)忘了策略模式的小伙伴先來簡單了解一下策略模式吧。
什么是策略模式
策略模式是一種行為型模式,它將對象和行為分開,將行為定義為 一個行為接口 和 具體行為的實現(xiàn)。策略模式最大的特點(diǎn)是行為的變化,行為之間可以相互替換。每個if判斷都可以理解為就是一個策略。本模式使得算法可獨(dú)立于使用它的用戶而變化。
簡單理解就是,針對不同的場景,使用不同的策略進(jìn)行處理。
策略模式結(jié)構(gòu)

- Strategy 接口定義了一個算法族,它們都實現(xiàn)了 behavior() 方法。
- Context 是使用到該算法族的類,其中的 doSomething() 方法會調(diào)用 behavior(),setStrategy(Strategy) 方法可以動態(tài)地改變 strategy 對象,也就是說能動態(tài)地改變 Context 所使用的算法。
策略模式適用場景
- 如果在一個系統(tǒng)里面有許多類,它們之間的區(qū)別僅在于它們 的行為,那么使用策略模式可以動態(tài)地讓一個對象在許多行 為中選擇一種行為。
- 一個系統(tǒng)需要動態(tài)地在幾種算法中選擇一種。
- 如果一個對象有很多的行為,如果不用恰當(dāng)?shù)哪J?,這些行 為就只好使用多重的條件選擇語句來實現(xiàn)。
- 不希望客戶端知道復(fù)雜的、與算法相關(guān)的數(shù)據(jù)結(jié)構(gòu),在具體策略類中封裝算法和相關(guān)的數(shù)據(jù)結(jié)構(gòu),提高算法的保密性與安全性。
生活中比較常見的應(yīng)用模式有:
- 電商網(wǎng)站支付方式,一般分為銀聯(lián)、微信、支付寶,可以采用策略模式。
- 電商網(wǎng)站活動方式,一般分為滿減送、限時折扣、包郵活動,拼團(tuán)等可以采用策略模式。
簡單示例
場景:最近太熱了,想要降降溫,有什么辦法呢
首先,定義一個降溫策略的接口
public interface CoolingStrategy {
/**
* 處理方式
*/
void handle();
}定義3種降溫策略;實現(xiàn)策略接口
public class IceCoolingStrategy implements CoolingStrategy {
@Override
public void handle() {
System.out.println("使用冰塊降溫");
}
}public class FanCoolingStrategy implements CoolingStrategy {
@Override
public void handle() {
System.out.println("使用風(fēng)扇降溫");
}
}public class AirConditionerCoolingStrategy implements CoolingStrategy {
@Override
public void handle() {
System.out.println("使用空調(diào)降溫");
}
}定義一個降溫策略的上下文
public class CoolingStrategyContext {
private final CoolingStrategy strategy;
public CoolingStrategyContext(CoolingStrategy strategy) {
this.strategy = strategy;
}
public void coolingHandle() {
strategy.handle();
}
} 測試
public class Main {
public static void main(String[] args) {
CoolingStrategyContext context = new CoolingStrategyContext(new FanCoolingStrategy());
context.coolingHandle();
context = new CoolingStrategyContext(new AirConditionerCoolingStrategy());
context.coolingHandle();
context = new CoolingStrategyContext(new IceCoolingStrategy());
context.coolingHandle();
}
} 運(yùn)行結(jié)果:
使用風(fēng)扇降溫
使用空調(diào)降溫
使用冰塊降溫
以上就是一個策略模式的簡單實現(xiàn)
項目實戰(zhàn)
場景
模擬在購買商品時候使用的各種類型優(yōu)惠券(滿減、直減、折扣、n元購)
這個場景幾乎也是大家的一個日常購物省錢渠道,購買商品的時候都希望找一些優(yōu)惠券,讓購買的商品更加實惠。而且到了大促的時候就會有更多的優(yōu)惠券需要計算那些商品一起購買更加優(yōu)惠!
用一坨坨代碼實現(xiàn)
/**
* 優(yōu)惠券折扣計算接口
* <p>
* 優(yōu)惠券類型;
* 1. 直減券
* 2. 滿減券
* 3. 折扣券
* 4. n元購
*/
public class CouponDiscountService {
public double discountAmount(int type, double typeContent, double skuPrice, double typeExt) {
// 1. 直減券
if (1 == type) {
return skuPrice - typeContent;
}
// 2. 滿減券
if (2 == type) {
if (skuPrice < typeExt) return skuPrice;
return skuPrice - typeContent;
}
// 3. 折扣券
if (3 == type) {
return skuPrice * typeContent;
}
// 4. n元購
if (4 == type) {
return typeContent;
}
return 0D;
}
}- 以上是不同類型的優(yōu)惠券計算折扣后的實際金額。
- 入?yún)?;?yōu)惠券類型、優(yōu)惠券金額、商品金額,因為有些優(yōu)惠券是滿多少減少多少,所以增加了
typeExt類型。這也是方法的不好擴(kuò)展性問題。 - 最后是整個的方法體中對優(yōu)惠券抵扣金額的實現(xiàn),最開始可能是一個最簡單的優(yōu)惠券,后面隨著產(chǎn)品功能的增加,不斷的擴(kuò)展
if語句。實際的代碼可能要比這個多很多
策略模式重構(gòu)代碼

- 整體的結(jié)構(gòu)模式并不復(fù)雜,主要體現(xiàn)的不同類型的優(yōu)惠券在計算優(yōu)惠券方式的不同計算策略。
- 這里包括一個接口類(
ICouponDiscount)以及四種優(yōu)惠券類型的實現(xiàn)方式。 - 最后提供了策略模式的上下控制類處理,整體的策略服務(wù)。
代碼實現(xiàn)
優(yōu)惠券接口
public interface ICouponDiscount<T> {
/**
* 優(yōu)惠券金額計算
* @param couponInfo 券折扣信息;直減、滿減、折扣、N元購
* @param skuPrice sku金額
* @return 優(yōu)惠后金額
*/
BigDecimal discountAmount(T couponInfo, BigDecimal skuPrice);
}- 定義了優(yōu)惠券折扣接口,也增加了泛型用于不同類型的接口可以傳遞不同的類型參數(shù)。
- 接口中包括商品金額以及出參返回最終折扣后的金額,這里在實際開發(fā)中會比現(xiàn)在的接口參數(shù)多一些,但核心邏輯是這些。
優(yōu)惠券接口實現(xiàn)
滿減
public class MJCouponDiscount implements ICouponDiscount<Map<String,String>> {
/**
* 滿減計算
* 1. 判斷滿足x元后-n元,否則不減
* 2. 最低支付金額1元
*/
public BigDecimal discountAmount(Map<String,String> couponInfo, BigDecimal skuPrice) {
String x = couponInfo.get("x");
String o = couponInfo.get("n");
// 小于商品金額條件的,直接返回商品原價
if (skuPrice.compareTo(new BigDecimal(x)) < 0) return skuPrice;
// 減去優(yōu)惠金額判斷
BigDecimal discountAmount = skuPrice.subtract(new BigDecimal(o));
if (discountAmount.compareTo(BigDecimal.ZERO) < 1) return BigDecimal.ONE;
return discountAmount;
}
}直減
public class ZJCouponDiscount implements ICouponDiscount<Double> {
/**
* 直減計算
* 1. 使用商品價格減去優(yōu)惠價格
* 2. 最低支付金額1元
*/
public BigDecimal discountAmount(Double couponInfo, BigDecimal skuPrice) {
BigDecimal discountAmount = skuPrice.subtract(new BigDecimal(couponInfo));
if (discountAmount.compareTo(BigDecimal.ZERO) < 1) return BigDecimal.ONE;
return discountAmount;
}
}折扣
public class ZKCouponDiscount implements ICouponDiscount<Double> {
/**
* 折扣計算
* 1. 使用商品價格乘以折扣比例,為最后支付金額
* 2. 保留兩位小數(shù)
* 3. 最低支付金額1元
*/
public BigDecimal discountAmount(Double couponInfo, BigDecimal skuPrice) {
BigDecimal discountAmount = skuPrice.multiply(new BigDecimal(couponInfo)).setScale(2, BigDecimal.ROUND_HALF_UP);
if (discountAmount.compareTo(BigDecimal.ZERO) < 1) return BigDecimal.ONE;
return discountAmount;
}
}N元購
public class NYGCouponDiscount implements ICouponDiscount<Double> {
/**
* n元購購買
* 1. 無論原價多少錢都固定金額購買
*/
public BigDecimal discountAmount(Double couponInfo, BigDecimal skuPrice) {
return new BigDecimal(couponInfo);
}
}以上是四種不同類型的優(yōu)惠券計算折扣金額的策略方式,可以從代碼中看到每一種優(yōu)惠方式的優(yōu)惠金額。
策略控制類
public class Context<T> {
private ICouponDiscount<T> couponDiscount;
public Context(ICouponDiscount<T> couponDiscount) {
this.couponDiscount = couponDiscount;
}
public BigDecimal discountAmount(T couponInfo, BigDecimal skuPrice) {
return couponDiscount.discountAmount(couponInfo, skuPrice);
}
}- 策略模式的控制類主要是外部可以傳遞不同的策略實現(xiàn),在通過統(tǒng)一的方法執(zhí)行優(yōu)惠策略計算。
- 另外這里也可以包裝成map結(jié)構(gòu),讓外部只需要對應(yīng)的泛型類型即可使用相應(yīng)的服務(wù)。
測試類
public class ApiTest {
private Logger logger = LoggerFactory.getLogger(ApiTest.class);
@Test
public void test_zj() {
// 直減;100-10,商品100元
Context<Double> context = new Context<Double>(new ZJCouponDiscount());
BigDecimal discountAmount = context.discountAmount(10D, new BigDecimal(100));
logger.info("測試結(jié)果:直減優(yōu)惠后金額 {}", discountAmount);
}
@Test
public void test_mj() {
// 滿100減10,商品100元
Context<Map<String,String>> context = new Context<Map<String,String>>(new MJCouponDiscount());
Map<String,String> mapReq = new HashMap<String, String>();
mapReq.put("x","100");
mapReq.put("n","10");
BigDecimal discountAmount = context.discountAmount(mapReq, new BigDecimal(100));
logger.info("測試結(jié)果:滿減優(yōu)惠后金額 {}", discountAmount);
}
@Test
public void test_zk() {
// 折扣9折,商品100元
Context<Double> context = new Context<Double>(new ZKCouponDiscount());
BigDecimal discountAmount = context.discountAmount(0.9D, new BigDecimal(100));
logger.info("測試結(jié)果:折扣9折后金額 {}", discountAmount);
}
@Test
public void test_nyg() {
// n元購;100-10,商品100元
Context<Double> context = new Context<Double>(new NYGCouponDiscount());
BigDecimal discountAmount = context.discountAmount(90D, new BigDecimal(100));
logger.info("測試結(jié)果:n元購優(yōu)惠后金額 {}", discountAmount);
}
}- 以上四組測試分別驗證了不同類型優(yōu)惠券的優(yōu)惠策略,測試結(jié)果是滿足我們的預(yù)期。
- 這里四種優(yōu)惠券最終都是在原價100元上折扣10元,最終支付90元。
總結(jié)
通過策略設(shè)計模式的使用可以把我們方法中的 if 語句優(yōu)化掉,大量的 if 語句使用會讓代碼難以擴(kuò)展,也不好維護(hù),同時在后期遇到各種問題也很難維護(hù)。在使用這樣的設(shè)計模式后可以很好的滿足隔離性與和擴(kuò)展性,對于不斷新增的需求也非常方便承接。
了解策略模式的優(yōu)點(diǎn)和缺點(diǎn),合理的使用策略模式,會讓你的代碼更加的整潔優(yōu)雅。
以上就是詳解Java如何優(yōu)雅的使用策略模式的詳細(xì)內(nèi)容,更多關(guān)于Java使用策略模式的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
基于SpringBoot實現(xiàn)動態(tài)配置數(shù)據(jù)庫的加載
這篇文章主要介紹了Spring?Boot?如何動態(tài)配置數(shù)據(jù)庫的加載,現(xiàn)項目有一個需求,期望通過在application.yml配置文件中設(shè)置一個開關(guān),來決定是否加載數(shù)據(jù)庫,文中通過代碼示例講解的非常詳細(xì),需要的朋友可以參考下2024-10-10
Spring?Boot?打包成Jar包運(yùn)行原理分析
這篇文章主要為大家介紹了Spring?Boot?打包成Jar包運(yùn)行的原理分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09
springboot+springsecurity+mybatis+JWT+Redis?實現(xiàn)前后端離實戰(zhàn)教程
這篇文章主要介紹了springboot+springsecurity+mybatis+JWT+Redis?實現(xiàn)前后端離實戰(zhàn)教程,需要的朋友可以參考下2024-01-01
java經(jīng)典問題:連個字符串互為回環(huán)變位
連個字符串互為回環(huán)變位經(jīng)常出現(xiàn)在java程序員面試中,這個是考驗程序員的解題思路和方法的最經(jīng)典的一題,小編為大家詳細(xì)分析一下,一起來學(xué)習(xí)吧。2017-11-11
netty服務(wù)端輔助類ServerBootstrap創(chuàng)建邏輯分析
這篇文章主要介紹了netty服務(wù)端輔助類ServerBootstrap創(chuàng)建邏輯分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03

