策略模式在工作中的运用

前言

在不同的场景下,执行不同的业务逻辑,在日常工作中是很寻常的事情。比如,订阅系统。在收到阿里云的回调事件、与收到AWS的回调事件,无论是收到的参数,还是执行的逻辑都可能是不同的。为了避免,每次新增一种场景,就要改变原有的代码结构,比如,改变原有逻辑,添加 if-else结构,一种可行的方案是,使用策略模式。

策略模式

贴个链接

简单来说,就是根据请求方的类型,执行特定的业务逻辑。

问题点

网络上大部分实现策略模式的代码,很多在将具体的策略注入到Map中时,是以硬编码 的方式实现的,比如掘金上的这篇文章:如何优雅的将设计模式运用到实际项目中去?

在工作中借鉴,使用截图

这种方案,虽然可以实现策略模式,但是每次新增策略都要修改这个集合。期望的方案是,新增的策略,自动注入到map中,而不必手动添加。

为了解决新增策略时,要修改map的情况,调研之后,发现有两种方案:

  1. 美团文章推介的 基于单例的方式,每次启动时,自动将策略注入到map中。
  2. 掘金上另一种方案:基于注解+反射的方式,动态将策略加载到map中。

相比,美团的方案更加优秀,代码改动少,且性能高。不过,本次以方案2为例说明。

代码实现

业务场景

需要提供给外部云厂商回调API,当云服务发生告警时,调用此API,将告警事情同步到服务使用方。

考虑点:

  1. 因为每家厂商的回调参数各有不同,此时,需要定义一个回调对象基类,每个云厂商对应的回调对象继承这个基类,并实现添加其特定的属性。
  2. 因为API是放开到公网上的,因此,为了避免被攻击,除了从集团域名出去外,还对IP进行了频控,比如,调用次数100次/秒。

代码结构

具体代码

定义实体类

基类

java 复制代码
import lombok.Data;

/**
 * @author wangbin16
 * @date 2024/1/18 15:33
 */
@Data
public class CloudAlertBase {
    /**
     * name = "alertType", value = "告警类型"
     */
    private Integer alertType;

    /**
     * 业务
     */
    private Integer business;
}

阿里云

java 复制代码
import lombok.Data;

/**
 * 阿里云监控告警
 * @author wangbin16
 * @date 2024/1/18 15:35
 */
@Data
public class AliyunCloudAlertAo extends CloudAlertBase {
    private static final long serialVersionUID = 1L;

    /**
     * name = "alertName", value = "报警名称"
     */
    private String alertName;

    /**
     * name = "alertState", value = "报警状态"
     */
    private String alertState;

    /**
     * name = "curValue", value = "报警发生或恢复时监控项的当前值"
     */
    private String curValue;

    /**
     * name = "dimensions", value = "发生报警的对象"
     */
    private String dimensions;

    /**
     * name = "expression", value = "报警规则的表达式"
     */
    private String expression;

策略模式

策略基类

java 复制代码
/**
 * @author wangbin16
 * @date 2024/1/18 15:41
 */
public interface CloudAlertStrategy {
    /**
     * 处理云监控告警
     * @param param
     */
    void handleCloudAlert(CloudAlertBase param);
}

阿里云策略

java 复制代码
@Slf4j
@Service
@CloudAlertAnnotation(alertType = CloudAlertTypeEnum.ALIYUN)
public class AliyunCloudAlertStrategy implements CloudAlertStrategy {
    @Override
    public void handleCloudAlert(CloudAlertBase param) {
        // TODO 处理阿里云监控告警

        // 暂时先发送给我 @wangbin16
        SendMsgUtils sendMsgUtils = new SendMsgUtils();
        sendMsgUtils.sendP2pPopoMsg(JSON.toJSONString(param), "wangbin16@xxx", "【阿里云监控告警】");
    }
}
策略注册
java 复制代码
import org.reflections.Reflections;

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

/**
 * @author wangbin16
 * @date 2024/1/18 15:50
 */
public class AnnotationCloudAlertStrategyFactory {
    /**
     * 存储策略
     */
    static Map<Integer, CloudAlertStrategy> strategyMap = new HashMap<>();

    static {
        registerStrategy();
    }

    /**
     * 自动注册策略
     */
    private static void registerStrategy() {
        // 通过反射获取所有的策略类
        Reflections reflections = new Reflections(CloudAlertStrategy.class.getPackage().getName());
        Set<Class<? extends CloudAlertStrategy>> cloudStrategyClassSet = reflections.getSubTypesOf(CloudAlertStrategy.class);
        if (cloudStrategyClassSet != null) {
            for (Class<?> clazz : cloudStrategyClassSet) {
                // 找到类型注解,自动完成策略注册
                if (clazz.isAnnotationPresent(CloudAlertAnnotation.class)) {
                    CloudAlertAnnotation alertTypeAnnotation = clazz.getAnnotation(CloudAlertAnnotation.class);
                    CloudAlertTypeEnum chargeType = alertTypeAnnotation.alertType();
                    try {
                        strategyMap.put(chargeType.getCode(), (CloudAlertStrategy) clazz.newInstance());
                    } catch (InstantiationException | IllegalAccessException e) {
                        e.getStackTrace();
                    }
                }
            }
        }
    }

    /**
     * 提供注册策略接口,外部只需要调用此接口接口新增策略
     * 策略定义时,即注入,这是口子
     */
    public static void registerChargeStrategy(CloudAlertTypeEnum alertType, CloudAlertStrategy strategy) {
        strategyMap.put(alertType.getCode(), strategy);
    }
}

策略选择器

java 复制代码
@Service
@Slf4j
public class CloudAlertStrategySelector {
    public CloudAlertStrategy selector(Integer alertType) {
        if (alertType == null) {
            return null;
        }
        return AnnotationCloudAlertStrategyFactory.strategyMap.get(alertType);
    }
}

服务调用

java 复制代码
@Slf4j
@Service("cloudMonitorService")
public class CloudMonitorService {
    @Autowired
    private CloudAlertStrategySelector cloudAlertStrategySelector;
    public void handleCloudMonitor(CloudAlertBase param) {
        logger.info("handleCloudMonitor param:{}", JSON.toJSONString(param));
        CloudAlertStrategy selector = cloudAlertStrategySelector.selector(param.getAlertType());
        if (selector != null) {
            selector.handleCloudAlert(param);
        } else {
            logger.error("外部云告警异常调用, param:{}", JSON.toJSONString(param));
        }
    }
}

测试

调用指定的接口,执行指定的逻辑

相关推荐
YY&DS8 分钟前
Qt Designer 自定义控件已提升后,如何修改提升类
开发语言·qt
右耳朵猫AI18 分钟前
Rust技术周刊 2026年第19周
开发语言·后端·rust
Leweslyh28 分钟前
基于 Confucius 架构的无人集群网络控制原语解析
开发语言·网络·php
月落归舟41 分钟前
Java线程小记
java·开发语言
摇滚侠1 小时前
01 基础语法 JavaScript 入门到精通全套教程
开发语言·javascript·ecmascript
sleven fung1 小时前
Milvus 向量数据库
开发语言·数据库·python·langchain·milvus
大大杰哥1 小时前
Java 日志框架详解:SLF4J + Logback 从入门到实战
java·开发语言·logback
ylscode1 小时前
黑客利用 GHOSTYNETWORKS 和 OMEGATECH 托管 JS 恶意软件基础设施
开发语言·安全·php·安全威胁分析
爱吃生蚝的于勒1 小时前
QT开发第二章——信号和槽
c语言·开发语言·c++·qt
xcLeigh2 小时前
Python入门:Python3 operator模块全面学习教程
开发语言·python·学习·教程·python3·operator