问题:
    java本来是一门以面向对象为主的语言,但很多人嘴上说着java面向对象,然后除了表映射实体之外,其他的还是面向过程的思路。
    就比如今天要说的,代码中大段大段的if-else判断,每个if-else代码块中又有很多复杂的逻辑,导致整个代码混乱不堪,让别人看到就感觉看到屎一样的代码一样。

   那么,如何优雅的替代这些代码呢,其实有个设计模式(策略模式)就很好的解决了这个问题。
情景举例:
比如分楼层的客流统计,现有场景总共有4楼,每个楼层的出入口都有客流统计摄像头,对应楼层有客流进出时处理不同的业务逻辑。
floor=1的时候,就执行1楼客流统计的逻辑;
floor=2的时候,就执行2楼客流统计的逻辑;
floor=3的时候,就执行3楼客流统计的逻辑;
等等,
然后有些人就会开始if-else了,比如有如下的伪代码:

if(floor == 1){
    1楼客流统计(此处省略100多行处理逻辑)
}else if(floor == 2){
   2楼客流统计(此处省略100多行处理逻辑)
}else if(floor == 3){
   3楼客流统计(此处省略100多行处理逻辑)
}else if(type=n){
    ...(此处省略几百上千行的逻辑)
}

用策略模式代替if-else:
首先,本次例子用的是Spring-Boot框架。

1、定义一个客流统计类,里面有floor属性,floor可以是"1"、“2”、“3”…

2、定义一个接口PassengerFlowHandler,里面有个抽象方法handle,入参是客流统计类

3、定义一个注解HandlerType,有个value属性,value是几就代表这个注解注的这个类是什么哪个楼层的客流统计

定义普通类 PassengerFlowHandlerOne,继承PassengerFlowHandler,代表1楼客流统计,即@HandlerType(“1”);

定义普通类 PassengerFlowHandlerTwo,继承PassengerFlowHandler,代表2楼客流统计,即@HandlerType(“2”);

定义普通类 PassengerFlowHandlerThree,继承PassengerFlowHandler,代表3楼客流统计,即@HandlerType(“3”);

定义一个初始化类HandlerProcessor,实现BeanFactoryPostProcessor,过程如下:
1、找到带有注解@HandlerType的类,
2、以注解的值为key,对应的类为value,存在一个map中
3、将这个map作为构造函数的参数,初始化HandlerContext,将HandlerContext注册到spring中成为一个单例bean。
很明显,目的就是为了保存不同floor对应的不同类。
定义类HandlerContext,有个map类型的属性叫handlerMap,有个getInstance的方法,入参是floor,返回PassengerFlowHandler。
最后使用的时候,是先调用handlerContext.getInstance方法,根据type获取对应的PassengerFlowHandler。
然后再调用他的handle方法,执行对应客流统计的处理逻辑。

具体代码如下:
1、客流统计类

package com.ahies.stm.app.bigdata.passengerFlow.entity;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.ahies.stm.app.base.BaseEntity;
import com.baomidou.mybatisplus.extension.activerecord.Model;

import java.io.Serializable;

import lombok.*;
import lombok.experimental.Accessors;

/**
 * <p>
 * 客流统计
 * </p>
 *
 * @author zhu
 * @since 2019-11-14
 */
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@TableName("t_bigdata_passenger_flow")
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class PassengerFlow extends BaseEntity<PassengerFlow> {

    private static final long serialVersionUID = 1L;

    /**
     * 进馆人数
     */
    private Integer inCnt;

    /**
     * 出馆人数
     */
    private Integer outCnt;

    /**
     * 剩余人数
     */
    private Integer surplusCnt;

    /**
     * 时间段
     */
    private String timeSlot;
    /**
     * 上报时间
     */
    @TableField(exist = false)
    private String reportTime;
    /*
    *上报日期
    */
    private String reportDate;

    // 楼层 1 2 3 4 all
    private String floor;


}

 

2、接口

package com.ahies.stm.app.hikvision.handler;

import com.ahies.stm.app.bigdata.passengerFlow.entity.PassengerFlow;

public interface PassengerFlowHandler {
    boolean handle(PassengerFlow passengerFlow);
}

3、实现类

package com.ahies.stm.app.hikvision.handler;

import com.ahies.stm.app.bigdata.passengerFlow.entity.PassengerFlow;
import com.ahies.stm.app.hikvision.annotation.HandlerType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
@HandlerType("1")
public class PassengerFlowHandlerOne implements PassengerFlowHandler {
    @Override
    public boolean handle(PassengerFlow passengerFlow) {
        System.out.println(passengerFlow.getFloor());
        return true;
    }
}
package com.ahies.stm.app.hikvision.handler;

import com.ahies.stm.app.bigdata.passengerFlow.entity.PassengerFlow;
import com.ahies.stm.app.hikvision.annotation.HandlerType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
@HandlerType("2")
public class PassengerFlowHandlerTwo implements PassengerFlowHandler {
    @Override
    public boolean handle(PassengerFlow passengerFlow) {
        System.out.println(passengerFlow.getFloor());
        return true;
    }
}

 

4、注解

package com.ahies.stm.app.hikvision.annotation;

import java.lang.annotation.*;

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

5、HandlerContext

package com.ahies.stm.app.hikvision.model;

import com.ahies.stm.app.hikvision.handler.PassengerFlowHandler;
import com.ahies.stm.app.util.SpringContextUtil;

import java.util.Map;

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

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

    public PassengerFlowHandler getInstance(String type){
        Class clazz = handlerMap.get(type);
        if(clazz == null){
            throw new IllegalArgumentException("没有type对应的处理器,type:"+type);
        }
        return (PassengerFlowHandler)SpringContextUtil.getBean(clazz);
    }
}

 

6、HandlerProcessor 

package com.ahies.stm.app.hikvision.config;

import cn.hutool.core.lang.ClassScaner;
import com.ahies.stm.app.hikvision.annotation.HandlerType;
import com.ahies.stm.app.hikvision.model.HandlerContext;
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 java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.Map;

@Component
@SuppressWarnings("unchecked")
public class HandlerProcessor implements BeanFactoryPostProcessor {
    //这里是具体的handler策略类的包的位置,为了后面的包扫描
    private static final String HANDLER_PACKAGE = "com.ahies.stm.app.hikvision.handler";
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        Map<String, Class> handlerMap = new HashMap<>();
        //包扫描
        ClassScaner.scanPackageByAnnotation(HANDLER_PACKAGE,HandlerType.class).forEach(clazz ->{
            Annotation annotation = clazz.getAnnotation(HandlerType.class);
            HandlerType handlerType = (HandlerType) annotation;
            String type = handlerType.value();
            handlerMap.put(type,clazz);
        });
        HandlerContext handlerContext = new HandlerContext(handlerMap);
        //注册单例
        beanFactory.registerSingleton(HandlerContext.class.getName(),handlerContext);
    }
}

 

7、SpringContextUtil  工具类
 

package com.ahies.stm.app.util;

/**
 * @Description
 * @Date 2019/10/18 11:35
 * @Author zsj
 */
import org.springframework.context.ApplicationContext;

/**
 * Spring获取上下文或者Bean工具类
 * @since 2018-04-09
 * @author WuZhiWei
 */

public class SpringContextUtil {

    private static ApplicationContext applicationContext;

    //获取上下文
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    //设置上下文
    public static void setApplicationContext(ApplicationContext applicationContext) {
        SpringContextUtil.applicationContext = applicationContext;
    }

    //通过名字获取上下文中的bean
    public static Object getBean(String name){
        return applicationContext.getBean(name);
    }

    //通过类型获取上下文中的bean
    public static Object getBean(Class<?> requiredType){
        return applicationContext.getBean(requiredType);
    }
}

 

8、使用

package com.ahies.stm.app.hikvision.controller;

import com.ahies.stm.app.bigdata.passengerFlow.entity.PassengerFlow;
import com.ahies.stm.app.hikvision.handler.PassengerFlowHandler;
import com.ahies.stm.app.hikvision.model.HandlerContext;
import com.ahies.stm.app.hikvision.sdk.MonitorClockHandler;
import com.ahies.stm.app.monitor.node.entity.Node;
import com.ahies.stm.app.hikvision.model.Camera;
import com.ahies.stm.app.hikvision.model.HcClinetProperties;
import com.ahies.stm.app.monitor.node.service.NodeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

/**
 * @Description TODO
 * @Date 2019/11/19 19:48
 * @Author zsj
 */
@RestController
@RequestMapping("/node")
public class NodeController {
    // 此处必须注入 monitorClockHandlers 否则 MonitorClockHandlerWindows start不运行
    @Autowired
    List<MonitorClockHandler> monitorClockHandlers;
    @Autowired
    NodeService nodeService;
    @Value("${hikvision.sdk.path}")
    private String sdkPath;
    @Autowired
    HandlerContext handlerContext;

   
    @RequestMapping("/test")
    public String test(String i) {
        PassengerFlow passengerFlow = new PassengerFlow();
        passengerFlow.setFloor(i);
        PassengerFlowHandler passengerFlowHandler = handlerContext.getInstance(passengerFlow.getFloor());
        passengerFlowHandler.handle(passengerFlow);
       return "";
    }




}