当前位置: 首页 > news >正文

【设计模式3_责任链、观察者】

责任链

有如下业务场景,需要对请求接口做一系列的校验,那么很容易写出以下伪代码,利用抛出异常拦截错误请求参数:

    public static void main(String[] args) {
        // ...
        try{
            checkSecurity();
            checkParams();
            checkRule();
        } catch (Exception e){
            // ...
        }
    }

    private static void checkSecurity(){
        // ...
        throw new RuntimeException();
    }

    private static void checkParams(){
        // ...
        throw new RuntimeException();
    }

    private static void checkRule(){
        // ...
        throw new RuntimeException();
    }

使用异常来校验,那么后续逻辑越来复杂的话:
如异常只能返回异常信息,不能返回更多字段,这时候需要自定义异常类。
异常的处理效率比条件判断的方式低很多。异常设计的初衷是解决程序运行中的各种意外情况,不应该用来做流程控制、条件控制。

这时候可以使用责任链模式

责任链模式为请求的接收创建了一个执行链,链上有多个节点,每个节点都有可能满足条件匹配然后处理请求事务。当某个节点处理完了,可以根据业务需求传递给下一个节点继续处理或返回处理完毕。

一,利用模版方法准备一个抽象类,作为所有责任节点的父类,在这个类里:

  1. 一个指向下一个责任节点属性
  2. 一个设置下一个对象的set方法
  3. 完成当前节点后链接到下一节点的逻辑
  4. 给子类提供差异化实现的抽象方法
public abstract class AbstractHandler {

    // 指向下一个责任节点属性
    private AbstractHandler nextHandler;

    // 设置下一个对象的set方法
    public void setNextHandler(AbstractHandler nextHandler) {
        this.nextHandler = nextHandler;
    }

    public AbstractHandler getNextHandler() {
        return this.nextHandler;
    }

    // 留给子类做差异化实现
    abstract boolean doFilter();

    // 对外提供的入口
    public boolean filter(){
        // 执行本节点的逻辑。如果校验通过则继续下节点,失败则直接返回
        boolean b = doFilter();
        if(b) {
	        // 指向下一节点,如果下节点为空则代表当前就是最后一个节点
	        if (this.nextHandler != null){
	            return this.nextHandler.filter();
	        }else {
	        	return false;
	        }
		} else {
			return false;
		}

    }
    
}

各实现类:

@Component
@Order(1)
public class CheckSecurityHandler extends AbstractHandler {
    @Override
    boolean doFilter() {

    }
}
@Component
@Order(1)
public class CheckRuleHandler extends AbstractHandler {
    @Override
    boolean doFilter() {

    }
}

一次性初始化所有的责任链对象(不是必须)

@Component
public class ChainOfResponseDemo {

    @Autowired
    private List<AbstractHandler> abstractHandlerList;


    private AbstractHandler abstractHandler;

    /* @PostConstruct注解的方法在项目启动的时候执行这个方法,
      也可以理解为在spring容器启动的时候执行,可作为一些数据的常规化加载,比如数据字典之类的。
    * */
    @PostConstruct
    public void initializeHandlerChain(){

        for (int i = 0; i < this.abstractHandlerList.size(); i++) {
            if ( i == 0){
                abstractHandler = abstractHandlerList.get(0);
            } else {
                AbstractHandler lastHandler = abstractHandlerList.get(i - 1);
                AbstractHandler nextHandler = abstractHandlerList.get(i);
                lastHandler.setNextHandler(nextHandler);
            }
        }
    }

    // 对外提供的入口
    public boolean execute(){
        return abstractHandler.filter();
    }


    public AbstractHandler getAbstractHandler() {
        return abstractHandler;
    }

    public void setAbstractHandler(AbstractHandler abstractHandler) {
        this.abstractHandler = abstractHandler;
    }
}

方法使用@PostConstruct 执行顺序:
其实从依赖注入的字面意思就可以知道,要将对象p注入到对象a,那么首先就必须得生成对象a和对象p,才能执行注入。
所以,如果一个类A中有个成员变量p被@Autowried注解,那么@Autowired注入是发生在A的构造方法执行完之后的。
如果想在生成对象时完成某些初始化操作,而偏偏这些初始化操作又依赖于依赖注入,那么久无法在构造函数中实现。
为此,可以使用@PostConstruct注解一个方法来完成初始化,@PostConstruct注解的方法将会在依赖注入完成后被自动调用。

        Constructor >> @Autowired >> @PostConstruct

当然也可以不用一次初始化所有责任链对象,而是在使用时,再将需要用到的责任对象创建、灵活组合排序。
参考:https://blog.csdn.net/cui_yonghua/article/details/93462893

观察者

当发生一个事件时,根据需求要触发多个不同操作,就可以用观察者模式实现类似 发布-订阅的模式。

用一个Subject类:

  1. 有一个成员变量 容器 List
  2. 有一个方法去通知容器里现存的所有观察者类
  3. 向容器中 添加/移除 观察者的方法

所有的观察者,可以统一继承一个抽象类,各自都要自己实现接收通知的方法体。
需要用到的哪些观察者,都可以在客户端自行向Subject中的容器加入(订阅),就可以被Subject通知到

Subject类:

public class Subject {

    private List<AbstractObserver> container = new ArrayList<>();

    public void notifyAllObserver(String...args) {
        for (AbstractObserver observer : container) {
            observer.observed(); // 对容器中现存的观察者逐一通知
        }
    }

    public void addObserver(AbstractObserver observer) {
        this.container.add(observer);
    }

    public void removeObserver(AbstractObserver observer) {
        this.container.remove(observer);
    }

}

观察者

public abstract class AbstractObserver {
    public abstract void observed();
}

public class TaskObserver1 extends AbstractObserver {
    @Override
    public void observed() {
        System.out.println("TaskObserver1...被通知到,我要开始睡觉了");
    }
}

public class TaskObserver2 extends AbstractObserver {
    @Override
    public void observed() {
        System.out.println("TaskObserver2...被通知到,我要开始学习了");
    }
}

客户端。使用演示

    public static void main(String[] args) {

        Subject subject = new Subject();
        TaskObserver1 taskObserver1 = new TaskObserver1();
        TaskObserver2 taskObserver2 = new TaskObserver2();

        subject.addObserver(taskObserver1);
        subject.addObserver(taskObserver2);

        subject.notifyAllObserver();

    }

扩展功能:
可以灵活的将 taskObserver1 添加或移除,以适应不同的业务场景。这一步还可以根据简单工厂,抽取成不同场景下需要通知不同观察者的固定实现。

notifyAllObserver 方法里也可以根据参数,选择性通知不同的observer。

observed() 方法也可以设置参数,如observed(AbstractObserver observer),可以实现,当一个observer收到消息时,再动态的对一个observe做添加或移除…等操作。

相关文章:

  • .NET MVC之AOP
  • 机器学习基础
  • 浅谈报表测试
  • 第十一:Fiddler抓包教程(11)-Fiddler设置安卓手机抓包,不会可是万万不行的!
  • ARMv8 MMU和translation stages、translation regimes和相关寄存器
  • Linux入门之配置以太网连接
  • 小时3.0报表某个型号数据比天数据多问题复盘
  • 深度学习 FairMOT多目标跟踪(PANDA)
  • 深度学习图像分割U-Net和FCN讲解
  • MySQL语句(二)
  • MySQL中的重做日志(redo log),回滚日志(undo log),以及二进制日志(binlog)的作用及生成时机
  • python-读写Excel(三)-xlwt格式设置
  • 主从复制Slave_IO_Running: NO Slave_SQL_Running: NO 解决办法
  • 14对象的方法
  • 神经网络原理与实例精解,神经网络案例讲解范文
  • 【node学习】协程
  • AzureCon上微软宣布了哪些容器相关的重磅消息
  • C# 免费离线人脸识别 2.0 Demo
  • Hexo+码云+git快速搭建免费的静态Blog
  • Linux快速配置 VIM 实现语法高亮 补全 缩进等功能
  • OpenStack安装流程(juno版)- 添加网络服务(neutron)- controller节点
  • php中curl和soap方式请求服务超时问题
  • SQLServer插入数据
  • Wamp集成环境 添加PHP的新版本
  • WebSocket使用
  • 初识 beanstalkd
  • 将回调地狱按在地上摩擦的Promise
  • 看域名解析域名安全对SEO的影响
  • 蓝海存储开关机注意事项总结
  • 如何学习JavaEE,项目又该如何做?
  • 我从编程教室毕业
  • 一起来学SpringBoot | 第三篇:SpringBoot日志配置
  • 再谈express与koa的对比
  • 走向全栈之MongoDB的使用
  • 微龛半导体获数千万Pre-A轮融资,投资方为国中创投 ...
  • ​软考-高级-系统架构设计师教程(清华第2版)【第1章-绪论-思维导图】​
  • #!/usr/bin/python与#!/usr/bin/env python的区别
  • #pragma multi_compile #pragma shader_feature
  • #周末课堂# 【Linux + JVM + Mysql高级性能优化班】(火热报名中~~~)
  • $emit传递多个参数_PPC和MIPS指令集下二进制代码中函数参数个数的识别方法
  • $L^p$ 调和函数恒为零
  • (2)Java 简介
  • (ibm)Java 语言的 XPath API
  • (二)springcloud实战之config配置中心
  • (附源码)小程序儿童艺术培训机构教育管理小程序 毕业设计 201740
  • (转)3D模板阴影原理
  • (转)GCC在C语言中内嵌汇编 asm __volatile__
  • (转)创业的注意事项
  • .net core 3.0 linux,.NET Core 3.0 的新增功能
  • .Net Web项目创建比较不错的参考文章
  • .NetCore 如何动态路由
  • .NET企业级应用架构设计系列之开场白
  • @ 代码随想录算法训练营第8周(C语言)|Day53(动态规划)
  • [ C++ ] STL---仿函数与priority_queue
  • [2016.7 day.5] T2