【设计模式】- 行为型模式1

模板方法模式

定义了一个操作中的算法骨架,将算法的一些步骤推迟到子类,使得子类可以不改变该算法结构的情况下重定义该算法的某些步骤

主要角色】:

  • 抽象类:给出一个算法的轮廓和骨架(包括一个模板方法 和 若干基本方法)
    • 模板方法:定义了算法的骨架(执行顺序),按某种顺序调用其包含的基本方法
    • 基本方法:实现算法各个步骤的方法
      • 抽象方法:由抽象类声明,具体子类实现
      • 具体方法:由抽象类或具体类声明或实现,子类可以覆盖也可以继承
      • 钩子方法:在抽象类中已经实现,包含用于判断的逻辑方法和需要子类重写的空方法(一般是用于判断的逻辑方法,方法名为isXxx,返回值为boolean类型)
  • 具体子类:实现抽象类中所定义的抽象方法和钩子方法

案例:炒菜

需求】:炒菜的步骤是固定的,分为倒油、热油、倒蔬菜、倒调料品、翻炒等步骤。

抽象类(定义模板方法和基本方法):

public abstract class AbstractClass {// 模板方法定义(子类不能修改模板方法)public final void cookProcess() {pourOil();heatOil();pourVegetable();pourSauce();fry();}public void pourOil() {System.out.println("倒油");}public void heatOil() {System.out.println("热油");}// 倒蔬菜(一个是包菜、一个是菜心)public abstract void pourVegetable();// 倒调味品public abstract void pourSauce();public void fry() {System.out.println("翻炒");}
}

炒包菜类、炒菜心类(具体子类):

public class ConcreteClassBaoCai extends AbstractClass {@Overridepublic void pourVegetable() {System.out.println("下锅的蔬菜是包菜");}@Overridepublic void pourSauce() {System.out.println("下锅的酱料是辣椒");}
}public class ConcreteClassCaiXin extends AbstractClass {@Overridepublic void pourVegetable() {System.out.println("下锅的蔬菜是菜心");}@Overridepublic void pourSauce() {System.out.println("下锅的酱料是蒜蓉");}
}

测试类:

public class Client {public static void main(String[] args) {// 创建对象AbstractClass baoCai = new ConcreteClassBaoCai();// 调用炒菜功能baoCai.cookProcess();}
}

将相同的代码放在抽象的父类中,把不同的代码放入不同的子类中。

适用场景

  1. 算法的整体部分比较固定,个别部分容易改变,就可以使用模板方法,将容易变化的部分抽象出来,让子类去实现这些方法。
  2. 通过子类来决定父类算法中某个步骤是否执行,实现子类对父类的反向控制。

策略模式

该模式定义了一系列算法, 使他们可以互相替换,算法的变化不会影响使用算法的客户。

主要角色】:

  • 抽象策略类:由一个接口或抽象类实现,给出所有具体策略所需的接口
  • 具体策略类:实现了抽象策略类定义的接口,提供具体的算法实现或行为
  • 环境类:持有一个策略类的引用,最终给客户端调用

案例:促销活动

需求】:超市争对不同的节日(春节、中秋节、圣诞节)推出不同的促销活动,由促销员(环境类)将促销活动展示给客户。

抽象策略类:

public interface Strategy {void show();
}

策略A、B、C类(具体策略类):

public class StrategyA implements Strategy {@Overridepublic void show() {System.out.println("买一送一");}
}public class StrategyB implements Strategy {@Overridepublic void show() {System.out.println("满200减50");}
}public class StrategyC implements Strategy {@Overridepublic void show() {System.out.println("打8折");}
}

销售员类(环境类):

public class SalesMan {private Strategy strategy;public SalesMan(Strategy strategy) {this.strategy = strategy;}// 由促销员展示促销活动给普通用户public void salesManShow() {strategy.show();}
}

测试类:

public class Client {public static void main(String[] args) {// 切换策略ASalesMan salesMan = new SalesMan(new StrategyA());salesMan.salesManShow();// 切换策略BsalesMan = new SalesMan(new StrategyB());salesMan.salesManShow();// 切换策略CsalesMan = new SalesMan(new StrategyC());salesMan.salesManShow();}
}

策略类之间可以自由切换;
增加一个新的策略只需要添加一个具体的策略类
策略模式可以避免使用多重的if else 和 switch case

适用场景

  1. 需要动态在几种算法中选择一种,可以把每个算法封装到策略类中
  2. 大量的if else和switch cash完全可以用策略模式代替,让代码更加美观优雅

命令模式

将请求封装成一个对象,使发出请求的责任和执行请求的责任分隔开,这样两者之间通过命令对象进行沟通,方便将命令对象进行存储、传递、调用、增加、管理。

主要角色

  • 抽象命令角色:定义命令的接口,声明执行方法
  • 具体命令角色:实现命令接口,通常会持有接收者,并调用接收者的功能来完成命令要执行的操作
  • 实现者 / 接收者角色:真正执行命令的对象
  • 调用者 / 请求者角色:要求命令对象执行请求,通常会持有命令对象

案例:点餐

  • 服务员:调用者角色,由服务员发起命令
  • 厨师:接收者命令,真正命令执行的对象
  • 订单:命令中包含订单
    在这里插入图片描述

订单类:

@Data
public class Order {// 餐桌号码private int diningTable;// 所下餐品和份数private Map<String, Integer> foodDir = new HashMap<>();// 添加食物public void addFood(String name, int num) {foodDir.put(name, num);}
}

厨师类(接收者):

public class Chef {// 制作食物public void makeFood(String name, int num) {System.out.println(num + "份" + name);}
}

抽象命令类:

public interface Command {void execute();
}

具体命令类:

public class OrderCommand implements Command {// 持有接收者对象private Chef receiver;private Order order;public OrderCommand(Chef receiver, Order order) {this.receiver = receiver;this.order = order;}@Overridepublic void execute() {System.out.println(order.getDiningTable() + "桌的订单:");Map<String, Integer> foodDir = order.getFoodDir();foodDir.forEach((foodName, num)->{receiver.makeFood(foodName, num); // 让厨师去完成订单里的菜品});System.out.println(order.getDiningTable() + "桌的饭准备完毕");}
}

服务员类(调用者角色):

public class Waitor {// 持有多个命令对象private List<Command> commands = new ArrayList<>();public void addCommand(Command command) {// 将command存储到List集合中commands.add(command);}// 发起命令public void orderUp() {System.out.println("服务员:厨师,订单来啦");commands.forEach(command -> {if(command != null) command.execute();});}
}

测试类:

public class Client {public static void main(String[] args) {// 创建第一个订单对象Order order1 = new Order();order1.setDiningTable(1);order1.addFood("西红柿鸡蛋面", 1);order1.addFood("小杯可乐", 2);// 创建第二个订单对象Order order2 = new Order();order2.setDiningTable(2);order2.addFood("油闷大虾", 1);order2.addFood("小杯雪碧", 1);// 创建厨师对象Chef receiver = new Chef();// 创建命令对象OrderCommand cmd1 = new OrderCommand(receiver, order1);OrderCommand cmd2 = new OrderCommand(receiver, order2);// 创建调用者Waitor invoke = new Waitor();invoke.addCommand(cmd1);invoke.addCommand(cmd2);// 让服务员发起命令invoke.orderUp();}
}

适用场景

  1. 系统需要将请求调用这和请求接收者解耦
  2. 系统需要在不同时间指定请求、将请求排队和执行请求
  3. 系统需要支持命令的撤销(undo)和恢复(redo)操作

JDK源码解析:Runnable类

Runnable类就是一个命令模式,Thread是调用者,start()方法就是执行方法

责任链模式

避免发送者和多个请求处理者耦合在一起,将所有请求的处理者通过钱一个对象记住其下一个对象的引用而连成一条链;当请求发生时,可以将请求沿着这条链传递,直到有对象处理它为止

问题】公司员工请假,可以批假的领导有:部门负责人、副总经理、总经理,但是每个领导可以批准的天数不同。员工需要根据自己要请假的天数去找不同的领导签字。
解决】:比如有个员工要请假,他只需要去找部门负责人,如果部门负责人发现他请假的时间自己处理不了,就会把这个请假单交给副总经理,如果副总经理处理不了,就会交给总经理处理。

主要角色】:

  • 抽象处理者角色:定义一个处理请求的接口,包含抽象处理方法和后继处理方法
  • 具体处理者角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理就处理;如果不能处理就把它转发给后继的处理者
  • 客户类角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程

案例:请假流程

需求】:请假一天以下的只需要小组长同意;请假1-3天需要部门经理同意;请假3-7天需要总经理同意。

请假条类:

@AllArgsConstructor
@Data
public class LeaveRequest {// 姓名private String name;// 请假天数private int num;// 请假内容private String content;
}

抽象处理者类:

@Data
public abstract class Handler {protected final static int NUM_ONE = 1;protected final static int NUM_THREE = 3;protected final static int NUM_SEVEN = 7;// 领导了可以处理的天数区间private int numStart;private int numEnd;public Handler(int numStart, int numEnd) {this.numStart = numStart;this.numEnd = numEnd;}// 声明后继者(上级领导)private Handler nextHandler;// 各级领导处理请假条的方法protected abstract void handleLeave(LeaveRequest leave);// 提交请假条(不能被继承)public final void submit(LeaveRequest leave) {// 该领导审批handleLeave(leave);if(nextHandler != null && leave.getNum() > numEnd) {// 交给上级领导审批nextHandler.handleLeave(leave);} else {System.out.println("流程结束");}}
}

小组长类、部门经理类、总经理类(具体的处理者)

public class GroupLeader extends Handler {public GroupLeader() {super(0, Handler.NUM_ONE);}@Overrideprotected void handleLeave(LeaveRequest leave) {System.out.println(leave.getName() + "请假" + leave.getNum() + "天,原因:" + leave.getContent());System.out.println("小组长审批:同意");}
}public class Manager extends Handler {public Manager() {super(Handler.NUM_ONE, Handler.NUM_THREE);}@Overrideprotected void handleLeave(LeaveRequest leave) {System.out.println(leave.getName() + "请假" + leave.getNum() + "天,原因:" + leave.getContent());System.out.println("部门经理审批:同意");}
}public class GeneralManager extends Handler {public GeneralManager() {super(Handler.NUM_THREE, Handler.NUM_SEVEN);}@Overrideprotected void handleLeave(LeaveRequest leave) {System.out.println(leave.getName() + "请假" + leave.getNum() + "天,原因:" + leave.getContent());System.out.println("总经理类审批:同意");}
}

测试类:

public class Client {public static void main(String[] args) {// 1. 创建请假条对象LeaveRequest leave = new LeaveRequest("小明", 1, "身体不适");// 2. 创建各级领导对象GroupLeader groupLeader = new GroupLeader();Manager manager = new Manager();GeneralManager generalManager = new GeneralManager();// 3. 设置处理者链groupLeader.setNextHandler(manager);manager.setNextHandler(generalManager);// 4. 小明提交请假groupLeader.submit(leave); // 调用小组长里的submit()方法}
}

降低了请求发送者 和 请求接收者的耦合度
每个类只需要处理自己该处理的工作,不能处理的传递给下一个对象完成

状态模式

案例引入:电梯

电梯接口:

public interface ILift {// 电梯状态常量int OPENING_STATE = 1;int CLOSING_STATE = 2;int RUNNING_STATE = 3;int STOPPING_STATE = 4;// 设置电梯状态void settState(int state);// 电梯操作功能void open(); // 开门void close(); // 关门void run(); // 运行void stop(); // 停止
}

电梯类(ILift的子实现类):

public class Lift implements ILift {// 记录当前电梯的状态private int state;@Overridepublic void settState(int state) {this.state = state;}@Overridepublic void open() {if(state == STOPPING_STATE || state == CLOSING_STATE) {System.out.println("电梯打开");settState(OPENING_STATE);}}@Overridepublic void close() {if(state == OPENING_STATE) {System.out.println("电梯关闭");settState(CLOSING_STATE);}}@Overridepublic void run() {if(state == CLOSING_STATE || state == STOPPING_STATE) {System.out.println("电梯运行");settState(RUNNING_STATE);}}@Overridepublic void stop() {if(state == CLOSING_STATE || state == RUNNING_STATE) {System.out.println("电梯停止");settState(STOPPING_STATE);}}
}

测试类:

public class Client {public static void main(String[] args) {// 1. 创建电梯对象Lift lift = new Lift();// 2. 设置电梯状态lift.settState(ILift.OPENING_STATE);// 3. 操作电梯lift.open();lift.close();lift.run();lift.stop();}
}

存在问题】使用了大量的if else,使得阅读性变差。而且如果需要新增一个断电的状态,也需要修改上边的代码逻辑

状态模式:对于有状态的对象,把复杂的判断逻辑提取到不同的状态对象中,允许状态对象在内部状态发生改变时改变其行为。

主要角色

  • 环境角色:定义了客户程序需要的接口,维护一个当前的状态,并将与状态相关的操作委托给当前状态对象来处理
  • 义一个接口,用来封装环境对象中特定状态所对应的行为
  • 具体状态角色:实现抽象状态所对应的行为

案例:电梯

抽象状态类:

@Data
public abstract class LiftState {// 环境角色变量protected Context context;// 电梯开启public abstract void open();// 电梯关闭public abstract void close();// 电梯运行public abstract void run();// 电梯停止public abstract void stop();
}

环境角色类:

public class Context {// 定义对应状态的常量public final static OpeningState OPENING_STATE = new OpeningState();public final static ClosingState CLOSING_STATE = new ClosingState();public final static RunningState RUNNING_STATE = new RunningState();public final static StoppingState STOPPING_STATE = new StoppingState();// 定义一个当前状态变量private LiftState liftState;public LiftState getLiftState() {return liftState;}public void setLiftState(LiftState liftState) {this.liftState = liftState;// 设置当前对象的Context对象this.liftState.setContext(this);}public void open() {this.liftState.open();}public void close() {this.liftState.close();}public void run() {this.liftState.run();}public void stop() {this.liftState.stop();}
}

电梯关闭、开启、运行、停止状态类(具体状态类):

public class ClosingState extends LiftState {@Overridepublic void open() {super.context.setLiftState(Context.OPENING_STATE);super.context.getLiftState().open();}@Overridepublic void close() {System.out.println("电梯关闭");}@Overridepublic void run() {super.context.setLiftState(Context.RUNNING_STATE);super.context.getLiftState().run();}@Overridepublic void stop() {super.context.setLiftState(Context.STOPPING_STATE);super.context.getLiftState().stop();}
}public class OpeningState extends LiftState {// 当前状态要执行的方法@Overridepublic void open() {System.out.println("电梯 开启");}@Overridepublic void close() {// 修改状态super.context.setLiftState(Context.CLOSING_STATE);// 调用当前状态中的close()方法super.context.close();}@Overridepublic void run() {// 什么都不做}@Overridepublic void stop() {// 什么都不做}
}public class RunningState extends LiftState {@Overridepublic void open() {// 什么都不做}@Overridepublic void close() {// 什么都不做}@Overridepublic void run() {System.out.println("电梯运行");}@Overridepublic void stop() {super.context.setLiftState(Context.STOPPING_STATE);super.context.stop();}
}public class StoppingState extends LiftState {@Overridepublic void open() {super.context.setLiftState(Context.OPENING_STATE);super.context.getLiftState().open();}@Overridepublic void close() {super.context.setLiftState(Context.CLOSING_STATE);super.context.getLiftState().close();}@Overridepublic void run() {super.context.setLiftState(Context.RUNNING_STATE);super.context.getLiftState().run();}@Overridepublic void stop() {System.out.println("电梯停止");}
}

测试类:

public class Client {public static void main(String[] args) {// 创建环境角色对象Context context = new Context();// 设置当前电梯状态context.setLiftState(new ClosingState());context.open();context.close();context.run();context.stop();}
}

适用场景

  1. 当一个对象的行为取决于他的状态,并且他必须在运行时根据状态改变他的行为
  2. 一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/81865.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ubuntu22.04 卸载ESP-IDF

要在Ubuntu 22.04上完全卸载ESP-IDF&#xff0c;请按照以下步骤操作&#xff1a; 卸载ESP-IDF的步骤 删除ESP-IDF目录&#xff1a; # 假设ESP-IDF安装在~/esp/esp-idf目录 rm -rf ~/esp/esp-idf删除ESP-IDF工具链和下载的工具&#xff1a; rm -rf ~/.espressif从PATH中移除ESP…

SQLMesh 内置宏详解:@PIVOT等常用宏的核心用法与示例

本文系统解析 SQLMesh 的四个核心内置宏&#xff0c;涵盖行列转换的 PIVOT、精准去重的 DEDUPLICATE、灵活生成日期范围的 DATE_SPINE&#xff0c;以及动态表路径解析的 RESOLVE_TEMPLATE。通过真实案例演示参数配置与 SQL 渲染逻辑&#xff0c;并对比宏调用与传统 SQL 的差异&…

基于Springboot + vue3实现的工商局商家管理系统

项目描述 本系统包含管理员、商家两个角色。 管理员角色&#xff1a; 用户管理&#xff1a;管理系统中所有用户的信息&#xff0c;包括添加、删除和修改用户。 许可证申请管理&#xff1a;管理商家的许可证申请&#xff0c;包括搜索、修改或删除许可证申请。 许可证审批管理…

第五部分:第五节 - Express 路由与中间件进阶:厨房的分工与异常处理

随着你的 Express 应用变得越来越大&#xff0c;所有的路由和中间件都写在一个文件里会变得难以管理。这时候就需要将代码进行拆分和组织。此外&#xff0c;一个健壮的后端应用必须能够优雅地处理错误和一些常见的 Web 开发问题&#xff0c;比如跨域。 路由模块化 (express.Ro…

萌新联赛第(三)场

C题 这道题用暴力去写想都不要想&#xff0c;一定超时&#xff0c;于是我们需要优化&#xff0c;下面是思路过程&#xff1a; 如图&#xff0c;本题只需找到x的因数个数和(n-x)的因数个数&#xff0c;这两个相乘&#xff0c;得到的就是对于这个x来说组合的个数&#xff0c;且x…

【Android构建系统】如何在Camera Hal的Android.bp中选择性引用某个模块

背景描述 本篇文章是一个Android.bp中选择性引用某个模块的实例。 如果是Android.mk编译时期&#xff0c;在编译阶段通过某个条件判断是不是引用某个模块A, 是比较好实现的。Android15使用Android.bp构建后&#xff0c;要想在Android.bp中通过自定义的一个变量或者条件实现选…

【OneNET】_01_使用微信小程序通过新版OneNET平台获取STM32设备信息并进行控制

【OneNET】_01_使用微信小程序通过新版OneNET平台获取STM32设备信息并进行控制 一、 前言1.1 OntNET硬件方面: STM32F103C8T6 ESP01S教程 1.2 微信小程序方面 二、STM32代码部分修改三、微信小程序修改的部分四、小笔记&#xff08;个人杂记&#xff09;4.1 OneNETOneNET物联网…

用 python 编写的一个图片自动分类小程序(三)

图片自动分类识别小程序记录 2025/5/18 0:38修改程序界面&#xff0c;增加一些功能 用 python 编写的一个图片自动识别分类小程序。 操作系统平台&#xff1a;Microsoft Windows 11 编程语言和 IDE&#xff1a;python 3.10 Visual studio code 一&#xff1a;图片自动分…

嵌入式硬件篇---SGP30 气体传感器

文章目录 前言一、SGP30 气体传感器详解(一)基本概述(二)工作原理传感器结构检测机制自校准功能(三)主要特性(四)应用场景智能家居空气质量检测仪汽车行业商业建筑二、TVOC 与 eCO2 的含义(一)TVOC(总挥发性有机化合物)定义危害健康标准(二)eCO2(等效二氧化碳)…

【原创】ubuntu22.04下载编译AOSP 15

安装依赖的库&#xff0c;顺便把vim 也安装一下 sudo apt-get install vim sudo apt-get install git gnupg flex bison build-essential zip curl zlib1g-dev libc6-dev-i386 x11proto-core-dev libx11-dev lib32z1-dev libgl1-mesa-dev libxml2-utils xsltproc unzip font…

防止勒索病毒的兜底方案——备份

勒索病毒入侵会对您的业务数据进行加密勒索&#xff0c;导致业务中断、数据泄露、数据丢失等&#xff0c;从而带来严重的业务风险。 防止勒索病毒有三个方向&#xff1a; 1&#xff09;实时防御已知勒索病毒 各个云厂商的云安全中心实现了对大量已知勒索病毒的实时防御。在服务…

es在已有历史数据的文档新增加字段操作

新增字段设置默认值 场景 在已经有大量数据的索引文档上&#xff0c;增加新字段 技术实现 一.更新索引映射 通过PUT请求显式定义新字段类型&#xff0c;确保后续写入的文档能被正确解析 PUT /文档名/_mapping {"properties": {"字段名1": {"type…

留给王小川的时间不多了

王小川&#xff0c;这位头顶“天才少年”光环的清华学霸、搜狗输入法创始人、中国互联网初代技术偶像&#xff0c;正迎来人生中最难啃的硬骨头。 他在2023年创立的百川智能&#xff0c;被称为“大模型六小虎”之一。今年4月&#xff0c;王小川在全员信中罕见地反思过去两年工作…

深入掌握MyBatis:连接池、动态SQL、多表查询与缓存

文章目录 一、MyBatis连接池1.1 连接池的作用1.2 MyBatis连接池分类 二、动态SQL2.1 if标签2.2 where标签2.3 foreach标签2.4 SQL片段复用 三、多表查询3.1 多对一查询&#xff08;一对一&#xff09;3.2 一对多查询 四、延迟加载4.1 立即加载 vs 延迟加载4.2 配置延迟加载 五、…

TDesign AI Chat - Vue3.x 可用!腾讯出品的 AIGC 交互对话组件,免费开源、包含设计资源

各位前端开发者有遇到做 AI Chat 项目的聊天交互界面需求了吗&#xff1f;TDesign 出品的这个组件很不错&#xff0c;推荐给大家。 TDesign AI Chat 是 TDesign 为 AIGC 场景开发的 UI 系列组件中的一部分&#xff0c;主要用于开发目前非常流行的 ChatBot 对话交互场景。最近 …

spring -MVC-02

SpringMVC-11 - 响应 在 SpringMVC 中&#xff0c;响应是服务器对客户端请求的反馈&#xff0c;它可以以多种形式呈现&#xff0c;包括视图名称、ModelAndView 对象、JSON 数据以及重定向等。以下是对 SpringMVC 中不同响应类型的详细介绍&#xff1a; 1. 视图名称 通过返回…

老旧设备升级利器:Modbus TCP转 Profinet让能效监控更智能

在工业自动化领域&#xff0c;ModbusTCP和Profinet是两种常见的通讯协议。Profinet是西门子公司推出的基于以太网的实时工业以太网标准&#xff0c;而Modbus则是由施耐德电气提出的全球首个真正开放的、应用于电子控制器上的现场总线协议。这两种协议各有各的优点&#xff0c;但…

ubuntu下docker安装mongodb-支持单副本集

1.mogodb支持事务的前提 1) MongoDB 版本&#xff1a;确保 MongoDB 版本大于或等于 4.0&#xff0c;因为事务支持是在 4.0 版本中引入的。 2) 副本集配置&#xff1a;MongoDB 必须以副本集&#xff08;Replica Set&#xff09;模式运行&#xff0c;即使是单节点副本集&#x…

【前端开发】Uniapp日期时间选择器:实现分钟动态步长设置

技术栈 Uniapp + Vue3 + uView年份显示前后一年,分钟动态设置间隔效果图 主体显示<view class="uni-row-between selector"><view class="uni-flex-1 left" @click="!props.disabled && openPicker()"><uni-iconscolor=…

iOS 蓝牙开发中的 BT 与 BLE

在 iOS 开发者的语境里&#xff0c;大家把 BT 和 BLE 当成两种不同的蓝牙技术在谈——它们来自同一个 Bluetooth 规范&#xff0c;但面向的场景、协议栈乃至 Apple 提供的 API 都截然不同。 缩写全称 / 技术名称规范层叫法iOS 支持现状典型用途BTBluetooth Classic&#xff08…