【java設計模式】(7)---策略模式(案例解析)

策略模式

1、概念

一、理解策略模式

策略模式是一種行爲型模式,它將對象和行爲分開,將行爲定義爲 一個行爲接口具體行爲的實現。策略模式最大的特色是行爲的變化,行爲之間能夠相互替換。java

每一個if判斷均可以理解爲就是一個策略。git

二、策略模式特色

策略模式把對象自己和行爲區分開來,所以咱們整個模式也分爲三個部分。github

一、抽象策略類(Strategy):策略的抽象,行爲方式的抽象
二、具體策略類(ConcreteStrategy):具體的策略實現,每一種行爲方式的具體實現。
三、環境類(Context):用來封裝具體行爲,操做策略的上下文環境。

三、舉例理解(打車)

這裏舉個簡單的例子,來理解開發中運用策略模式的場景。算法

有一個打車軟件,如今有三種計費模式給 用戶 選擇,一、拼車 二、快車 三、豪車這個時候用戶就是對象,三種計費方式就是行爲,能夠根據不一樣的行爲計算不一樣不通的值。spring

1)傳統實現方式數據庫

代碼segmentfault

/**
      * @Description: 這裏只展現計費最終費用示例
      *
      * @param  type 計費類型
      * @param  originalPrice 原始價格
      */
    public Double calculationPrice(String type, Double originalPrice) {

        //拼車計費
        if (type.equals("pc")) {
            return originalPrice * 0.5;
        }
        //快車計費
        if (type.equals("kc")) {
            return originalPrice * 1;
        }
        //豪車計費
        if (type.equals("hc")) {
            return originalPrice * 2;
        }
        return originalPrice;
    }

傳統的實現方式,經過傳統if代碼判斷。這樣就會致使後期的維護性很是差。當後期須要新增計費方式,還須要在這裏再加上if(),也不符合設計模式的開閉原則。設計模式

2)策略模式實現微信

抽象策略類架構

/**
 * 出行策略接口
 */
public interface PriceStrategy {
	/**
	 * @param originalPrice 原始價格
	 * @return  計算後的價格
	 */
	Double countPrice(Double originalPrice);
}

具體策略實現類

/**
  * @Description: 拼車的計費方式
  */
public class PcStrategy implements PriceStrategy {
    @Override
    public Double countPrice(Double originalPrice) {
        return originalPrice * 0.5;
    }
}

/**
  * @Description: 快車的計費方式
  */
public class KcStrategy implements PriceStrategy {
    @Override
    public Double countPrice(Double originalPrice) {
        return originalPrice * 1;
    }
}

/**
  * @Description: 拼車的計費方式
  */
public class HcStrategy implements PriceStrategy {
    @Override
    public Double countPrice(Double originalPrice) {
        return originalPrice * 2;
    }
}

環境類

也叫作上下文類或環境類,起承上啓下封裝做用。

/**
 * 負責和具體的策略類交互
 * 這樣的話,具體的算法和直接的客戶端調用分離了,使得算法能夠獨立於客戶端獨立的變化。
 * 若是使用spring的依賴注入功能,還能夠經過配置文件,動態的注入不一樣策略對象,動態的切換不一樣的算法.
 */
public class PriceContext {

    /**
     * 出行策略接口
     */
    private PriceStrategy riceStrategy;
    /**
     * 構造函數注入
     */
    public PriceContext(PriceStrategy riceStrategy) {
        this.riceStrategy = riceStrategy;
    }
    /**
     * 計算價格
     */
    public Double countPrice(Double originalPrice) {
        return riceStrategy.countPrice(originalPrice);
    }
}

測試類

public static void main(String[] args) {
        //具體行爲策略
        PriceStrategy pcStrategy = new PcStrategy();
        PriceStrategy kcStrategy = new KcStrategy();
        PriceStrategy hcStrategy = new HcStrategy();

        //用戶選擇不一樣的策略
        PriceContext pcContext = new PriceContext(pcStrategy);
        PriceContext kcContext = new PriceContext(kcStrategy);
        PriceContext hcContext = new PriceContext(hcStrategy);

        System.out.println("拼車價格 = " +  pcContext.countPrice(10D));
        System.out.println("快車價格 = " +  kcContext.countPrice(10D));
        System.out.println("豪車價格 = " +  hcContext.countPrice(10D));
    }

運行結果

拼車價格 = 5.0
快車價格 = 10.0
豪車價格 = 20.0

整理流程就是這個樣的,這裏有一點須要注意 我在作測試的時候具體策略對象都是new出來,而實際運用應該經過spring來管理,這樣才能作到真正的開閉原則。下面會在舉例。

四、策略模式優缺點

優勢

1)避免使用多重條件判斷

若是沒有策略模式,一個策略家族有多個策略算法,一會要使用A策略,一會要使用B策略,怎麼設計呢?使用多重if的條件語句?多重條件語句不易維護,並且出錯的機率大大加強。

使用策略模式後,簡化了操做,同時避免了條件語句判斷。

2)擴展性良好

在現有的系統中增長一個策略太容易了,只要實現接口就能夠了,其餘都不用修改,相似於一個可反覆拆卸的插件,這大大地符合了OCP原則。

缺點

1)策略類數量增多

策略模式一個明顯的缺點是當備用行爲過多時,行爲對象會很是龐大

五、策略模式運用場景

經過上面的優缺點咱們能夠很好的去思考,什麼場景下能夠考慮用策略模式?

個人理解就是:每一個if判斷均可以理解爲就是一個策略。按理說均可以採用策略模式,可是若是if else裏面的邏輯很少,且複用性很低,那就不須要。若是if裏面的行爲比較大

並且這些行爲複用性比較高就能夠考慮經過採用策略模式。

在咱們生活中比較常見的應用模式有:

一、電商網站支付方式,通常分爲銀聯、微信、支付寶,能夠採用策略模式
二、電商網站活動方式,通常分爲滿減送、限時折扣、包郵活動,拼團等能夠採用策略模式

2、策略模式實戰示例

最近正在作到電商項目,由於有多個活動,因此我就考慮用策略模式來實現。咱們活動分爲不少種滿減送,包郵活動,限時折扣等等,這裏大體寫下對於多個活動如何去使用策略模式。

一、Order實體

活動是跟訂單綁定在一塊兒的,只有下了單纔去計算這個訂單走了哪一個活動。

/**
  * @Description: 訂單實體
  */
public class Order {
    /**
     * 用戶ID
     */
    private Long userId;
    /**
     * 訂單編號
     */
    private String orderNumber;
    /**
     * 購買數量
     */
    private Integer goodsNumber;
    /**
     * 訂單運費
     */
    private Double orderFreight;
    /**
     * 訂單總價(訂單價格 + 運費)
     */
    private Double orderPrice;
    /**
     * 活動類型 一、包郵 二、滿減送 三、限時折扣
     */
    private String activityType;
    /**
     * 活動ID
     */
    private String activityId;
  //省略get set方法

二、ActivityStrategy(活動策略接口)

/**
 * 定義一個總的活動抽象
 */
public interface ActivityStrategy {

    /**
     * 定義一個咱們優惠活動的價格算法方法
     */
    Order calculate (Order order);
}

三、具體活動策略實現類

FreeShippingActivity 包郵

/**
  * @Description: 包郵活動
  */
@Component
@Service("freeShippingActivity")
public class FreeShippingActivity implements ActivityStrategy {
    @Override
    public Order calculate(Order order) {
        //包郵活動是一個大的主題 ,裏面能夠建立不少小活動 好比價格滿100包郵活動,或者滿2件以上包郵活動,江浙滬包郵活動等等
        //若是這裏經過活動ID獲取用戶具體選擇了哪個活動。
        String activityId = order.getActivityId();
        //查詢數據庫
        System.out.println("模擬查詢數據庫 ,當前的活動是滿100包郵");
        order.setOrderFreight(0.0D);
        return order;
    }
}

FullDeliveryActivity (滿減送活動)

/**
  * @Description: 滿減送活動
  */
@Component
@Service("fullDeliveryActivity")
public class FullDeliveryActivity implements ActivityStrategy {
    @Override
    public Order calculate(Order order) {
        //若是這裏經過活動ID獲取用戶具體選擇了哪個活動。
        String activityId = order.getActivityId();
        //查詢數據庫
        System.out.println("模擬查詢數據庫 ,當前的活動是滿100減20的");
        if (order.getOrderPrice() > 100) {
            order.setOrderPrice(order.getOrderPrice() - 20);
        }
        return order;
    }
}

LimitDiscountActivity (限時折扣活動)

/**
  * @Description: 限時折扣活動
  */
@Component
@Service("limitDiscountActivity")
public class LimitDiscountActivity implements ActivityStrategy {
    @Override
    public Order calculate(Order order) {
        //若是這裏經過活動ID獲取用戶具體選擇了哪個活動。
        String activityId = order.getActivityId();
        //查詢數據庫
        System.out.println("模擬查詢數據庫 ,當前的活動是截至2020.10.1前 打9折");
            order.setOrderPrice(order.getOrderPrice() * 0.9);

        return order;
    }
}

三、環境類

/**
 * 負責和具體的策略類交互 動態的切換不一樣的算法
 */
@Component
public class ActivityContext {

    @Autowired
    private FreeShippingActivity freeShippingActivity;

    @Autowired
    FullDeliveryActivity fullDeliveryActivity;

    @Autowired
    LimitDiscountActivity limitDiscountActivity;

    /**
     * 出行策略接口
     */
    private final Map<String, ActivityStrategy> activityStrategyMap = new HashMap();
  
    /**
     * 初始化 把這幾個活動的示例 初始化的時候就裝到一個map集合中
     */
    @PostConstruct
    public void init() {
        //一、包郵 二、滿減送 三、限時折扣
        activityStrategyMap.put("1", freeShippingActivity);
        activityStrategyMap.put("2", fullDeliveryActivity);
        activityStrategyMap.put("3", limitDiscountActivity);
    }

    /**
     * 計算價格
     */
    public Order calculate(Order order) {
        ActivityStrategy activityStrategy = activityStrategyMap.get(order.getActivityType());
        return activityStrategy.calculate(order);
    }
}

四、測試類

@RestController
public class PayController {

    @Autowired
    private ActivityContext activityContext;

    @RequestMapping("/test")
    public  Object test(){
        Order order = new Order();
        //1 表明包郵
        order.setActivityType("1");
        //具體活動ID
        order.setActivityId("12");
        //總價
        order.setOrderPrice(200D);
        //運費
        order.setOrderFreight(10D);
        return activityContext.calculate(order);
    }
}
//輸出: 模擬查詢數據庫 ,當前的活動是包郵

總結 這裏咱們沒有用到if else來判斷用戶到底選擇了哪一個活動類型,而是經過先把全部活動的bean實體裝入一個map中,而後經過activityType 來獲取具體是哪一個活動類型。

之後新添加一個活動,好比拼團活動,咱們只需作兩步

一、新建一個拼團活動策略類 實現總策略接口 二、activityStrategyMap中put這個折扣活動的bean實體。

除了map管理bean外,還有一種方式就是能夠經過spring來管理這些具體的策略類

//freeShippingActivity 這個值能夠在數據庫添加一張表來管理,而後來讀取 這樣的話新建活動只須要作上面的第一步,代碼更加好維護了。
ActivityStrategy payStrategy= SpringUtils.getBean("freeShippingActivity", ActivityStrategy.class);

這裏只是列了個架構,實際開發中比這個複雜多了,由於能夠同時選擇多個活動,活動於活動之間又會有互斥關係。


參考

一、策略模式

二、支付平臺選擇(策略模式)

三、Java設計模式-策略模式



別人罵我胖,我會生氣,由於我內心認可了我胖。別人說我矮,我就會以爲可笑,由於我內心知道我不可能矮。這就是咱們爲何會對別人的攻擊生氣。
攻我盾者,乃我心裏之矛(8)。
相關文章
相關標籤/搜索