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

23种设计模式-责任链模式(Android开发实际应用场景介绍)

什么是责任链模式

责任链模式是一种行为型设计模式,它的核心思想是将请求从一系列处理者中传递,直到其中一个处理者能够处理它为止。在这个过程中,请求可以被任何一个处理者处理,也可以被拒绝,直到有一个处理者能够处理请求。

在Java开发中,责任链模式的实现方式通常是通过一个抽象处理者(Handler)类来定义处理者的接口,并在这个类中定义一个指向下一个处理者的引用,形成一个链式结构。具体处理者(Concrete Handler)则继承自抽象处理者,并实现其处理请求的方法。当一个请求被发起时,它会首先被传递到第一个处理者,如果第一个处理者不能处理它,那么请求会被传递到下一个处理者,直到有一个处理者能够处理它为止。

责任链模式代码解释

下面我们以一个简单的例子来说明责任链模式的使用方法。假设我们有一个请假系统,需要根据不同的请假天数来分别由部门经理、人事主管和总经理进行审核。如果请假天数不超过2天,由部门经理审核;如果请假天数不超过5天,由人事主管审核;如果请假天数超过5天,则需要总经理审核。

首先我们定义一个抽象处理者类Handler:

public abstract class Handler {protected Handler successor;public void setSuccessor(Handler successor) {this.successor = successor;}public abstract void handleRequest(int days);
}

在这个类中,我们定义了一个指向下一个处理者的引用successor,并提供了一个设置下一个处理者的方法setSuccessor。同时,我们还定义了一个抽象方法handleRequest,该方法用于处理请求。具体的处理者将继承这个类,并实现自己的handleRequest方法。

下面我们来定义具体的处理者类。首先是部门经理处理者:

public class DepartmentManager extends Handler {@Overridepublic void handleRequest(int days) {if (days <= 2) {System.out.println("部门经理审批通过");} else {if (successor != null) {successor.handleRequest(days);}}}
}

在这个类中,我们实现了父类中定义的handleRequest方法,如果请假天数不超过2天,则部门经理能够处理该请求并输出相应的处理结果;否则,请求将被传递给下一个处理者,即人事主管处理者。

接下来是人事主管处理者:

public class HRManager extends Handler {@Overridepublic void handleRequest(int days) {if (days <= 5) {System.out.println("人事主管审批通过");} else {if (successor != null) {successor.handleRequest(days);}}}
}

这个类的实现与部门经理处理者类类似,如果请假天数不超过5天,则人事主管能够处理该请求并输出相应的处理结果;否则,请求将被传递给下一个处理者,即总经理处理者。

最后是总经理处理者:

public class GeneralManager extends Handler {@Overridepublic void handleRequest(int days) {System.out.println("总经理审批通过");}
}

这个类的实现非常简单,因为总经理是责任链中的最后一个处理者,无论请求的请假天数是多少,总经理都能够处理该请求。

最后我们来测试一下这个责任链模式的实现:

public class Test {public static void main(String[] args) {Handler departmentManager = new DepartmentManager();Handler hrManager = new HRManager();Handler generalManager = new GeneralManager();departmentManager.setSuccessor(hrManager);hrManager.setSuccessor(generalManager);departmentManager.handleRequest(1);departmentManager.handleRequest(3);departmentManager.handleRequest(7);}
}

在这个测试程序中,我们首先创建了部门经理、人事主管和总经理三个处理者,然后通过setSuccessor方法将它们组合成一个责任链。最后我们分别测试了请求请假1天、请假3天和请假7天的情况。运行结果如下:

部门经理审批通过
人事主管审批通过
总经理审批通过

可以看到,在请求请假1天、请假3天和请假7天的情况下,责任链分别被部门经理、人事主管和总经理处理,并输出了相应的处理结果。

Android开发中的实际应用解析

在安卓中,责任链模式也有广泛的应用。例如,Android中的事件分发机制就是一种典型的责任链模式。当用户触摸屏幕时,事件首先会被传递给Activity,在Activity中处理完后,事件会继续被传递给它的子View,由子View来处理。如果子View不能处理该事件,事件会被传递给父View,直到事件被处理为止。这个过程形成了一个责任链,而每个View都是责任链中的一个处理者。下面我们来看一下Android源码中的事件分发机制实现。

Android中的事件分发机制是从ViewGroup类开始的。ViewGroup类继承自View类,是一种特殊的View,可以包含其他View。在ViewGroup类中,有一个onInterceptTouchEvent方法,用于拦截子View的触摸事件。如果一个ViewGroup在onInterceptTouchEvent方法中返回了true,则表示它要拦截子View的触摸事件,并将事件交给自己的onTouchEvent方法来处理。如果返回了false,则表示它不拦截子View的触摸事件,将事件传递给子View来处理。

下面是ViewGroup类中onInterceptTouchEvent方法的源码:

public boolean onInterceptTouchEvent(MotionEvent ev) {if (ev.isFromSource(InputDevice.SOURCE_MOUSE)&& ev.getActionMasked() == MotionEvent.ACTION_DOWN&& ev.isButtonPressed(MotionEvent.BUTTON_SECONDARY)) {// The right mouse button is currently pressed.// We want to give the user a chance to reconsider before// performing an action that cannot be undone, such as dismissing// a notification or deleting a message.showContextMenu();return true;}if (mOnInterceptTouchListener != null && mOnInterceptTouchListener.onTouch(this, ev)) {return true;}if (ev.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {final int action = ev.getAction();final float x = ev.getX();final float y = ev.getY();if (action == MotionEvent.ACTION_DOWN) {mLastTouchDownIndex = ev.getActionIndex();mLastTouchDownTime = ev.getDownTime();mLastTouchDownX = x;mLastTouchDownY = y;mIgnoreNextUpEvent = false;} else {// Short circuit if we've already canceled, or if we have no// children.final boolean canceled = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;if (!canceled && mChildrenCount != 0) {// If this is a pointer event, propagate it to our children.if (action == MotionEvent.ACTION_POINTER_DOWN|| action == MotionEvent.ACTION_MOVE) {final int index = ev.getActionIndex();final int id = ev.getPointerId(index);if (id != mScrollPointerId) {final float dx = x - mLastTouchDownX;final float dy = y - mLastTouchDownY;final float xDiff = Math.abs(dx);final float yDiff = Math.abs(dy);final int touchSlop = mTouchSlop;final boolean isGeneratedGesture = ev.isGeneratedGesture();boolean interceptHorizontal = false;boolean interceptVertical = false;if ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) == 0) {if ((mNestedScrollAxes & SCROLL_AXIS_HORIZONTAL) != 0) {interceptHorizontal |= (xDiff > touchSlop);}if ((mNestedScrollAxes & SCROLL_AXIS_VERTICAL) != 0) {interceptVertical |= (yDiff > touchSlop);}}if (isGeneratedGesture|| interceptHorizontal|| interceptVertical|| hasPointersOutOfView()) {mLastTouchMotionY = y;mLastTouchMotionX = x;cancelAndClearTouchTargets(ev);return true;}}} else if (action == MotionEvent.ACTION_POINTER_UP) {final int index = ev.getActionIndex();final int id = ev.getPointerId(index);if (id == mScrollPointerId) {// This was our active pointer going up. Choose a new// action pointer and adjust accordingly.final int newIndex = ev.getPointerCount() - 1;mLastTouchDownX = ev.getX(newIndex);mLastTouchDownY = ev.getY(newIndex);mLastTouchDownIndex = ev.findPointerIndex(newIndex);if (mVelocityTracker != null) {mVelocityTracker.clear();}}}}}}return false;
}

可以看到,在onInterceptTouchEvent方法中,首先判断了是否有设置OnInterceptTouchListener,如果有则交由OnInterceptTouchListener来处理触摸事件。

接着,如果触摸事件是鼠标右键的按下事件,则弹出上下文菜单并返回true,拦截触摸事件,防止后续的触摸事件继续传递下去。

如果以上两种情况都不满足,则判断触摸事件是否来自于指针,如果是,则根据具体的触摸事件类型进行处理。例如,如果是按下事件,则记录下按下的坐标和时间等信息;如果是移动事件,则计算移动距离,并根据一定的条件来决定是否拦截事件。

如果没有任何一个子View对该事件进行了处理,则返回false,表示不拦截该事件,让其继续传递给子View进行处理。

以上就是在安卓中使用责任链模式的一个例子,即事件分发机制。在这个机制中,每个子View都是一个责任链节点,它们依次处理触摸事件,并根据具体的情况来决定是否将事件交给下一个节点处理。

总结

责任链模式的优点在于,它能够将一个大型的、复杂的任务分解成多个小的、简单的任务,每个任务都由一个节点来处理,从而降低了系统的复杂性和耦合度。此外,责任链模式还具有灵活性,可以根据具体的需求来动态地调整节点的顺序和个数,从而满足不同的业务场景。

相关文章:

23种设计模式-责任链模式(Android开发实际应用场景介绍)

什么是责任链模式 责任链模式是一种行为型设计模式&#xff0c;它的核心思想是将请求从一系列处理者中传递&#xff0c;直到其中一个处理者能够处理它为止。在这个过程中&#xff0c;请求可以被任何一个处理者处理&#xff0c;也可以被拒绝&#xff0c;直到有一个处理者能够处…...

Socket+Select+Epoll笔记

讲到epoll&#xff0c;就必须了解Socket&#xff0c;上篇博客写了Socket的基本使用方法&#xff0c;步骤主要为创建一个socketsocket是进程之间通信的&#xff0c;那么进程通信如何找到这个socket呢&#xff1f;当然是端口号&#xff0c;所以socket就要和端口号进行绑定&#x…...

git查看最近修改的文件

git log --name-status 每次修改的文件列表, 显示状态 git log --name-only 每次修改的文件列表 git log --stat 每次修改的文件列表, 及文件修改的统计 git whatchanged 每次修改的文件列表 git whatchanged --stat 每次修改的文件列表, 及文件修改的统计 git show 显示最…...

【算法基础(四)】堆排序(二)

堆排序&#xff08;二&#xff09; 把数组从零开始连续的一段 完全二叉树 size i 左 son 2*11 i 右 son 2*12 父 (i-1) / 2 堆是完全二叉树&#xff0c;分为大根堆和小根堆 在完全二叉树里&#xff0c;每一棵子数最大的值是头节点的值&#xff0c;就是大根堆 同理&…...

C++类型转换

C语言的转换是在变量前加类型名进行转换的&#xff0c;比如double pi 3.14;int a (int) pi;对于指针也是如此double* dptr &pi;int* iptr (int*)dptr;虽然c兼容了C语言的转型方式&#xff0c;但是也做了很多限制&#xff0c;比如向上类型转换&#xff0c;在c中建议使用…...

Keil MDK6要来了,将嵌入式软件开发水平带到新高度,支持跨平台(2023-03-11)

注&#xff1a;这个是MDK6&#xff0c;不是MDK5 AC6&#xff0c;属于下一代MDK视频版&#xff1a; https://www.bilibili.com/video/BV16s4y157WF Keil MDK6要来了&#xff0c;将嵌入式软件开发水平带到新高度&#xff0c;支持跨平台一年一度的全球顶级嵌入式会展Embedded Wor…...

蓝桥杯刷题第九天

题目描述本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将所填结果输出即可。素数就是不能再进行等分的整数。比如7&#xff0c;11。而 9 不是素数&#xff0c;因为它可以平分为 3 等份。一般认为最小的素数是2&#xff0c;接着是 3&#xff0c;5&…...

a-tree-select 基本使用,下拉框高度和宽度设置、回显时滚动条定位解决。

目录一、基本使用1. 界面效果2. 代码实现3. 问题1&#xff1a;下拉框占满整个屏幕4. 问题4&#xff1a;菜单内容过长时&#xff0c;下拉菜单宽度无限变宽。二、数据回显、滚动条定位1. 界面效果2. 代码实现2.1 获取默认展开节点2.1.1 代码实现2.1.2 说明2.2 设置滚动条定位2.2.…...

【Linux】之nc命令(连接与扫描指定端口、监测服务端口的使用情况)解析、详解实例、邮件告警

&#x1f341;博主简介 &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01; 文章目录nc命令简介nc命令的安装nc命令语法格式…...

cdn简单配置

cdn配置域名接入CDN编辑CDN配置本地修改hosts文件&#xff0c;绕过公网解析域名接入CDN 添加CDN域名以及回源配置 编辑CDN配置 默认后端端口是80&#xff0c;如果测试发现无法访问&#xff0c;则可能是443或其它 如果域名在CDN后端有https强制跳转&#xff0c;后端端口一定是44…...

前端安全(自留)

目录XSS——跨站脚本常见解决CSRF ——跨站请求伪造常见解决XSS——跨站脚本 当目标站点在渲染html的过程中&#xff0c;遇到陌生的脚本指令执行。 攻击者通过在网站注入恶意脚本&#xff0c;使之在用户的浏览器上运行&#xff0c;从而盗取用户的信息如 cookie 等。 常见 解…...

零基础转行云计算可行吗

目前处于云年代&#xff0c;云计算运维工程师的工作远景还是十分广泛的。像是阿里云计算&#xff0c;滴滴&#xff0c;抖音等等互联网大厂目前都在使用云核算技能。 云计算运维工程师的薪资水平也十分可观。 运维工程师(Operations)&#xff0c;在国内又称为运维开发工程师(Dev…...

【AcWing】蓝桥杯备赛-深度优先搜索-dfs(1)

目录 写在前面&#xff1a; 题目&#xff1a;92. 递归实现指数型枚举 - AcWing题库 读题&#xff1a; 输入格式&#xff1a; 输出格式&#xff1a; 数据范围&#xff1a; 输入样例&#xff1a; 输出样例&#xff1a; 解题思路&#xff1a; 代码&#xff1a; AC &…...

孩子免费就读|私企经理自费赴美国东海岸高校访学

私企U经理无文章无课题&#xff0c;出国访学除了为考察市场、拓宽人脉、提升履资外&#xff0c;另一个主要目的是带孩子在美国接受当地免费的公立中小学教育&#xff0c;并把访学目标学校定位在东海岸。最终其采纳了板凳费相对较低的佐治亚大学邀请函&#xff0c;签证时居然全家…...

前端面试hr经常会问的问题

文章目录前言1.自我介绍2.为什么你要离职&#xff1f;3.工作经历4.职业规划5.优点、缺点6.还有什么要问的总结前言 这里记录了一些面试中hr或者项目负责人经常会问的一些问题&#xff0c;可以提前参考参考&#xff0c;想想该怎么回答&#xff0c;为之后的面试做好准备&#xf…...

C动态数组

在实际项目中&#xff0c;我们经常与各式各样的数据打交道。 例如&#xff1a;我们处理的是学生的数据。 struct student {int id; // 学号char name[20]; // 姓名int gender; // 性别int mark; // 成绩 };学生数据使用一个结构体表示&#xff0c;该结构体拥有4个成员。分别为…...

【STL一】STL组件(容器、迭代器、算法)

【STL一】STL组件&#xff08;容器、迭代器、算法&#xff09;一、STL二、STL组件&#xff08;component&#xff09;1、stl六大组件2、C STL的13个头文件3、stl所有头文件三、容器&#xff08;container)1、序列容器&#xff08;Sequence container)——顺序容器2、关联容器&a…...

Java每日一练(20230312)

目录 1. 两数之和 II ★ 2. 反转链表 ★★ 3. 二叉树的层序遍历 II ★★★ &#x1f31f; 每日一练刷题专栏 C/C 每日一练 ​专栏 Python 每日一练 专栏 Java 每日一练 专栏 1. 两数之和 II 给定一个已按照 非递减顺序排列 的整数数组 numbers &#xff0c;请你从数…...

Linux中sudo,su与su -命令的区别

前言 su命令就是切换用户的工具&#xff0c;怎么理解呢&#xff1f;比如我们以普通用户tom登录的&#xff0c;但要添加用户任务&#xff0c;执行useradd &#xff0c;tom用户没有这个权限&#xff0c;而这个权限恰恰由root所拥有。解决办法无法有两个&#xff0c;一是退出tom用…...

归并排序有多简单?一幅图教你看懂【C语言】

目录 归并排序的递归实现 代码实现 归并排序的非递归实现 代码实现 归并排序的思想很简单——分治法。简单地说&#xff0c;归并排序的是将序列拆分成几段子序列&#xff0c;将每一段子序列分别进行排序&#xff0c;排好之后再将有序的子序列归并&#xff08;有点像合并两…...

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…...

Chapter03-Authentication vulnerabilities

文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

【WiFi帧结构】

文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成&#xff1a;MAC头部frame bodyFCS&#xff0c;其中MAC是固定格式的&#xff0c;frame body是可变长度。 MAC头部有frame control&#xff0c;duration&#xff0c;address1&#xff0c;address2&#xff0c;addre…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

day52 ResNet18 CBAM

在深度学习的旅程中&#xff0c;我们不断探索如何提升模型的性能。今天&#xff0c;我将分享我在 ResNet18 模型中插入 CBAM&#xff08;Convolutional Block Attention Module&#xff09;模块&#xff0c;并采用分阶段微调策略的实践过程。通过这个过程&#xff0c;我不仅提升…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版​分享

平时用 iPhone 的时候&#xff0c;难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵&#xff0c;或者买了二手 iPhone 却被原来的 iCloud 账号锁住&#xff0c;这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

【C语言练习】080. 使用C语言实现简单的数据库操作

080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

Docker 本地安装 mysql 数据库

Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker &#xff1b;并安装。 基础操作不再赘述。 打开 macOS 终端&#xff0c;开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...