首页 > 编程语言 >Java策略模式实践

Java策略模式实践

时间:2024-04-12 16:33:16浏览次数:28  
标签:Java 策略 通知 route 实践 模式 RouteContext public String

1 什么是策略模式

策略模式(Strategy Pattern):一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。

在策略模式定义了一系列算法或策略,并将每个算法封装在独立的类中,使得它们可以互相替换。通过使用策略模式,可以在运行时根据需要选择不同的算法,而不需要修改客户端代码。

策略模式可以优雅的解决if-else所带来的复杂和难以维护的问题,并且拥有很好的可扩展性。

2 策略模式的构成

策略模式包含以下几个核心角色:

  • 环境上下文(Context):维护一个对策略对象的引用,负责将客户端请求委派给具体的策略对象执行。环境类可以通过依赖注入、简单工厂等方式来获取具体策略对象。
  • 抽象策略(Abstract Strategy):定义了策略对象的公共接口或抽象类,规定了具体策略类必须实现的方法。
  • 具体策略(Concrete Strategy):实现了抽象策略定义的接口或抽象类,包含了具体的算法实现。

策略模式通过将算法与使用算法的代码解耦,提供了一种动态选择不同算法的方法。客户端代码不需要知道具体的算法细节,而是通过调用环境类来使用所选择的策略。

举例:

定义活动的 Strategy 接口和实现了 Strategy 接口的实体策略类。Context 是一个使用了某种策略的类。

StrategyPatternDemo,我们的演示类使用 Context 和策略对象来演示 Context 在它所配置或使用的策略改变时的行为变化。

image-20240410181831812

3 策略模式实践

在我司的通知服务中,涉及到不同渠道的通知服务,例如:钉钉,短信,邮箱,微信等,所以定义抽象通知渠道,具体的通知渠道类继承并丰富其细节:

public abstract class AxxNotifyRoute implements NotifyRoute {

    /**
     * 通知渠道,指具体的提供通知服务的公司,例如使用了阿里云的短信网关,则notifyChannel则为aliyun
     */
    String notifyChannel;

    /**
     * 通知途径,指具体的通知载体
     * 短信:SMS
     * 电话:PHONE
     * 钉钉:DINGDING
     * 邮箱:EMAIL
     * 微信:WECHAT
     * 站内信:INNER_MESSAGE
     * APP:APP
     */
    String routeType;

    /**
     * 通知地址
     */
    String routeURI;
}

首先看Context上下文环境,我们需要一个RouteContext来决定路由到哪种通知渠道、加载路由策略以及执行路由方法:

public class RouteContext {

    private RouteStrategy routeStrategy;

    public RouteContext(RouteStrategy routeStrategy) {
        this.routeStrategy = routeStrategy;
    }

    public RouteContext() {
    }

    /**
     * 加载路由策略,通过spring容器获取
     *
     * @param route
     * @return
     */
    public void loadRouteStrategy(NotifyRoute route) {
        doLoad(route);
    }

    private void doLoad(NotifyRoute route) {
        if (route == null) return;
        for (RouteStrategyEnum value : RouteStrategyEnum.values()) {
            if (route.getRouteType().equals(value.getTypeName())) {
                routeStrategy = (RouteStrategy) SpringContext.getBean(value.getObjName());
                return;
            }
        }
    }

    /**
     * 加载 RouteStrategy 通过反射的方式
     *
     * @param route
     */
    @SneakyThrows
    private void doLoadFromClass(NotifyRoute route) {
        if (route == null) return;
        for (RouteStrategyEnum value : RouteStrategyEnum.values()) {
            if (route.getRouteType().equals(value.getTypeName())) {
                Class<?> clazz = Class.forName(value.getClazzName());
                routeStrategy = (RouteStrategy) clazz.getDeclaredConstructor().newInstance();
                return;
            }
        }
    }

    /**
     * 渠道路由和通知记录落库
     * @param instance
     * @return
     */
    public NotificationRouteResult route(NotificationInstance instance) {
        LocalDateTime now = LocalDateTimeUtil.now();
        NotificationRouteResult routeResult = routeStrategy.route(instance);
        new Thread(() -> NotificationRecordHandler.recordNotification(instance, routeResult, now)).start();
        return routeResult;
    }
}

其中SpringContext是一个获取上下文以及ioc容器内Bean的工具容器:

image-20240410185508323

枚举类RouteStrategyEnum记录了通知渠道的BeanName以及全限定类名:

image-20240410190224519

因为我们提供了两种获取策略对象的方式:1是通过SpringContext获取ioc容器中注册的策略;2是通过枚举类中的全限定名反射得到策略对象。如下所示:

image-20240410185138732

image-20240410185945143

再来看策略类公共接口:

public interface RouteStrategy {
    NotificationRouteResult route(NotificationInstance instance);
}

返回结果是通知结果状态以及原因:

@Data
public class NotificationRouteResult {

    private String notifyStatus;

    private String cause;

    public NotificationRouteResult(String notifyStatus) {
        this.notifyStatus = notifyStatus;
    }

    public NotificationRouteResult() {
    }
    public NotificationRouteResult(String notifyStatus, String cause) {
        this.notifyStatus = notifyStatus;
        this.cause = cause;
    }
}

当前仅接入了短信通知和邮件通知服务,策略类如下:

image-20240411101354468

image-20240411101534764

到此为止所有前置类都准备就绪,重点看下RouteContext#route方法,逻辑是执行上下文环境中的策略对象的route方法,开新线程对通知记录落库。

image-20240411102255658

入参instance记录了一个通知实例的详细信息:

image-20240411102224522

最后看下RouteContext是怎么应用的:首先new RouteContext()对象,加载通知请求中的路由信息,此处通知方法都是最小粒度单位,传参是单个通知实例。至于批量通知和多途径通知,由任务调度服务实现。

image-20240411103322069

这里RouteContext的构建和加载策略类的方式还不够优雅,后续可以改为建造者模式。


本博客内容仅供个人学习使用,禁止用于商业用途。转载需注明出处并链接至原文。

标签:Java,策略,通知,route,实践,模式,RouteContext,public,String
From: https://www.cnblogs.com/zhaobo1997/p/18131604

相关文章

  • Amazon Bedrock 实践系列 | Claude 3 深度探秘
    生成式AI和大模型在2024年已经进入落地实践阶段。因此,围绕开发者在生成式应用程序开发中的主要痛点和需求,我们组织了这个“AmazonBedrock实践”的系列,希望可以帮助开发者高效地上手生成式AI和大模型的应用开发。本篇为第二篇(特别提示:本篇探讨的AnthropicClaude3模型......
  • 李彦宏:闭源才有真正商业模式才能聚集算力和人才;史上首位数学和计算机最高奖双得主丨RT
       开发者朋友们大家好: 这里是「RTE开发者日报」,每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享RTE(RealTimeEngagement)领域内「有话题的新闻」、「有态度的观点」、「有意思的数据」、「有思考的文章」、「有看点的会议」,但内容仅代表编......
  • java + geotools 读写shp时,中文属性乱码问题
    创建时要注意 :  newDataStore.setCharset(Charset.forName("GBK"));代码如下:FilenewFile=newFile(filepath);Map<String,Serializable>params=newHashMap<>();params.put("url",newFile.toURI().toURL());params.put("createspati......
  • Java stream sorted使用 Comparator 进行多字段排序
    摘要:介绍使用JavaStream流排序器Comparator对List集合进行多字段排序的方法,包括复杂实体对象多字段升降序混合排序方法。综述​ Java8的Stream使用了函数式编程模式,人如其名,它可以被用来对集合或数组进行链状流式的排序、过滤和统计等操作,从而让我们更方便的对集合或数组......
  • C++观察者模式的实现
    C++观察者模式的实现观察者模式介绍观察者模式是软件设计模式里面一种很常用又很重要的一种设计模式,观察者模式又叫做发布-订阅(Publish/Subscribe)模式。也就是主题对象(subject)发布通知,订阅该主题的多个观察者(observer)可以收到通知从而更新自己。主题对象Subject发出通知时并不......
  • Java 中文官方教程 2022 版(四十一)
    原文:docs.oracle.com/javase/tutorial/reallybigindex.html错误处理原文:docs.oracle.com/javase/tutorial/jaxp/limits/error.html建议应用程序在设置新属性时捕获org.xml.sax.SAXNotRecognizedException异常,以便应用程序在不支持这些属性的旧版本上正常工作。例如,可下载的......
  • Java 中文官方教程 2022 版(四十二)
    原文:docs.oracle.com/javase/tutorial/reallybigindex.html设置策略文件以授予所需的权限。原文:docs.oracle.com/javase/tutorial/security/toolsign/rstep3.html接下来,您将使用策略工具创建一个名为exampleraypolicy的策略文件,并在其中授予来自已签名JAR文件的代码权限......
  • Java 中文官方教程 2022 版(四十四)
    原文:docs.oracle.com/javase/tutorial/reallybigindex.html调用方法原文:docs.oracle.com/javase/tutorial/reflect/member/methodInvocation.html反射提供了一种在类上调用方法的方式。通常,只有在非反射代码中无法将类的实例强制转换为所需类型时才需要这样做。方法是使用j......
  • Java 中文官方教程 2022 版(四十五)
    原文:docs.oracle.com/javase/tutorial/reallybigindex.html教程:自定义网络原文:docs.oracle.com/javase/tutorial/networking/index.htmlJava平台备受推崇,部分原因是其适用于编写使用和与互联网资源以及万维网进行交互的程序。事实上,兼容Java的浏览器极大地利用了Java......
  • Java 中文官方教程 2022 版(四十六)
    原文:docs.oracle.com/javase/tutorial/reallybigindex.html定义简单的通用类型原文:docs.oracle.com/javase/tutorial/extra/generics/simple.html这里是包java.util中接口List和Iterator的定义的一个小节选:publicinterfaceList<E>{voidadd(Ex);Iterator<E......