1. 程式人生 > >策略模式和工廠模式搭配使用

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

策略模式和工廠模式的搭配使用可以很好地消除程式碼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 釋出!