问题:
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 "";
}
}