SpringBoot與策略模式的結合

  策略模式屬於對象的行爲模式。其用意是針對一組算法,將每一個算法封裝到具有共同接口的獨立的類中,從而使得它們可以相互替換。策略模式使得算法可以在不影響到客戶端的情況下發生變化。

在系統中,常常需要根據不同的用戶類型或者屬性進行相應的處理,常見做法,需要對處理方式進行抽象,同時根據不同的類型或者屬性來指定對應的實現類來進行處理。

假設現在要設計一個電子商務網站的購物車系統。本網站可能對所有的高級會員7折折扣;對中級會員8折折扣;對初級會員9折折扣,普通用戶沒有折扣。

抽象一個計算價格的接口

public interface PriceStrategy {
    double calculatePrice(double fee);
}

高級vip

public class SeniorVIPStrategy implements PriceStrategy {
    @Override
    public double calculatePrice(double fee) {
        return fee * 0.7;
    }
}

中級vip

public class IntermediateVIPStrategy implements PriceStrategy {
    @Override
    public double calculatePrice(double fee) {
        return fee * 0.8;
    }
}

低級VIP

public class InitialVIPStrategy implements PriceStrategy {
    @Override
    public double calculatePrice(double fee) {
        return fee * 0.9;
    }
}

普通用戶

public class UserStrategy implements PriceStrategy {
    @Override
    public double calculatePrice(double fee) {
        return fee;
    }
}

費用類

public class Charge {
    private PriceStrategy strategy;

    public Charge(PriceStrategy strategy) {
        this.strategy = strategy;
    }
    
    public double price(double fee) {
        return strategy.calculatePrice(fee);
    }
}

接下來在業務代碼中使用

public double charge(User user, double cost) {
    Charge charge;
    double cost = 100.0;
    switch(user.getUserType()) {
        case 1:
            charge = new Charge(new SeniorVIPStrategy());
            break;
        case 2:
            charge = new Charge(new IntermediateVIPStrategy());
            break;
        case 3:
            charge = new Charge(new InitialVIPStrategy());
            break;
        case 4:
            charge = new Charge(new UserStrategy());
            break;
        default:
            throw new RuntimeException("不支持的用戶類型");
    }
    return charge.price(cost);
}

這樣導致代碼裏面有大量的判斷存在,如果有新的用戶類型及對應的計算規則,需要修改這部分的代碼 ,繁瑣異常。

接下來利用SpringBoot對上述流程進行些微的改造:

1.定義一個註解,用來標註用戶類型

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
//@Inherited //如果需要事務處理,需要加上這個註解
public @interface UserTypeAnnotation {
    int userType() default 0;
}

2.同上面的流程一樣,定義一系列的價格策略計算類簇,但是注意,需要在每個實現類加上上面自定義的註解,並配置上對應的值

@Component
@UserTypeAnnotation(userType = 1)
//高級會員
public class SeniorVIPStrategy implements PriceStrategy {
    public double calculatePrice(double cost) {
        return cost * 0.7;
    }
}

@Component
@UserTypeAnnotation(userType = 2)
//中級會員
public class IntermediateVIPStrategy implements PriceStrategy {
    public double calculatePrice(double cost) {
        return cost * 0.8;
    }
}
@Component
@UserTypeAnnotation(userType = 3)
//初級會員
public class InitialVIPStrategy implements PriceStrategy {
    public double calculatePrice(double cost) {
        return cost * 0.9;
    }
}
@Component
@UserTypeAnnotation(userType = 4)
//普通用戶
public class UserStrategy implements PriceStrategy {
    public double calculatePrice(double cost) {
        return cost;
    }
}

3.在容器啓動時,初始化一個map,用來存放userType與對應策略處理類的映射關係

@Component
public class StrategyFactory implements CommandLineRunner, ApplicationContextAware {
    private volatile ApplicationContext applicationContext;
    private static HashMap<Integer, PriceStrategy> userTypeStrategyMap;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public void run(String... args) {
        initUserTypeStrategyMap();
    }

    private void initUserTypeStrategyMap() {
        Collection<PriceStrategy> userTypeStrategyList = this.applicationContext.getBeansOfType(PriceStrategy.class).values();
        userTypeStrategyMap = new HashMap<>(userTypeStrategyList.size());
        for (PriceStrategy strategy : userTypeStrategyList) {
            Class<? extends PriceStrategy> aClass = strategy.getClass();
            UserTypeAnnotation annotation = aClass.getAnnotation(UserTypeAnnotation.class);
            if (annotation != null) {
                userTypeStrategyMap.put(annotation.userType(), strategy);
            }
        }
    }

    
    //通過這個方法獲取對應的價格計算策略實現類
    public static PriceStrategy getPriceStrategyByUserType(Integer userType) {
        PriceStrategy strategy = userTypeStrategyMap.get(userType);
        if (strategy == null) {
            throw new RuntimeException("不支持的用戶類型");
        }
        return strategy;
    }
}

4.調用

public double charge(User user, double cost) {
   double cost = 100.0;
   return StrategyFactory.getPriceStrategyByUserType(user.getUserType()).calculatePrice(cost);
}

這樣當有新的user_type及價格計算方式出現時,只需要實現PriceStrategy接口並在對應的實現類上面加入@Component和@UserTypeAnnotation註解即可,無需修改業務代碼 !

測試代碼地址:https://github.com/zksqq1/DPStrategy

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章