策略模式和工廠模式搭配使用

  • 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中也是可以的。

參考

if-else

if-else2

if-else3

本文由部落格一文多發平台 OpenWrite 發布!