1 Map+函数式接口方法

1.1 场景

在工作中, 经常会遇到大量条件判断的情况, 因此一般使用策略模式来定义各种业务行为
但缺点也明显:

  • 如果 if-else的判断情况很多,那么对应的具体策略实现类也会很多,每种行为都要重新定义类来实现

  • 没法俯视整个分派的业务逻辑

1.2 Map+函数式接口方法使用

用上了Java8的新特性lambda表达式

  • 判断条件放在key中
  • 对应的业务逻辑放在value中

这样子写的好处是非常直观,能直接看到判断条件对应的业务逻辑

需求:根据资源类型resourceType和不同的入参规则, 来返回不同的业务编码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
  
@Service
public class QueryGrantTypeService {

@Autowired
private GrantTypeService grantTypeService;

private Map<String, Function<BusinessObject,String>> grantTypeMap=new HashMap<>();

/**
* 初始化业务分派逻辑,代替了if-else部分
* key: 业务类型
* value: lambda表达式,最终会获得该业务编码
*/
@PostConstruct
public void dispatcherInit(){
//业务1 -漏洞业务
grantTypeMap.put(RiskNotificationEnum.VULNERABILITY_NOTICE.getCode(),businessObject-> grantTypeService.getVulCode(businessObject));
//业务1 -安全业务
grantTypeMap.put(RiskNotificationEnum.SECURITY_EVENT_TROUBLESHOOTING.getCode(),businessObject-> grantTypeService.getSecCode(businessObject));
//业务1 -指令业务
grantTypeMap.put(Constants.ISSUE_COMMAND, businessObject-> grantTypeService.getZlCode(businessObject));

}

public String getResult(String resourceType, BusinessObject businessObject){
Function<BusinessObject,String> result=grantTypeMap.get(resourceType);
if(result!=null){
return result.apply(businessObject);
}
return null;
}
}


  1. BusinessObject 是个公用接口, 不同的业务类都可以继承BusinessObject, 不会对原本数据有任何影响, 在最终实现的方法中可以转换为原本的对象

    1
    2
    3
    4
    5
    6
    /**  
    * 定义所有业务对象都需要的公共方法或属性
    */public interface BusinessObject {


    }
  2. 具体方法实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    public String getSecCode(BusinessObject businessObject) {  
    //类型转化
    SecurityEventDO securityEventDO = (SecurityEventDO) businessObject;

    log.info("GrantTypeService-getSecCode:{}", JSON.toJSONString(securityEventDO));
    String timestamp = getTimeStr();

    String pre = getKey(NoticeSourceEnum.class, securityEventDO.getEventSource(), SystemManagerConstant.SEC);

    String key = RiskNotificationEnum.SECURITY_EVENT_TROUBLESHOOTING.getCode();
    String countStr = getValue(key);
    //攻击来源
    String attackSource = getCode(securityEventDO.getAttackSource(), AttackSourceEnum.class);
    //被攻击资产类型
    String assetType = getCode(securityEventDO.getAssetType(), AssetTypeEnum.class);
    //攻击类型(其他攻击类型为8)
    String attackType = getCode(securityEventDO.getAttackType(), AttackTypeEnum.class);
    if (securityEventDO.getAttackType().startsWith("其他")) {
    attackType = "8";
    }
    //告警来源
    String alertSource = getCode(securityEventDO.getAlertSource(), AlertSourceEnum.class);

    String secCode = pre + timestamp + attackSource + assetType + attackType + alertSource + "-" + countStr;
    log.info("安全事件编码为:{}", secCode);
    return secCode;
    }
  3. 直接使用controller层接口调用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    @RestController
    public class GrantTypeController {

        @Autowired
        private QueryGrantTypeService queryGrantTypeService;

        @PostMapping("/grantType")
        public String test(String resourceName){
            return queryGrantTypeService.getResult(resourceName);
        }
    }

1.3 总结

策略模式通过接口、实现类、逻辑分派来完成,把 if语句块的逻辑抽出来写成一个类,更好维护。

Map+函数式接口通过Map.get(key)来代替 if-else的业务分派,能够避免策略模式带来的类增多、难以俯视整个业务逻辑的问题。

2 责任链模式优化参数多重校验

2.1 场景

  • 之前在做需求,写一个方法,先在前面做验证,
  • if不满足A条件则return,
  • if不满足B条件则return…
  • 一共写了5个验证,等验证通过以后才执行下面的逻辑,
  • 过了一阵产品提了需求,跟这个方法类似,
  • 我又把这个方法copy了一份,只不过验证条件稍微有点不一样,变成6个验证了。

2.2 责任链模式

1)职责链模式(Chain of Responsibility Pattern),又叫==责任链模式==:为请求创建了一个接收者对象的链。
2)职责链模式通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推
3)这种类型的设计模式属于==行为型模式==

1
2
3
4
5
6
7
8
9
10
11
12
public static void main(String[] args) {  
Handler.Builder builder = new Handler.Builder();
//链式编程,谁在前谁在后看的清清楚楚
builder.addHandler(new ValidateHandler())
.addHandler(new LoginHandler())
.addHandler(new AuthHandler())
.addHandler(new BusinessLogicHandler());
User user = new User();
user.setUserName("woniu");
user.setPassWord("666");
builder.build().doHandler(user);
}

Handler抽象接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public abstract class Handler<T> {  

protected Handler next;

private void next(Handler next) {
this.next = next;
}

public abstract void doHandler(User user);

public static class Builder<T> {
private Handler<T> head;
private Handler<T> tail;

public Builder<T> addHandler(Handler handler) {
if (this.head == null) {
this.head = this.tail = handler;
return this; }
this.tail.next(handler);
this.tail = handler;
return this; }

public Handler<T> build() {
return this.head;
}
}
}

抽象实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class AuthHandler extends Handler {  
@Override
public void doHandler(User user) {
if (!"管理员".equals(user.getRoleName())) {
System.out.println("您不是管理员,没有操作权限");
return; }
if (null != next) {
next.doHandler(user);
}
}
}

public class BusinessLogicHandler extends Handler {

@Override
public void doHandler(User user) {
System.out.println("你好棒!");
}
}

public class LoginHandler extends Handler {

@Override
public void doHandler(User user) {
if (!"woniu".equals(user.getUserName()) || !"666".equals(user.getPassWord())) {
System.out.println("用户名密码不正确");
return; }
if (null != next) {
next.doHandler(user);
}
}
}

实体类

1
2
3
4
5
6
7
8
9
@Data  
public class User {

private String userName;
private String passWord;
private String roleName;


}

3 业务编排功能

3.1 场景

  • 写代码过程中经常使用到业务编排功能, 有时添加一些校验以及其他业务流程, 并不会影响其他业务流程的执行, 大量的业务代码会在一个方法中耦合, 每次修改都要重头去理解, 非常的麻烦, 这时候就需要用到业务编排
  • 业务编排可以直接使用 LiteFlow 来完成, 但是项目中只有少部分逻辑需要业务编排, 则没有必要引用
  • 因此可以使用责任链模式 + 工厂模式 + 枚举 来完成业务编排

3.2 责任链模式 + 工厂模式 + 枚举

3.2.1 在工厂方法中使用责任链封装业务对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

public class GatewayHandlerEnumFactory {

private static GatewayDao gatewayDao = new GatewayImpl();

// 提供静态方法,获取第一个handler
public static GatewayHandler getFirstGatewayHandler() {

GatewayEntity firstGatewayEntity = gatewayDao.getFirstGatewayEntity();
GatewayHandler firstGatewayHandler = newGatewayHandler(firstGatewayEntity);
if (firstGatewayHandler == null) {
return null;
}

GatewayEntity tempGatewayEntity = firstGatewayEntity;
Integer nextHandlerId = null;
GatewayHandler tempGatewayHandler = firstGatewayHandler;
// 迭代遍历所有handler,以及将它们链接起来
while ((nextHandlerId = tempGatewayEntity.getNextHandlerId()) != null) {
GatewayEntity gatewayEntity = gatewayDao.getGatewayEntity(nextHandlerId);
GatewayHandler gatewayHandler = newGatewayHandler(gatewayEntity);
tempGatewayHandler.setNext(gatewayHandler);
tempGatewayHandler = gatewayHandler;
tempGatewayEntity = gatewayEntity;
}
// 返回第一个handler
return firstGatewayHandler;
}

/**
* 反射实体化具体的处理者
*
* @param firstGatewayEntity
* @return
*/
private static GatewayHandler newGatewayHandler(GatewayEntity firstGatewayEntity) {
// 获取全限定类名
String className = firstGatewayEntity.getConference();
try {
// 根据全限定类名,加载并初始化该类,即会初始化该类的静态段
Class<?> clazz = Class.forName(className);
return (GatewayHandler) clazz.newInstance();
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}
return null;
}

}

3.2.2 获取枚举配置项接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/**  
* @className: GatewayDao
* @date: 2023/2/2
**/public interface GatewayDao {

/**
* 根据 handlerId 获取配置项
*
* @param handlerId
* @return
*/
GatewayEntity getGatewayEntity(Integer handlerId);

/**
* 获取第一个处理者
*
* @return
*/
GatewayEntity getFirstGatewayEntity();
}

//接口实现
public class GatewayImpl implements GatewayDao {

/**
* 初始化,将枚举中配置的handler初始化到map中,方便获取
*/
private static Map<Integer, GatewayEntity> gatewayEntityMap = new HashMap<>();

static {
GatewayEnum[] values = GatewayEnum.values();
for (GatewayEnum value : values) {
GatewayEntity gatewayEntity = value.getGatewayEntity();
gatewayEntityMap.put(gatewayEntity.getHandlerId(), gatewayEntity);
}
}

@Override
public GatewayEntity getGatewayEntity(Integer handlerId) {
return gatewayEntityMap.get(handlerId);
}

@Override
public GatewayEntity getFirstGatewayEntity() {
for (Map.Entry<Integer, GatewayEntity> entry : gatewayEntityMap.entrySet()) {
GatewayEntity value = entry.getValue();
// 没有上一个handler的就是第一个
if (value.getPreHandlerId() == null) {
return value;
}
}
return null;
}
}

3.2.3 实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Data  
@AllArgsConstructor
public class GatewayEntity {

private Integer handlerId;

private String name;

private String conference;


private Integer preHandlerId;

private Integer nextHandlerId;
}

3.2.4 枚举类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public enum GatewayEnum {  
// handlerId, 拦截者名称,全限定类名,preHandlerId,nextHandlerId
API_HANDLER(new GatewayEntity(1, "api接口限流", "com.woniu.zerenlianfactorydemo.afteryouhua.ApiLimitGatewayHandler", null, 2)),
BLACKLIST_HANDLER(new GatewayEntity(2, "黑名单拦截", "com.woniu.zerenlianfactorydemo.afteryouhua.BlacklistGatewayHandler", 1, 3)),
SESSION_HANDLER(new GatewayEntity(3, "用户会话拦截", "com.woniu.zerenlianfactorydemo.afteryouhua.SessionGatewayHandler", 2, 4)),
;
GatewayEntity gatewayEntity;

public GatewayEntity getGatewayEntity() {
return gatewayEntity;
}

GatewayEnum(GatewayEntity gatewayEntity) {
this.gatewayEntity = gatewayEntity;
}
}

3.2.5 责任链业务抽象类

1
2
3
4
5
6
7
8
9
10
11
12
13
public abstract class GatewayHandler {  

/**
* 下一关用当前抽象类来接收
*/
protected GatewayHandler next;

public void setNext(GatewayHandler next) {
this.next = next;
}

public abstract void service();
}

3.2.6 实现抽象类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class BlacklistGatewayHandler extends GatewayHandler {  

@Override
public void service() {
System.out.println("黑名单拦截");
if (this.next != null) {
this.next.service();
}
}
}

public class ApiLimitGatewayHandler extends GatewayHandler {

@Override
public void service() {
System.out.println("api接口限流");
if (this.next != null) {
this.next.service();
}
}
}

public class SessionGatewayHandler extends GatewayHandler {

@Override
public void service() {
System.out.println("用户会话拦截");
if (this.next != null) {
this.next.service();
}
}
}

3.2.7 调用执行

1
2
3
4
5
6
7
8
9
/**  
* 用责任链模式 + 工厂模式 + 枚举,动态配置请求链并调用。
*/
public class GetewayClient {
public static void main(String[] args) {
GatewayHandler firstGatewayHandler = GatewayHandlerEnumFactory.getFirstGatewayHandler();
firstGatewayHandler.service();
}
}

4 工厂模式+模板方法

4.1 场景

  • 不同的业务场景走的是同一套流程,但是具体实现不同, 这样的话就可以将相同的部分抽为抽象方法,分别实现不同的业务逻辑
  • 同时不同的对象可使用工厂模式进行维护。

4.2 工厂模式+模板方法

比如,我现在有一个业务是需要查询数据, 处理之后 组合为不同的Echars图形

4.2.1 工厂方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Component  
public class ChartServiceFactory {

private final Map<ChartType, AbstractChartService<EcharsOptionsRequest, ?, String>> serviceMap;

@Autowired
public ChartServiceFactory(List<AbstractChartService<EcharsOptionsRequest, ?, String>> services) {
serviceMap = services.stream()
.collect(Collectors.toMap(service -> {
//折线图
if (service instanceof LineChartService) return ChartType.LINE;
// 柱状图
if (service instanceof BarChartService) return ChartType.BAR;
//饼图
if (service instanceof PieChartService) return ChartType.PIE;
throw new IllegalArgumentException("Unknown service type: " + service.getClass());
}, service -> service));
}


public AbstractChartService<EcharsOptionsRequest, ?, String> getChartService(ChartType chartType) { // NOSONAR
return serviceMap.get(chartType);
}


}

4.2.2 业务抽象模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@Slf4j  
@Service
public abstract class AbstractChartService<T extends EcharsOptionsRequest, D, R> {


/**
* 模板方法
*/
public R execute(T request) {
List<D> data = queryData(request);

if (validateData(data)) {
log.info(getType().getDesc() + " 当前时间节点没有数据: {}", request);
return emptyChart(request);
}

return processData(request, data);
}

/**
* 查询数据
*/
protected abstract List<D> queryData(T request);

/**
* 校验数据
*/
protected abstract boolean validateData(List<D> data);

/**
* 处理数据
*/
protected abstract R processData(T request, List<D> data);



protected abstract R emptyChart(T request);


protected abstract ChartType getType();
}

4.2.3 接口调用

1
2
3
4
5
6
7
8
9
10
11
12
13

@Autowired
private ChartServiceFactory chartServiceFactory;

@Override
public String getECharsOptions(EcharsOptionsRequest request) {
ChartType chartType = ChartType.getByCode(request.getType());
if (chartType == null) {
return "不支持的图表类型";
}
AbstractChartService<EcharsOptionsRequest, ?, String> chartService = chartServiceFactory.getChartService(chartType);
return chartService.execute(request);
}