教你如何用策略模式,替換臃腫的 if-else 嵌套

本文收錄在我的博客: www.chengxy-nds.top,同進步

1、傳統的實現方式

先說一下具體的需求:公司推廣入口不少,每個下單來源在下單時都作特殊的邏輯處理,可能每兩天就會加一個來源。javascript

那麼按照傳統的實現方式代碼就是以下:java

public class OrderServiceImpl implements IOrderService {
    @Override
    public String handle(OrderDTO dto) {
        String type = dto.getType();
        if ("1".equals(type)) {
            return "處理普通訂單";
        } else if ("2".equals(type)) {
            return "處理團購訂單";
        } else if ("3".equals(type)) {
            return "處理促銷訂單";
        }
        return null;
    }
}

爲何非得寫的這麼臃腫?不少同事會說:「哎呀,沒辦法呀,業務催的緊,這樣開發效率快省事」。的確是句大實話,不少時候業務方確實像催命鬼同樣的讓你趕工期,想快速實現功能,這樣寫是最好的選擇。程序員

上邊的代碼看似還算清晰,可若是我告訴你公司訂單來源有上百種,你想象一下那種臃腫的if-else,去翻代碼時是什麼感覺?算法

2、策略模式的實現方式

策略模式是oop中最著名的設計模式之一,是對方法行爲的抽象,能夠歸類爲行爲設計模式,也是oopinterface經典的應用。其特色簡單又實用,是我最喜歡的模式之一。設計模式

策略模式定義了一個擁有共同行爲的算法族,每一個算法都被封裝起來,能夠互相替換,獨立於客戶端而變化。安全

很多人說:Java的設計模式背了不少,可平常還不就是寫if-else的業務,根本就不用到。其實不是用不到是沒有用到合適的位置!ide

一、策略模式的使用場景:
  • 針對同一問題的多種處理方式,僅僅是具體行爲有差異時;
  • 須要安全地封裝多種同一類型的操做時;
  • 同一抽象類有多個子類,而客戶端須要使用if-else 或者 switch-case 來選擇具體子類時。

這個是用策略模式修改後代碼:工具

@Component
@OrderHandlerType(16)
public class DispatchModeProcessor extends AbstractHandler{

    @Autowired
    private OrderStencilledService orderStencilledService;
    
    @Override
    public void handle(OrderBO orderBO) {
        
        /**
         * 訂單完結廣播通知(1 - 支付完成)
         */
        orderStencilledService.dispatchModeFanout(orderBO);
        
        /**
         *  SCMS 出庫單
         */
        orderStencilledService.createScmsDeliveryOrder(orderBO.getPayOrderInfoBO().getLocalOrderNo());
    }
}

每一個訂單來源都有本身單獨的邏輯實現類,而每次須要添加訂單來源,直接新建實現類,修改@OrderHandlerType(16)的數值便可,不再用去翻那幾百行的if-lese,一勞永逸!oop

二、具體的實現過程:

一、定義一個標識訂單來源的註解post

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface OrderHandlerType {
    int value() default 0;
}

二、抽象出來一個具體的業務處理器

public abstract class AbstractHandler {
    abstract public void handle(OrderBO orderBO);
}

三、項目啓動掃描 handler 入口

@Component
@SuppressWarnings({"unused","rawtypes"})
public class HandlerProcessor implements BeanFactoryPostProcessor {
    
    private String basePackage = "com.ecej.order.pipeline.processor";
    
    public static final Logger log = LoggerFactory.getLogger(HandlerProcessor.class);
    
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        
        Map<Integer,Class> map = new HashMap<Integer,Class>();
        
        ClassScaner.scan(basePackage, OrderHandlerType.class).forEach(x ->{
            int type = x.getAnnotation(OrderHandlerType.class).value();
            map.put(type,x);
        });
        
        beanFactory.registerSingleton(OrderHandlerType.class.getName(), map);
        
        log.info("處理器初始化{}", JSONObject.toJSONString(beanFactory.getBean(OrderHandlerType.class.getName())));
    }
}

四、掃描須要用到的工具類

public class ClassScaner {
    private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();

    private final List<TypeFilter> includeFilters = new ArrayList<TypeFilter>();

    private final List<TypeFilter> excludeFilters = new ArrayList<TypeFilter>();

    private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
    
    /**
     * 添加包含的Fiter
     * @param includeFilter
     */
    public void addIncludeFilter(TypeFilter includeFilter) {
        this.includeFilters.add(includeFilter);
    }

    /**
     * 添加排除的Fiter
     * @param includeFilter
     */
    public void addExcludeFilter(TypeFilter excludeFilter) {
        this.excludeFilters.add(excludeFilter);
    }
    
    /**
     * 掃描指定的包,獲取包下全部的Class
     * @param basePackage 包名
     * @param targetTypes 須要指定的目標類型,能夠是pojo,能夠是註解
     * @return Set<Class<?>>
     */
    public static Set<Class<?>> scan(String basePackage,
            Class<?>... targetTypes) {
        ClassScaner cs = new ClassScaner();
        for (Class<?> targetType : targetTypes){
            if(TypeUtils.isAssignable(Annotation.class, targetType)){
                cs.addIncludeFilter(new AnnotationTypeFilter((Class<? extends Annotation>) targetType));
            }else{
                cs.addIncludeFilter(new AssignableTypeFilter(targetType));
            }
        }
        return cs.doScan(basePackage);
    }
    
    /**
     * 掃描指定的包,獲取包下全部的Class
     * @param basePackages 包名,多個
     * @param targetTypes 須要指定的目標類型,能夠是pojo,能夠是註解
     * @return Set<Class<?>>
     */
    public static Set<Class<?>> scan(String[] basePackages,
            Class<?>... targetTypes) {
        ClassScaner cs = new ClassScaner();
        for (Class<?> targetType : targetTypes){
            if(TypeUtils.isAssignable(Annotation.class, targetType)){
                cs.addIncludeFilter(new AnnotationTypeFilter((Class<? extends Annotation>) targetType));
            }else{
                cs.addIncludeFilter(new AssignableTypeFilter(targetType));
            }
        }
        Set<Class<?>> classes = new HashSet<Class<?>>();
        for (String s : basePackages){
            classes.addAll(cs.doScan(s));
        }
        return classes;
    }
    
    /**
     * 掃描指定的包,獲取包下全部的Class
     * @param basePackages 包名
     * @return Set<Class<?>>
     */
    public Set<Class<?>> doScan(String [] basePackages) {
        Set<Class<?>> classes = new HashSet<Class<?>>();
        for (String basePackage :basePackages) {
            classes.addAll(doScan(basePackage));
        }
        return classes;
    }
    
    /**
     * 掃描指定的包,獲取包下全部的Class
     * @param basePackages 包名
     * @return Set<Class<?>>
     */
    public Set<Class<?>> doScan(String basePackage) {
        Set<Class<?>> classes = new HashSet<Class<?>>();
        try {
            String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
                    + ClassUtils.convertClassNameToResourcePath(
                            SystemPropertyUtils.resolvePlaceholders(basePackage))+"/**/*.class";
            Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
            for (int i = 0; i < resources.length; i++) {
                Resource resource = resources[i];
                if (resource.isReadable()) {
                    MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
                    if ((includeFilters.size() == 0 && excludeFilters.size() == 0)|| matches(metadataReader)) {
                        try {
                            classes.add(Class.forName(metadataReader.getClassMetadata().getClassName()));
                        } catch (ClassNotFoundException ignore) {}
                    }
                }
            }
        } catch (IOException ex) {
            throw new RuntimeException("I/O failure during classpath scanning", ex);
        }
        return classes;
    }
    
    /**
     * 處理 excludeFilters和includeFilters
     * @param metadataReader
     * @return boolean
     * @throws IOException
     */
    private boolean matches(MetadataReader metadataReader) throws IOException {
        for (TypeFilter tf : this.excludeFilters) {
            if (tf.match(metadataReader, this.metadataReaderFactory)) {
                return false;
            }
        }
        for (TypeFilter tf : this.includeFilters) {
            if (tf.match(metadataReader, this.metadataReaderFactory)) {
                return true;
            }
        }
        return false;
    }
}

五、 根據類型實例化抽象類

@Component
public class HandlerContext {

    @Autowired
    private ApplicationContext beanFactory;

    public  AbstractHandler getInstance(Integer type){
        
        Map<Integer,Class> map = (Map<Integer, Class>) beanFactory.getBean(OrderHandlerType.class.getName());
        
        return (AbstractHandler)beanFactory.getBean(map.get(type));
    }
    
}

六、調用入口,我這裏是接的MQ消息,會批量的處理多個訂單來源

@Component
@RabbitListener(queues = "OrderPipelineQueue")
public class PipelineSubscribe{
 
    private final Logger LOGGER = LoggerFactory.getLogger(PipelineSubscribe.class);
    
    @Autowired
    private HandlerContext HandlerContext;
    
    @Autowired
    private OrderValidateService orderValidateService;
    
    @RabbitHandler
    public void subscribeMessage(MessageBean bean){
        
        OrderBO orderBO = JSONObject.parseObject(bean.getOrderBO(), OrderBO.class);
        
        if(null != orderBO &&CollectionUtils.isNotEmpty(bean.getType()))
        {
            for(int value:bean.getType())
            {
                AbstractHandler handler = HandlerContext.getInstance(value);
                handler.handle(orderBO);
            }
        }
    }
}

接收實體 MessageBean 類代碼

public class MessageBean implements Serializable {
    private static final long serialVersionUID = 5454831432308782668L;
    private String cachKey;
    private List<Integer> type;
    private String orderBO;

    public MessageBean(List<Integer> type, String orderBO) {
        this.type = type;
        this.orderBO = orderBO;
    }
}

3、策略模式的優缺點

優勢
  • 易於擴展,增長一個新的策略只須要添加一個具體的策略類便可,基本不須要改變原有的代碼,符合開放封閉原則

    • 避免使用多重條件選擇語句,充分體現面向對象設計思想 策略類之間能夠自由切換,因爲策略類都實現同一個接口,因此使它們之間能夠自由切換

      • 每一個策略類使用一個策略類,符合單一職責原則 客戶端與策略算法解耦,二者都依賴於抽象策略接口,符合依賴反轉原則
    • 客戶端不須要知道都有哪些策略類,符合最小知識原則
缺點
  • 策略模式,當策略算法太多時,會形成不少的策略類
  • 客戶端不知道有哪些策略類,不能決定使用哪一個策略類,這點能夠經過封裝common公共包解決,也能夠考慮使IOC容器依賴注入的方式來解決。

如下是訂單來源策略類的一部分,不得不說策略類確實比較多。
在這裏插入圖片描述

總結:

凡事都有他的兩面性,if-else多層嵌套和也都有其各自的優缺點:

if-else的有點就是簡單,想快速迭代功能,邏輯嵌套少且不會持續增長,if-else更好些,缺點也是顯而易見,代碼臃腫繁瑣不便於維護。

策略模式 將各個場景的邏輯剝離出來維護,同一抽象類有多個子類,須要使用if-else 或者 switch-case 來選擇具體子類時,建議選策略模式,他的缺點就是會產生比較多的策略類文件。

兩種實現方式各有利弊,如何選擇仍是要依據具體業務場景,仍是那句話設計模式不是爲了用而用,必定要用在最合適的位置。


原創不易,燃燒秀髮輸出內容

習慣在VX看技術文章,想要獲取更多Java資源的同窗,能夠關注個人公衆號: 程序員內點事,暗號:[ 666]
相關文章
相關標籤/搜索