spring+策略模式


需求: 這里虛擬一個業務需求,讓大家容易理解。假設有一個訂單系統,里面的一個功能是根據訂單的不同類型作出不同的處理。

 

1. 常規代碼實現 

1.1 實體類

import lombok.Data;

import java.math.BigDecimal;

@Data
public class OrderDTO {
    private String code;
    private BigDecimal price;
    /**
     * 訂單類型
     * 1: 普通訂單
     * 2: 團購訂單
     * 3: 促銷訂單
     */
    private String type;
}

 

1.2  接口類

import qinfeng.zheng.strategy.dto.OrderDTO;

public interface IOrderService {
    /**
     * 根據訂單類型做出相應的處理
     *
     * @param orderDTO
     * @return
     */
    String handle(OrderDTO orderDTO);

}

 

1.3 接口實現 

import org.springframework.stereotype.Service;
import qinfeng.zheng.strategy.dto.OrderDTO;
import qinfeng.zheng.strategy.service.IOrderService;

@Service
public class V1OrderServiceImpl implements IOrderService {
    @Override
    public String handle(OrderDTO orderDTO) {
        String type = orderDTO.getType();
        if (type.equals("1")) {
            return "處理普通訂單成功";
        }
        if (type.equals("2")) {
            return "處理團購訂單成功";
        }
        if (type.equals("3")) {
            return "處理促銷訂單成功";
        }
        return "訂單類型不存在";
    }
}

 

1.4 結論

  不用說, 這代碼 很low. 

 

2. 使用策略模式實現此功能

   策略模式的關鍵就是一個抽象處理類,配上一個持有這個抽象處理類實例的context. 下面是代碼的具體的實現 

 

2.1 抽象類

import qinfeng.zheng.strategy.dto.OrderDTO;

public abstract class AbsHandler {
    abstract public String handle(OrderDTO orderDTO);
}

 

2.2 具體實現類

import org.springframework.stereotype.Component;
import qinfeng.zheng.strategy.anno.HandlerType;
import qinfeng.zheng.strategy.dto.OrderDTO;

@Component
@HandlerType("1")
public class NormalHandler extends AbsHandler {
    @Override
    public String handle(OrderDTO orderDTO) {
        return "處理普通訂單";
    }
}
import org.springframework.stereotype.Component;
import qinfeng.zheng.strategy.anno.HandlerType;
import qinfeng.zheng.strategy.dto.OrderDTO;

@Component
@HandlerType("2")
public class GroupHandler extends AbsHandler {
    @Override
    public String handle(OrderDTO orderDTO) {
        return "處理團購訂單";
    }
}
import org.springframework.stereotype.Component;
import qinfeng.zheng.strategy.anno.HandlerType;
import qinfeng.zheng.strategy.dto.OrderDTO;

@Component
@HandlerType("3")
public class PromotionHandler extends AbsHandler {
    @Override
    public String handle(OrderDTO orderDTO) {
        return "處理促銷訂單";
    }
}

2.3 context類

public class HandlerContext {
    private Map<String, Class> handlerMap;

    public HandlerContext(Map<String, Class> handlerMap) {
        this.handlerMap = handlerMap;
    }

    public AbsHandler getInstance(String type) throws Exception {
        Class aClass = handlerMap.get(type);
        if (aClass == null) {
            throw new IllegalArgumentException("not found handler type :" + type);
        }
        return (AbsHandler) aClass.newInstance();
    }

}

 

2.3 自定義注解類

import java.lang.annotation.*;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HandlerType {
    String value();
}

 

2.4 自定義BeanFactory后置處理類,將HandlerContext注冊到spring容器中

import cn.hutool.core.lang.ClassScanner;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;
import qinfeng.zheng.strategy.anno.HandlerType;
import qinfeng.zheng.strategy.context.HandlerContext;

import java.util.HashMap;
import java.util.Map;

@Component
public class HandlerProcessor implements BeanFactoryPostProcessor {
    private static final String HANDLE_PACKAGE = "qinfeng.zheng.strategy";

    /**
     * 掃描@HandlerType注解,初始化HandlerContext, 並將其注冊到spring容器中
     * @param beanFactory  bean工廠呀......
     * @throws BeansException
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        Map<String, Class> handlerMap = new HashMap<>();
        ClassScanner.scanPackageByAnnotation(HANDLE_PACKAGE,HandlerType.class).forEach(aClass -> {
            String value = aClass.getAnnotation(HandlerType.class).value();
            handlerMap.put(value, aClass);
        });
        // 初始化HandlerContext
        HandlerContext handlerContext = new HandlerContext(handlerMap);
        // 手動將HandlerContext注冊到spring容器中 beanFactory.registerSingleton(HandlerContext.class.getName(), handlerContext);
    }
}

 

2.4 修改IOrderService接口實現 

@Service
public class V2OrderServiceImpl implements IOrderService {

 @Autowired private HandlerContext handlerContext;

    @Transactional
    @Override
    public String handle(OrderDTO orderDTO) throws Exception {
        AbsHandler handler = handlerContext.getInstance(orderDTO.getType());
        return handler.handle(orderDTO);
    }
}

 

2.5 寫個controller測試一下

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import qinfeng.zheng.strategy.dto.OrderDTO;
import qinfeng.zheng.strategy.service.IOrderService;

@RestController
public class OrderController {
    /**
     * @Autowired自定根據 v2OrderServiceImpl 名稱 適配 V2OrderServiceImpl這個實現類
     */
    @Autowired
    private IOrderService v2OrderServiceImpl;
    
    @GetMapping("/handle")
    public String handler(OrderDTO orderDTO) throws Exception {
        return v2OrderServiceImpl.handle(orderDTO);
    }
}

 

好, 代碼寫完了,使用策略模式之后,代碼的擴展性確實增強,若訂單類型發生改變,只需添加AbsHandler的實現類即可,其它都不需要變。

但是,我覺得這代碼並不好呀, 這樣操作,實現策略模式的代碼太多了。 其二,我使用了 aClass.newInstance()反射方式創建一個具體的Handler對象,這兒並沒有使用spring的代理功能,所以如果在AbsHandler#handle方法上添加事務,是不會生效的,只能在V2OrderServiceImpl#handle方法添加事務。

不過這個示例也讓我真實體驗了一把 BeanFactoryPostProcessor 這個接口的功能, 以后如果有需要手動注冊bean的場景,可以考慮使用這個接口,但是這個接口的主要功能並不注冊bean, 而是修改管理bean工廠內所有的beandefinition(未實例化)數據,並且隨心所欲的修改屬性。 

 

 

此外,ClassScanner這個類使用hutool類庫。

以上示例代碼大多來自“java那些事兒”


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM