【设计模式深度剖析】【3】【行为型】【职责链模式】| 以购物中心客户服务流程为例加深理解
👈️上一篇:命令模式 | 下一篇:策略模式👉️
设计模式-专栏👈️
文章目录
- 职责链模式
- 定义
- 英文原话
- 直译
- 如何理解呢?
- 职责链模式的角色
- 1. Handler(抽象处理者)
- 2. ConcreteHandler(具体处理者)
- 3. Client(客户类)
- 类图
- 类图分析
- 代码示例
- 职责链模式的应用
- 优点
- 缺点
- 使用场景
- 示例解析:购物中心客户服务流程
- 类图
- 类图分析
- 代码示例
职责链模式
职责链模式(Chain of Responsibility Pattern)是一种常见的行为模式。
职责链模式是一种将多个对象链接起来以处理相同请求的设计模式,就像一条流水线或接力棒传递,每个对象都有机会处理请求,如果不能处理则传递给下一个对象,直到找到能够处理的对象或传递完毕。
这种模式降低了对象间的耦合度,增强了系统的可扩展性和灵活性,使得请求的处理过程更加清晰和模块化。
简而言之,职责链模式==让请求在多个对象间“接力传递”,直到找到“合适的人”==来处理。
定义
英文原话
Chain of Responsibility Pattern: Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.
直译
职责链模式:通过将多个对象连接成一条链,并沿着这条链传递请求,以避免请求的发送者与接收者之间的紧密耦合。直到链上的某个对象处理请求为止。
如何理解呢?
想象一下在图书馆借阅图书的流程。在一个大型的图书馆系统中,处理图书借阅的流程可能涉及到多个步骤和角色,每个角色都有自己特定的职责。
- 读者:读者想要借阅一本书。他们首先会到自助借书机或前台服务台提出借阅请求。
- 自助借书机:这是第一个“处理者”。如果自助借书机正常工作且读者持有有效的借阅证,它可以直接处理借阅请求,打印出借阅凭条,并更新图书的借阅状态。
- 前台服务台:如果自助借书机出现故障或读者遇到问题,他们可以将借阅请求传递给前台服务台的工作人员。工作人员会检查读者的借阅证是否有效,并手动处理借阅请求。
- 图书管理员:如果前台服务台的工作人员发现图书已经被其他读者预约或存在其他需要管理员处理的问题(例如,图书需要修复或更新库存),他们会将借阅请求传递给图书管理员。图书管理员会进一步处理这些特殊情况,并决定是否批准借阅请求。
- 系统管理员:在某些情况下,例如系统出现严重故障或需要更改借阅规则时,图书管理员可能无法直接处理请求。这时,他们可以将请求传递给系统管理员,由系统管理员来处理这些系统级别的问题。
在这个场景中,每个角色(自助借书机、前台服务台、图书管理员、系统管理员)都是一个“处理者”,他们共同组成了一个“职责链”。当一个借阅请求出现时,它会沿着这个链传递,直到有一个“处理者”能够处理它为止。
这种流程设计降低了各个角色之间的依赖和耦合,使得借阅流程更加灵活和高效。同时,它也提高了系统的可扩展性,因为新的处理者可以很容易地添加到链中,以处理新的请求或应对新的情况。
在软件系统中,当有多个对象可以处理同一类请求时,使用职责链模式可以避免请求发送者和接收者之间的紧密耦合,使得系统更加灵活和可扩展。
职责链模式的角色
职责链模式中的角色通常包括以下几种:
1. Handler(抽象处理者)
定义一个处理请求的接口
(可选) 实现后继链
定义一个处理请求的接口,通常包含一个方法用于处理请求和一个属性用于保存对下一个处理者的引用。
2. ConcreteHandler(具体处理者)
- 处理它所负责的请求
- 可访问它的后继者
- 如果可处理该请求,就处理;否则将该请求转发给它的后继者
实现抽象处理者接口,处理它所负责的请求;如果可以处理该请求就处理,否则将该请求传给它的后继者。
3. Client(客户类)
设置职责链
向链上的具体处理者(ConcreteHandler)对象提交请求
创建处理链,并向链的第一个处理者对象发送请求。
类图
类图分析
抽象处理者组合了自身类型的对象(定义了 successor
后继处理器属性并通过setSuccssor()
方法进行赋值),体现在子类上就是每个具体的处理者可以设置后继处理器,即当前无法处理的话,递给后继处理器进行处理。
代码示例
下面是一个简单的Java示例,演示了职责链模式的应用:
抽象处理者
abstract class Handler {protected Handler successor; // 持有后继者的引用 // 设置后继者 public void setSuccessor(Handler successor) {this.successor = successor;}// 处理请求的方法(声明为抽象方法,由具体处理者实现) public abstract void handleRequest(int request);
}
具体处理者A
class ConcreteHandlerA extends Handler {@Overridepublic void handleRequest(int request) {if (request >= 0 && request < 10) {System.out.println("Handler A handled request " + request);} else if (successor != null) {successor.handleRequest(request); // 如果不能处理,则传递给后继者 }}
}
具体处理者B
class ConcreteHandlerB extends Handler {@Overridepublic void handleRequest(int request) {if (request >= 10 && request < 20) {System.out.println("Handler B handled request " + request);} else if (successor != null) {successor.handleRequest(request); // 如果不能处理,则传递给后继者 }}
}
具体处理者C
class ConcreteHandlerC extends Handler {@Overridepublic void handleRequest(int request) {if (request >= 20) {System.out.println("Handler C handled request " + request);} else {// 这里没有后继者,请求到此为止 System.out.println("No handler could process the request " + request);}}
}
客户类
package com.polaris.designpattern.list3.behavioral.pattern03.chainofresponsibility.classicdemo;public class Client {public static void main(String[] args) {Handler handlerA = new ConcreteHandlerA();Handler handlerB = new ConcreteHandlerB();Handler handlerC = new ConcreteHandlerC();// 设置职责链 handlerA.setSuccessor(handlerB);handlerB.setSuccessor(handlerC);// 发送请求 int[] requests = {2, 15, 30};for (int request : requests) {handlerA.handleRequest(request);}}
}
/* Output:
Handler A handled request 2
Handler B handled request 15
Handler C handled request 30
*///~
在上面的示例中,我们定义了三个具体处理者(A、B、C),它们分别处理不同范围的请求。在客户类中,我们创建了一个处理链,并将请求发送到链的第一个处理者(A)。如果A不能处理请求,它会将请求传递给B,依此类推。
最终,所有的请求都会被处理或者因为找不到合适的处理者而结束。
职责链模式的应用
优点
- 降低耦合度:请求者和接收者之间不直接联系,降低了系统的耦合度。
- 增强系统的可扩展性:可以根据需要增加新的请求处理类,满足开闭原则。
- 增强给对象指派职责的灵活性:当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
- 责任链简化了对象之间的连接:每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
缺点
- 不能保证每个请求一定被处理:由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理;一个请求也可能因为链的结构没有得到正确构建而得不到处理(比如忘记给处理者设置后继者)。
- 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
- 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。
使用场景
- 有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。
- 在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
- 可动态指定一组对象处理请求,客户端可以动态地设置职责链来处理请求,也可以改变链内的成员或者调动它们的次序。
示例解析:购物中心客户服务流程
为了更直观地解释职责链模式,我们可以使用一个简单的例子:购物中心的客户服务流程。当顾客在购物中心遇到问题(如退换货、咨询商品信息等)时,他们可能会首先找到最近的店员寻求帮助。如果店员不能解决问题,他们可能会将问题转交给部门经理,如果部门经理也不能处理,问题最终可能会提交给客服中心。
生活中的例子:
- 顾客:在购物中心遇到问题的顾客。
- 店员:首先接待顾客的人,可以处理一些简单的请求,如商品信息查询。
- 部门经理:如果店员不能解决问题,顾客的问题会转交给部门经理,部门经理能处理更复杂的请求,如退换货。
- 客服中心:如果部门经理也无法解决问题,问题最终会提交给客服中心。
类图
类图分析
抽象出抽象处理者 CustomerServiceRequestHandler
客户服务请求处理器,
店员 Clerk
, 部门经理 DepartmentManager
, 客服中心 CustomerServiceCenter
分别对其进行实现,是请求的具体处理者,
从类图也可发现,店员Clerk
对象组合了一个请求处理器对象,即后继处理节点,在这个示例是部门经理DepartmentManager
对象,表示店员如果可以处理的客户问题,店员就进行处理,否则交给他的后继节点部门经理处理;
如果请求被流转到部门经理DepartmentManager
对象来处理,如果他能处理,则自行处理返回了,否则将客户的问题递给后继节点处理,这里是客服中心CustomerServiceCenter
对象,从类图可以看出部门经理对象组合了一个客户服务请求处理器,具体是客服中心对象。
在本示例中,任何店员,部门经理处理不了的问题,客服中心都负责处理,他是最后的问题处理节点,因此它不再组合后继节点。
代码示例
首先,我们定义一个处理请求的接口
public interface CustomerServiceRequestHandler {String handleRequest(String request);void setNextHandler(CustomerServiceRequestHandler nextHandler);
}
然后,我们创建实现该接口的类来表示不同的处理者:
1.店员 Clerk
public class Clerk implements CustomerServiceRequestHandler {private CustomerServiceRequestHandler nextHandler;@Overridepublic String handleRequest(String request) {if (canHandleRequest(request)) {return "Clerk handled request: " + request;} else if (nextHandler != null) {return nextHandler.handleRequest(request);} else {return "No handler could process the request: " + request;}}@Overridepublic void setNextHandler(CustomerServiceRequestHandler nextHandler) {this.nextHandler = nextHandler;}private boolean canHandleRequest(String request) {// 假设店员只能处理商品信息查询请求return request.startsWith("Product Info");}
}
2.部门经理 DepartmentManager
public class DepartmentManager implements CustomerServiceRequestHandler {private CustomerServiceRequestHandler nextHandler;@Overridepublic String handleRequest(String request) {if (canHandleRequest(request)) {return "Department Manager handled request: " + request;} else if (nextHandler != null) {return nextHandler.handleRequest(request);} else {return "No handler could process the request: " + request;}}@Overridepublic void setNextHandler(CustomerServiceRequestHandler nextHandler) {this.nextHandler = nextHandler;}private boolean canHandleRequest(String request) {// 假设部门经理可以处理退货请求return request.startsWith("Return an item");}
}
3.客服中心 CustomerServiceCenter
public class CustomerServiceCenter implements CustomerServiceRequestHandler {@Overridepublic String handleRequest(String request) {return "Customer Service Center handled request: " + request;}@Overridepublic void setNextHandler(CustomerServiceRequestHandler nextHandler) {// 客服中心是链的末尾,不需要设置下一个处理者// 这里可以抛出一个异常或者忽略这个调用}
}
最后,我们可以创建一个客户端类来演示如何使用这个职责链
public class CustomerServiceChainDemo {public static void main(String[] args) {// 创建处理者实例CustomerServiceRequestHandler clerk = new Clerk();CustomerServiceRequestHandler manager = new DepartmentManager();CustomerServiceRequestHandler center = new CustomerServiceCenter();// 构建职责链clerk.setNextHandler(manager);manager.setNextHandler(center);// 模拟顾客提交请求//查询商品信息String request1 = "Product Info 123";String result1 = clerk.handleRequest(request1);System.out.println(result1); // 输出:Clerk handled request: Product Info 123//退货String request2 = "Return an item";String result2 = clerk.handleRequest(request2);System.out.println(result2); // 输出:Department Manager handled request: Return an item//服务投诉String request3 = "Complaint about service";String result3 = clerk.handleRequest(request3);System.out.println(result3); // 输出:Customer Service Center handled request: Complaint about service}
}
/* Output:
Clerk handled request: Product Info 123
Department Manager handled request: Return an item
Customer Service Center handled request: Complaint about service
*///~
在这个例子中,我们模拟了三种不同类型的顾客请求,并展示了它们如何通过职责链被不同的处理者处理。如果一个处理者不能处理请求,它会将请求传递给链中的下一个处理者,直到找到能够处理该请求的处理者,或者到达链的末尾。
👈️上一篇:命令模式 | 下一篇:策略模式👉️
设计模式-专栏👈️
相关文章:
【设计模式深度剖析】【3】【行为型】【职责链模式】| 以购物中心客户服务流程为例加深理解
👈️上一篇:命令模式 | 下一篇:策略模式👉️ 设计模式-专栏👈️ 文章目录 职责链模式定义英文原话直译如何理解呢? 职责链模式的角色1. Handler(抽象处理者)2. ConcreteHandler(具体处理者…...
评价GPT-4的方案
评价GPT-4的方案 引言: 随着人工智能技术的不断发展,自然语言处理领域取得了显著的突破。其中,GPT-4作为最新的大型语言模型之一,备受关注。本方案旨在对GPT-4进行全面评价,包括其技术特点、性能表现、应用场景以及潜在的影响等方面。 一、技术特点 1. 模型规模和参数数…...
LeetCode | 1624.两个相同字符之间的最长子字符串
这道题拿到手想法就是去双重遍历暴力解,对于每个字符,从后往前遍历字符串,找到从后往前一直到本次遍历的这个字符串这段子串中和这个字符串相同的字符位置,然后得到子字符串的长度,和ans存储的值做一个比较,…...
【CS.AI】GPT-4o:重新定义人工智能的新标杆
文章目录 1 序言2 GPT-4o的技术亮点3 GPT-4o与前代版本的对比3.1 热门AI模型对比表格GPT-3.5GPT-4GPT-4oBERTT5 3.2 其他 4 个人体验与感受5 结论 1 序言 嘿,大家好!今天要聊聊一个超级酷的AI新突破——GPT-4o!最近,OpenAI发布了…...
野火FPGA跟练(四)——串口RS232、亚稳态
目录 简介接口与引脚通信协议亚稳态RS232接收模块模块框图时序波形RTL 代码易错点Testbench 代码仿真 RS232发送模块模块框图时序波形RTL 代码Testbench 代码仿真 简介 UART:Universal Asynchronous Receiver/Transmitter,异步串行通信接口。发送数据时…...
Qt for Android 申请摄像头权限
步骤 1. 添加用户权限 方式1: AndroidManifest.xml 中新增(不添加后面申请选项时不弹窗) 或者再Qt Creator中直接添加 方式2: .pro 中引用multimedia 模块,编译时配置自动添加 <uses-permission android:name"android.permissi…...
kivy 百词斩项目 报错
AttributeError: FigureCanvasKivyAgg object has no attribute resize_event AttributeError: FigureCanvasKivyAgg object has no attribute resize_event 是一种常见的Python错误,当你试图访问一个对象(在这个例子中是 FigureCanvasKivyAgg 对象&am…...
ChatTTS 文字生成语言本地模型部署
ChatTTS部署 官方信息 [ChatTTS首页](https://chattts.com/)搭建步骤 1、下载源码 git clone https://github.com/2noise/ChatTTS.git 2、按照环境 pip install torch ChatTTS pip install -r requirements.txt 3、下载模型 git clone https://www.modelscope.cn/pzc163/ch…...
多曝光融合算法(三)cv2.createAlignMTB()多曝光图像融合的像素匹配问题
文章目录 1.cv2.createAlignMTB() 主要是计算2张图像的位移,假设位移移动不大2.多曝光图像的aline算法:median thresold bitmap原理讲解3.图像拼接算法stitch4.多曝光融合工具箱 1.cv2.createAlignMTB() 主要是计算2张图像的位移,假设位移移动…...
C/C++|类型推导中的模式匹配
在C11及以上的相关语法中,特别是在模版元编程的范式里,类型推导是了重中之重。 在 《Effective Modern C 》 中第一章主要就是讲各种类型推导。 当然了,谈到类型推导,我们不得不先搞懂类型推导中的模式匹配,这是基础&a…...
The 18th Northeast Collegiate Programming Contest(5/9/13)
心得 赛中ac:5,目前ac:9,题目总数:13 中档可做题还是很多的,可惜遇到了难绷的queueforces, 最后15min才判出来,oi赛制5wa4遗憾离场,赛后把几个题都给调过了࿰…...
Vue前端在线预览文件插件
Vue前端在线预览文件插件 一、使用场景 1.1.像文档资料等,只想让他人在线预览,但不能下载。此等场景需求可以用到此插件。 二、此文档介绍两种插件 1.view.xdocin插件 (上线后免费几天,然后收费,添加作者后,可以延…...
【ai】Audio2Face
Audio2Face 简介 Audio2Face是英伟达Omniverse平台的一部分,它使用先进的AI技术来生成基于音频输入的逼真面部动画。这个技术主要利用深度学习模型来解析人声,进而驱动一个三维模型的面部表情。下面是Audio2Face工作流程的详细说明: 预备阶段 在使用Audio2Face之前,需要准…...
2024.6.9 一
装饰器(Decorators) 装饰器是 Python 的一个重要部分。简单地说:他们是修改其他函数的功能的函数。他们有助于让我们的代码更简短,我们在用到装饰器时,常用到一个的符号,这个叫做语法糖,在函数定义前加上decorator_name, 那么后面的函数执行…...
地图之战争迷雾/地图算法/自动导航(一)
战争迷雾 TiledMap 创建黑色覆盖块,然后使用碰撞组件,控制黑色块的显示和隐藏 地图算法 在有些游戏中,地图需要随机生成,比如游戏中的迷宫等,这就需要地图生成的算法;在角色扮演类游戏中,角色…...
【wiki知识库】06.文档管理页面的添加--前端Vue部分
📝个人主页:哈__ 期待您的关注 目录 一、🔥今日目标 二、🐻前端Vue模块的改造 BUG修改 1.wangeditor无法展示问题 2.弹窗无法正常关闭问题 2.1 添加admin-doc.vue 2.1.1 点击admin-ebook中的路由跳转到admin-doc 2.2.2 进入…...
新电脑必装的7款软件,缺一不可
如果你买了新电脑或者是重装了新系统,那么这7款软件你一定要安装。 1、SpaceSniffer 如果你的C盘经常爆红,但是不知道是什么原因,那么你应该需要SpaceSniffer这款软件,它可以把你C盘中文件的空间占用情况,以大小方框…...
程序员学习Processing和TouchDesigner视觉编程相关工具
Proessing Processing 是一种用于视觉艺术和创意编程的开发环境和编程语言。它最初是为了帮助非专业程序员学习编程,特别是那些对于创意编程和视觉表达感兴趣的人。Processing 提供了简单易用的 API,使得绘制图形、创建动画和交互式应用变得相对容易。 …...
gitlabcicd-k8s部署gitlab
一.安装准备环境 存储使用nfs挂载持久化 k8s环境 helm安装 建议helm 3 二.部署gitlab-deploy.yaml nfs的ip是192.168.110.190 挂载目录是/data/data 注意所需要的目录需要创建:/data/data/gitlab/config ,/data/data/gitlab/logs ,/dat…...
浅谈JDBC
文章目录 一、什么是 JDBC?二、JDBC 操作流程三、JDBC代码例子 一、什么是 JDBC? JDBC是一种可用于执行SQL语句的JAVA API,是链接数据库和JAVA应用程序的纽带。JDBC一般需要进行3个步骤:与数据库建立一个链接、向数据库发送SQL语…...
【数据结构初阶】--- 顺序表
顺序表,好像学C语言时从来没听过,实际上就是给数组穿了层衣服,本质是一模一样的。 这里的顺序表实际是定义了一个结构体,设计各种函数来实现它的功能,比如说数组中的增删改查插入,这些基本操作其实平时就会…...
一个完整的java项目通常包含哪些层次(很全面)
1.View层(视图层) 职责:负责数据的展示和用户交互。在Web应用中,View层通常与HTML、CSS和JavaScript等技术相关。 技术实现:在Spring MVC中,View层可以使用JSP、Thymeleaf、FreeMarker等模板引擎来实现。…...
设置电脑定时关机
1.使用快捷键winR 打开运行界面 2.输入cmd ,点击确认,打开命令行窗口,输入 shutdown -s -t 100,回车执行命令,自动关机设置成功 shutdown: 这是主命令,用于执行关闭或重启操作。-s: 这个参数用于指定执行关…...
Java 编译报错:找不到符号? 手把手教你排查解决!
Java 编译报错:找不到符号? 手把手教你排查解决! 在 Java 开发过程中,我们经常会遇到编译器抛出 "找不到符号" 错误。这个错误提示意味着编译器无法在它所理解的范围内找到你所引用的类、变量或方法。这篇文章将带你一步…...
Gitte的使用(Windows/Linux)
Gitte的使用(Windows/Linux) 一、Windows上使用Gitte1.下载程序2.在Gitte上创建远程仓库3.连接远程仓库4.推送文件到远程仓库 二、Linux上使用Gitte1.第一次从仓库上传1.1生成公钥1.2配置SSH公钥1.3新建一个仓库1.4配置用户名和邮箱在Linux中1.5创建仓库…...
c++之旅第十弹——IO流
大家好啊,这里是c之旅第十弹,跟随我的步伐来开始这一篇的学习吧! 如果有知识性错误,欢迎各位指正!!一起加油!! 创作不易,希望大家多多支持哦! 一.流的概念&…...
量化交易:Miniqmt获取可转债数据和交易python代码
哈喽,大家好,我是木头左! 低风险资产除了国债外,还有可转债,兼容有高收益的股性和低风险的债性,号称“下有保底,上不封顶”。 🔍 可转债:金融市场的双面娇娃 可转债&am…...
测试开发之自动化篇 —— 使用Selenium IDE录制脚本!
今天,我们开始介绍基于开源Selenium工具的Web网站自动化测试。 Selenium包含了3大组件,分别为:1. Selenium IDE 基于Chrome和Firefox扩展的集成开发环境,可以录制、回放和导出不同语言的测试脚本。 2. WebDriver 包括一组为不同…...
Django 外键关联数据
在设计数据库的时候,是得需要通过外键的形式将各个表进行连接。 原先的表是这样的 要想更改成这样: 下面是操作步骤: 有两张表是关联的 # 在 models.py 里创建class Department(models.Model):"""部门表""&quo…...
开源与新质生产力
在这个信息技术迅猛发展的时代,全球范围内的产业都在经历着深刻的变革。在这样的背景下,“新质生产力”的概念引起了广泛的讨论。无论是已经成为或正努力转型成为新质生产力的企业,都在寻求新的增长动力和竞争优势。作为一名长期从事开源领域…...
建立网站最先进的互联网技术有哪些/最新新闻热点事件及评论
1.debug的启动,退出和常用指令。 用r命令修改寄存器AX中的内容 用debug的D查看内存中的内容 查看1000:9单元中的内容 用e命令修改从1000:0开始的10个单元的内容 用e命令将机器码写入内存 用u命令将内存单元中的内容翻译为汇编指令显示 使用t命…...
网站联动/seo流程
一、集合数据类型(set):无序不重复的集合,交集、并集等功能 二、三元运算符 三、深浅拷贝 1)字符串和数字:深浅内存地址都一样 2)其他:浅拷贝:仅复制最外面第一层 深拷贝:除了最内层其他均拷贝四…...
dw做网站基础/seo外链平台热狗
阅读目录 一、概念描述二、带有IN谓词的子查询三、带有比较运算符的子查询四、带有ANY(SOME)或ALL谓词的子查询五、带有 EXISTS 谓词的子查询六、总结回到顶部 一、概念描述 在SQL语言中,一个 SELECT-FROM-WHERE 语句称为一个查询块。将一个查…...
网站开发设计工具/网站关键词排名分析
对于该教程而言,缺少了删除已存在的电影记录的功能。因此,我在这里给出删除功能的代码供大家参考学习。 另外,需要注意的是要为VS2008打上SP1服务包,不然就不能使用ADO.NET Entity Data Model功能了。附按本教程制作的MovieDataba…...
如何做正版小说网站/武汉seo推广优化
0 引言 《信息安全技术 信息系统安全等级保护基本要求》(GB/T 22239-2008)在我国推行信息安全等级保护制度的过程中起到了非常重要的作用, 被广泛用于各行业或领域, 指导用户开展信息系统安全等级保护的建设整改、等级测评等工作[1]。随着信息技术的发展…...
wordpress英文主题破解版/互联网营销师含金量
QPushButton 在任何 GUI 设计中,命令按钮都是最重要和最常用的控件。 任何计算机用户都熟悉带有保存、打开、确定、是、否和取消等作为标题的按钮。 在 PyQt API 中,QPushButton 类对象提供了一个按钮,当单击该按钮时,可以对其进行编程以调用某个函数。 QPushButton 类从…...