策略模式和工廠模式搭配使用
- 2019 年 11 月 2 日
- 筆記
策略模式和工廠模式的搭配使用可以很好地消除程式碼
if-else
的多層嵌套
需求
針對店下商鋪,有這樣一個需求,對用戶客戶分為了普通客戶、vip
客戶、超級vip
用戶、專屬vip
用戶4
個等級,每當用戶購買商品時,針對不同的用戶等級和消費金額採取不同的打折優惠策略。在平常的開發當中,必然會出現多層的if-else
嵌套判斷,先判斷用戶的等級再判斷用戶購買商品的消費金額。
弊端
以上的情況出現了多層的if-else
嵌套,除此之外,以後如果需求再有變動,需要再增加一個用戶等級,那麼又會再次添加if-else
的嵌套判斷,那麼如何解決上述的弊端呢,採用策略模式和工廠模式的搭配使用,可以很好地優化多層if-else
的多層嵌套
實現
編寫用戶等級枚舉類
package com.zbiti.ifelse.UserType; /** * 用戶類型枚舉類 */ public enum UserPayServiceEnum { VIP(1,"Vip"), SUPERVIP(2,"SuperVip"), PARTICULALYVIP(3,"ParticularlyVip"), NORMAL(4,"NormalPayService"); /** * 狀態值 */ private int code; /** * 類型描述 */ private String value; private UserPayServiceEnum(int code, String value) { this.code = code; this.value = value; } public int getCode() { return code; } public String getValue() { return value; } public static UserPayServiceEnum valueOf(int code) { for (UserPayServiceEnum type : UserPayServiceEnum.values()) { if (type.getCode()==code) { return type; } } return null; } public static void main(String[] args) { System.out.println(UserPayServiceEnum.VIP.getValue()); } }
編寫不同的用戶等級策略類
以下需要注意的是每個策略類實現了InitializingBean
介面的作用是每當策略類被spring
容器啟動初始化後會調用afterPropertiesSet
方法,而在這個方法裡面的作用是會往工廠里針對不同用戶等級保存其對應的用戶策略引用
編寫打折介面
不同的用戶等級策略類實現該介面,該介面包含了打折方法
package com.zbiti.ifelse.UserType; import java.math.BigDecimal; public interface UserPayService { /** * 計算應付價格 */ public BigDecimal quote(BigDecimal orderPrice); }
編寫普通用戶策略類
package com.zbiti.ifelse.UserType; import org.springframework.beans.factory.InitializingBean; import org.springframework.stereotype.Service; import java.math.BigDecimal; /** * 普通會員不打折原價 */ //實現InitializingBean介面,容器啟動後會調用afterPropertiesSet()方法,往工廠里寫入打折策略 @Service public class NormalPayService implements UserPayService, InitializingBean { @Override public BigDecimal quote(BigDecimal orderPrice) { return new BigDecimal("10"); } @Override public void afterPropertiesSet() throws Exception { UserPayServiceStrategyFactory.register(UserPayServiceEnum.NORMAL.getValue(), this); } }
編寫vip用戶策略類
package com.zbiti.ifelse.UserType; import org.springframework.beans.factory.InitializingBean; import org.springframework.stereotype.Service; import java.math.BigDecimal; /** * 普通會員打9折,消費超100打8折 */ //實現InitializingBean介面,容器啟動後會調用afterPropertiesSet()方法,往工廠里寫入打折策略 @Service public class VipPayService implements UserPayService, InitializingBean { @Override public BigDecimal quote(BigDecimal orderPrice) { if (orderPrice.compareTo(new BigDecimal("100")) > 1) { return new BigDecimal("8"); } return new BigDecimal("9"); } public void myShow(){ System.out.println("myShow method invoke----->"); } @Override public void afterPropertiesSet() throws Exception { UserPayServiceStrategyFactory.register(UserPayServiceEnum.VIP.getValue(), this); } }
編寫超級vip用戶策略類
package com.zbiti.ifelse.UserType; import org.springframework.beans.factory.InitializingBean; import org.springframework.stereotype.Service; import java.math.BigDecimal; /** * 超級會員打8折 */ @Service public class SuperVipPayService implements UserPayService , InitializingBean { @Override public BigDecimal quote(BigDecimal orderPrice) { return new BigDecimal("8"); } @Override public void afterPropertiesSet() throws Exception { UserPayServiceStrategyFactory.register(UserPayServiceEnum.SUPERVIP.getValue(),this); } }
編寫專屬用戶vip策略類
package com.zbiti.ifelse.UserType; import org.springframework.beans.factory.InitializingBean; import org.springframework.stereotype.Service; import java.math.BigDecimal; /** * 專屬會員 下單消費超30打七折 */ @Service public class ParticularlyVipPayService implements UserPayService, InitializingBean { @Override public BigDecimal quote(BigDecimal orderPrice) { if (orderPrice.compareTo(new BigDecimal("30"))>0) { return new BigDecimal("7"); } return new BigDecimal("8"); } @Override public void afterPropertiesSet() throws Exception { UserPayServiceStrategyFactory.register(UserPayServiceEnum.PARTICULALYVIP.getValue(),this); } }
編寫工廠類
注意這裡工廠的register
方法,該方法會在spring
容器啟動初始化bean
即各個不同等級的用戶策略類完成後調用afterPropertiesSet
方法里調用register
方法,當容器啟動完成後,我們的spring
容器中即有了一個鍵為用戶等級,值為用戶等級策略類的map
,在對不同用戶進行優惠打折的時候,可以根據用戶等級來取得當前用戶的策略類
package com.zbiti.ifelse.UserType; import org.springframework.util.Assert; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * 版本二:工廠使用(高級版) */ //@Service public class UserPayServiceStrategyFactory { private static Map<String,UserPayService> services = new ConcurrentHashMap<String,UserPayService>(); public static UserPayService getByUserType(String type){ return services.get(type); } public static void register(String userType,UserPayService userPayService){ Assert.notNull(userType,"userType can't be null"); services.put(userType,userPayService); } }
編寫測試類
package com.zbiti.ifelse; import com.zbiti.ifelse.UserType.UserPayService; import com.zbiti.ifelse.UserType.UserPayServiceStrategyFactory; import com.zbiti.ifelse.UserType.VipPayService; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import java.math.BigDecimal; @SpringBootTest @Slf4j class IfElseApplicationTests { @Test void contextLoads() { calPrice(); } public void calPrice() { BigDecimal orderPrice = new BigDecimal("100"); String vipType = "Vip"; //指定用戶類型,獲得相對應的策略 UserPayService strategy = UserPayServiceStrategyFactory.getByUserType(vipType); // UserPayService strategy2 = UserPayServiceStrategyFactory2.getByUserType(vipType); System.out.println(strategy); // System.out.println(strategy2); BigDecimal quote = strategy.quote(orderPrice); if(strategy instanceof VipPayService){ ((VipPayService) strategy).myShow(); } System.out.println(quote); } }
結果
可以看到vip
用戶打9
折,在這個不同用戶等級購買商品時採取的不同打折策略里,我們沒有出現了多層的if-else
的嵌套
tips
編寫工廠類的實現方式上面是其中一種實現(比較推薦),另外也有其它的方式,可以參考如下
提前將策略寫入到map
,但是這裡需要手動new
策略對象
package com.zbiti.ifelse.UserType; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * 版本一:工廠使用 */ public class UserPayServiceStrategyFactory2 { private static Map<String,UserPayService> services = new ConcurrentHashMap<String,UserPayService>(); public static UserPayService getByUserType(String type){ return services.get(type); } static{ services.put(UserPayServiceEnum.VIP.getValue(), new VipPayService()); services.put(UserPayServiceEnum.SUPERVIP.getValue(), new SuperVipPayService()); services.put(UserPayServiceEnum.PARTICULALYVIP.getValue(), new ParticularlyVipPayService()); services.put(UserPayServiceEnum.NORMAL.getValue(), new NormalPayService()); } }
也可以通過反射,編寫工廠類
package com.zbiti.ifelse.UserType; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * 版本一:工廠使用 */ public class UserPayServiceStrategyFactory3 { private static Map<String, Class<? extends UserPayService>> services = new ConcurrentHashMap<>(); //初始化map,存放策略 static { services.put(UserPayServiceEnum.VIP.getValue(), VipPayService.class); services.put(UserPayServiceEnum.SUPERVIP.getValue(), SuperVipPayService.class); services.put(UserPayServiceEnum.PARTICULALYVIP.getValue(), ParticularlyVipPayService.class); services.put(UserPayServiceEnum.NORMAL.getValue(), NormalPayService.class); } //獲取策略 public static UserPayService getByUserType(String type) { try { Class<? extends UserPayService> userPayServiceClass = services.get(type); return userPayServiceClass.newInstance(); } catch (Exception e) { e.printStackTrace(); return new NormalPayService(); } } }
其實也可以搭配註解的使用,自定義一個註解類,在策略類上標識上註解(值為不同的用戶等級),容器啟動的時候通過掃描我們的自定義註解,寫入map
中也是可以的。
參考
本文由部落格一文多發平台 OpenWrite 發布!