设计模式の命令访问者迭代器模式

文章目录

  • 前言
  • 一、命令模式
  • 二、访问者模式
  • 三、迭代器模式


前言

  本篇是关于设计模式中命令模式、访问者模式、以及迭代器模式的学习笔记。


一、命令模式

  命令模式是一种行为型设计模式,其核心目的在于将命令的发送者和接受者解耦,提供一个中间层对命令进行统一的管理,请求的发送者不需要知道接收者的具体实现,而只需依赖一个抽象的命令接口。
  命令模式通常包括以下的角色:

  • 抽象/接口命令层:定义了执行命令的抽象方法,例如执行,撤销。
  • 具体命令类:继承/实现了命令层,对命令的具体内容做一个描述。
  • 接收者:实际执行操作的对象,包含与请求相关的逻辑。
  • 调用者:调用命令对象以触发请求的执行。
  • 客户端:创建具体的命令对象,并将其绑定到调用者上。

  举一个生活中的案例,假设对于家电,想要通过一个万能遥控器去控制某个家电的开/关,而不需要每个家电都配一个遥控器,则可以将开,关设为具体命令类:

/*** 命令抽象类*/
public interface Command {/*** 执行命令*/void execute();/*** 回滚操作*/void undo();
}

  以对灯的开关操作为例,开灯的命令类:

/*** 开灯*/
public class LightOnCommand implements Command{private Light light;public LightOnCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.on();}@Overridepublic void undo() {light.off();}
}

  关灯的命令类:

/*** 关灯*/
public class LightOffCommand implements Command{private Light light;public LightOffCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.off();}@Overridepublic void undo() {light.on();}
}

  具体的电器,有开关的操作。

public class Light {public void on(){System.out.println("把灯打开");}public void off(){System.out.println("关灯了");}
}

  以及一个空的命令类

public class NoCommand implements Command{/*** 执行命令*/@Overridepublic void execute() {}/*** 回滚操作*/@Overridepublic void undo() {}
}

  命令的统一管理类,主要作用:

  • 对命令组进行初始化。
  • 设置操控某一个对象的具体命令。
  • 执行具体的命令。
  • 撤销命令。
public class RemoteController {//存放各种设备的开命令Command[] onCommands;//存放各种设备的关命令Command[] offCommands;//撤销的命令Command unCommand;/*** 初始化空命令*/public RemoteController(int number) {onCommands = new Command[number];offCommands = new Command[number];for (int i = 0; i < number; i++) {onCommands[i] = new NoCommand();offCommands[i] = new NoCommand();}}/*** 设置具体的命令** @param no         设备组编号* @param onCommand  具体设备组设备的开命令* @param offCommand 具体设备组设备的关命令*/public void setCommand(int no, Command onCommand, Command offCommand) {onCommands[no] = onCommand;offCommands[no] = offCommand;}/*** 按下某个设备的开按钮** @param no 设备组编号*/public void onButtonWasPushed(int no) {onCommands[no].execute();//记录本次操作,便于撤销unCommand = onCommands[no];}/*** 按下某个设备的关按钮** @param no 设备组编号*/public void offButtonWasPushed(int no) {offCommands[no].execute();//记录本次操作,便于撤销unCommand = offCommands[no];}/*** 撤销*/public void undo() {unCommand.undo();}
}

  客户端:

public class Client {public static void main(String[] args) {Light light = new Light();//初始化开启电灯命令LightOnCommand lightOnCommand = new LightOnCommand(light);//初始化关闭电灯命令LightOffCommand lightOffCommand = new LightOffCommand(light);//初始化遥控器面板RemoteController remoteController = new RemoteController(5);remoteController.setCommand(0, lightOnCommand, lightOffCommand);System.out.println("--------开启电灯--------");remoteController.onButtonWasPushed(0);System.out.println("--------关闭电灯--------");remoteController.offButtonWasPushed(0);System.out.println("--------撤销上一步操作--------");remoteController.undo();}
}

  如果需要进行扩展,比如电视,只需要去增加一个电视机对象,以及两个实现了命令接口的具体命令类,最后在客户端进行初始化即可,无需对命令管理类进行修改。

二、访问者模式

  访问者模式是一种行为型设计模式,主要用来将操作与对象结构分离。核心思想在不修改对象结构的前提下,定义作用于这些对象的新操作。
  访问者模式通过引入一个访问者对象,将对结构中各个元素的操作从元素类中抽离出来。这样,当需要添加新操作时,无需修改元素类,而只需添加新的访问者类,访问者模式由以下几个角色组成:

  • 访问者接口:声明对每种元素类型的访问操作方法。
  • 具体访问者:实现访问者接口,定义具体的操作逻辑。
  • 元素接口:定义接受访问者对象的方法。
  • 具体元素:实现元素接口,在接受访问者对象的方法中调用访问者的相应方法。
  • 对象结构:包含一组元素,可以遍历这些元素并让访问者访问它们。

  举一个生活中的案例,例如在网上商城将商品加入购物车,需要计算商品总价,还需要获取商品的详细清单,利用访问者模式,可以将商品作为被访问的类

/*** 被访问的元素接口*/
public interface Item {void accept(Visitor visitor);
}
/*** 被访问的元素:书籍*/
public class Book implements Item {private String title;private double price;public Book(String title, double price) {this.title = title;this.price = price;}public String getTitle() {return title;}public double getPrice() {return price;}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}
}
/*** 被访问的元素:电子产品*/
public class Electronic implements Item {private String name;private double price;public Electronic(String name, double price) {this.name = name;this.price = price;}public String getName() {return name;}public double getPrice() {return price;}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}
}

  定义一个访问者接口层,访问具体的商品(书籍,电子产品):

public interface Visitor {void visit(Book book);void visit(Electronic electronic);
}

  定义计算价格和打印清单具体的行为:

/*** 打印清单*/
public class ItemPrinterVisitor implements Visitor {@Overridepublic void visit(Book book) {System.out.println("Book: " + book.getTitle() + " - Price: " + book.getPrice());}@Overridepublic void visit(Electronic electronic) {System.out.println("Electronic: " + electronic.getName() + " - Price: " + electronic.getPrice());}
}
/*** 计算书籍和电子产品的价格*/
public class PriceCalculatorVisitor implements Visitor{private double totalPrice = 0;@Overridepublic void visit(Book book) {totalPrice = totalPrice+book.getPrice();}@Overridepublic void visit(Electronic electronic) {totalPrice = totalPrice + electronic.getPrice();}public double getTotalPrice(){return totalPrice;}
}

  以及具体的对象接口,让访问者去操作书籍和电子产品:

/*** 对象接口*/
public class ShoppingCart {private List<Item> items = new ArrayList<>();public void addItem(Item item) {items.add(item);}public void accept(Visitor visitor) {for (Item item : items) {item.accept(visitor);}}
}

  客户端:

public class Client {public static void main(String[] args) {// 创建购物车ShoppingCart cart = new ShoppingCart();cart.addItem(new Book("Spring源码深度解析", 50));cart.addItem(new Electronic("Iphone17", 6300));// 计算总价PriceCalculatorVisitor priceCalculator = new PriceCalculatorVisitor();cart.accept(priceCalculator);System.out.println("Total Price: " + priceCalculator.getTotalPrice());// 打印清单ItemPrinterVisitor itemPrinter = new ItemPrinterVisitor();cart.accept(itemPrinter);}
}

  这样的好处在于,将具体的操作对象结构分离,后续增加具体的操作,无需对对象结构进行修改。因此适用于需要对对象结构中的对象执行多种不相关的操作,而操作的实现细节需要彼此独立的场景。
  同时也可以体会一下如果不使用访问者模式,会存在怎么样的弊端:

  1. 代码耦合度高:在ShoppingCart类中,我们需要判断每个商品的类型,并为不同类型的商品执行不同的操作。这导致了ShoppingCart 类与具体商品类型的紧密耦合。如果要新增一种商品?就又要加一个条件分支。
  2. 扩展性差:同样地,除了计算价格和打印清单,如果还需要再增加一种操作?那么购物车的代码还需要进行修改。
// 书籍类(Book)
public class Book {private String title;private double price;public Book(String title, double price) {this.title = title;this.price = price;}public String getTitle() {return title;}public double getPrice() {return price;}
}// 电子产品类(ElectronicProduct)
public class ElectronicProduct {private String name;private double price;public ElectronicProduct(String name, double price) {this.name = name;this.price = price;}public String getName() {return name;}public double getPrice() {return price;}
}

  在购物车中直接计算总价,打印清单:

import java.util.ArrayList;
import java.util.List;public class ShoppingCart {private List<Object> items;public ShoppingCart() {items = new ArrayList<>();}public void addItem(Object item) {items.add(item);}public List<Object> getItems() {return items;}// 计算总价public double calculateTotalPrice() {double totalPrice = 0;for (Object item : items) {if (item instanceof Book) {totalPrice += ((Book) item).getPrice();} else if (item instanceof ElectronicProduct) {totalPrice += ((ElectronicProduct) item).getPrice();}}return totalPrice;}// 打印订单清单public void printOrderDetails() {System.out.println("Shopping Cart Details:");for (Object item : items) {if (item instanceof Book) {Book book = (Book) item;System.out.println("Book: " + book.getTitle() + ", Price: " + book.getPrice());} else if (item instanceof ElectronicProduct) {ElectronicProduct product = (ElectronicProduct) item;System.out.println("Product: " + product.getName() + ", Price: " + product.getPrice());}}}
}

三、迭代器模式

  迭代器模式是一种行为型设计模式,核心思想是将集合的遍历操作与集合的实现分离,从而不暴露集合的内部结构,只需要依赖一个公共的接口来访问集合中的元素。通过这种方式,可以在不改变集合结构的情况下,提供不同的遍历方式和逻辑,通常包含以下的角色:

  • 迭代器接口:定义遍历集合元素的方法,可以使用JDK的Iterator接口。
  • 具体迭代器:实现迭代器接口,负责具体的遍历逻辑。
  • 聚合接口:定义返回一个迭代器的方法。
  • 具体聚合:实现聚合接口,维护集合的实际数据结构,并提供创建迭代器的方法。

  假设现在有一个电商项目,需要管理多个订单。每个订单由多个商品组成。希望能够顺序遍历订单中的商品,而不暴露订单内部的实现细节,首先创建一个商品的实例

public class Product {private String productName;private double price;public Product(String productName, double price) {this.productName = productName;this.price = price;}public String getProductName() {return productName;}public double getPrice() {return price;}
}

  再编写一个聚合接口

public interface Aggregate {Iterator createIterator();
}

  创建具体聚合迭代器接口

public class Order implements Aggregate{private String orderId;private Product[] items;public Order(String orderId, Product[] items) {this.orderId = orderId;this.items = items;}public String getOrderId() {return orderId;}public Product[] getItems() {return items;}@Overridepublic Iterator createIterator() {return new OrderIterator(this);}
}
public class OrderIterator implements Iterator {private Order order;private int index;public OrderIterator(Order order) {this.order = order;this.index = 0;}@Overridepublic boolean hasNext() {return index < order.getItems().length;}@Overridepublic Object next() {if (hasNext()) {return order.getItems()[index++];}return null;}
}

  客户端,创建产品并且遍历:

public class Client {public static void main(String[] args) {Product p1 = new Product("产品1", 5);Product p2 = new Product("产品2", 6);Product p3 = new Product("产品3", 7);Order order = new Order("10001", new Product[]{p1, p2, p3});Iterator iterator = order.createIterator();while (iterator.hasNext()){Product next = (Product) iterator.next();System.out.println(next);}}
}

  同时也可以对比一下不使用迭代器模式,在客户端中直接遍历,体会一下存在哪些弊端:

  1. 客户端直接操作 Order 类的内部实现,如果以后把商品的存储方式改为其他数据结构,所有使用 Order 类的代码都需要修改。
  2. 遍历商品数组的逻辑直接写在了客户端,每个需要遍历商品的地方,都必须重复写遍历逻辑。
  3. 以及如果需要进行不同方式的遍历,需要修改遍历逻辑以及所有使用到的客户端的代码。
public class Product {private String productName;private double price;public Product(String productName, double price) {this.productName = productName;this.price = price;}public String getProductName() {return productName;}public double getPrice() {return price;}
}public class Order {private String orderId;private Product[] items;public Order(String orderId, Product[] items) {this.orderId = orderId;this.items = items;}public String getOrderId() {return orderId;}public Product[] getItems() {return items;}
}public class Client {public static void main(String[] args) {// 创建产品Product p1 = new Product("Laptop", 1000);Product p2 = new Product("Smartphone", 800);Product p3 = new Product("Headphones", 150);// 创建订单Product[] products = {p1, p2, p3};Order order = new Order("O1001", products);// 直接在客户端遍历商品Product[] items = order.getItems();for (int i = 0; i < items.length; i++) {System.out.println("Product: " + items[i].getProductName() + ", Price: " + items[i].getPrice());}}
}

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

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

相关文章

UE5仿漫威争锋灵蝶冲刺技能

这两天玩了一下漫威争锋Marvel Rivals&#xff0c;发现是UE5做的&#xff0c;对里面一些角色技能挺感兴趣的&#xff0c;想简单复刻一下技能功能&#xff0c;顺便复习一下学过的知识 首先把摄像机设置调整一下 CameraBoom里搜索lag 把摄像机延迟关掉 &#xff0c;这样摄像机就…

常用类晨考day15

1.基本数据类型以及对应包装类 Byte Short Integer Long Float Double Boolean Character 2.什么是自动拆箱和装箱&#xff0c;jdk版本有什么要求&#xff1f;代码举 例并标明 Integer a 100; // 装箱 int b a; // 拆箱 从JDK1.5才开始支持 3.NumberFormatException是什么异常…

Vue中<script setup></script>的主要语法元素和特性

<script setup>是 Vue 3 中引入的一种新的组件内脚本语法糖&#xff0c;它带来了更简洁、高效的组件逻辑编写方式。 以下是 <script setup> 的主要语法元素和特性&#xff1a; 1.导入和使用 直接在 <script setup> 中导入依赖&#xff0c;不需要在 compon…

深度学习推理速度优化指南

深度学习推理速度优化指南 简介一、显卡频率设置二、查看当前显卡频率三、调整显卡频率范围注意事项总结 简介 本文旨在探讨深度学习推理过程中 CUDA 或 TensorRT 推理速度变慢的问题&#xff0c;并提供实用的解决策略。我们将从显卡频率设置、模型权重优化等方面出发&#xf…

etcd+京东hotkey探测使用

qhotKey链接 京东hotkey把热点数据默认缓存在了本地缓存caffeine中&#xff0c;也可以存到redis中&#xff0c;但是京东hotkey的SDK没有redis的实现方法&#xff0c;因此需要自己实现。 官方目录结构下&#xff1a;分别是client客户端&#xff08;要打包引入到自己的项目&…

如何实现层叠布局

文章目录 1 概念介绍2 使用方法3 示例代码我们在上一章回中介绍了GirdView Widget,本章回中将介绍Stack这种Widget,闲话休提,让我们一起Talk Flutter吧。 1 概念介绍 在Flutter中Stack主要用来叠加显示其它的Widget,类似我们日常生活中的楼层或者说PS中的图层,因此它也是一…

Java 上机实践11(组件及事件处理)

&#xff08;大家好&#xff0c;今天分享的是Java的相关知识&#xff0c;大家可以在评论区进行互动答疑哦~加油&#xff01;&#x1f495;&#xff09; 目录 Plug&#xff1a;程序实现 方法一&#xff08;记事本&#xff09; 方法二&#xff08;IDEA&#xff09; 实验一&am…

UVM 验证方法学之interface学习系列文章(十一)virtual interface 再续篇

一 前言 并非总是可以将被测单元(DUT)视为一个黑盒,即仅监控和驱动DUT的顶层端口信号。这一点在从模块级测试转向更大规模的系统级测试时尤为明显。有时,我们需要了解实现细节以便访问DUT内部的信号。这被称为白盒验证。 Verilog一直提供从其他作用域访问几乎任何层次结构…

本地如何启动casdoor

1、下载代码 GitHub - casdoor/casdoor at v1.777.0 下载对应tag的代码&#xff0c;我这里选择的时v1.777.0版本 通过网盘分享的文件&#xff1a;casdoor-1.777.0.zip 链接: https://pan.baidu.com/s/1fPNqyJYeyfZnem_LtEc0hw 提取码: avpd 2、启动后端 1、使用goland编译…

CSDN外链失效3:

参考我之前的博客&#xff1a; 外链失效博客1&#xff1a;随想笔记1&#xff1a;CSDN写博客经常崩溃&#xff0c;遇到外链图片转存失败怎么办_csdn外链图片转存失败-CSDN博客 外链失效博客2&#xff1a;网络随想2&#xff1a;转语雀_md格式转语雀lake格式-CSDN博客 markdown…

Kubernates

kubernates是一个开源的&#xff0c;用于管理云平台中多个主机上的容器化的应用&#xff0c;Kubernetes的目标是让部署容器化的应用简单并且高效&#xff08;powerful&#xff09;,Kubernetes提供了应用部署&#xff0c;规划&#xff0c;更新&#xff0c;维护的一种机制。 架构…

Pycharm 更改字体大小

更改代码字体的大小 更改软件字体的大小

Ubuntu20.04解决docker安装后is the docker daemon running? 问题

Ubuntu20.04解决docker安装后is the docker daemon running? 问题 问题描述问题分析问题解决 问题描述 docker info后报错 ERROR: Cannot connect to the Docker daemon at unix:///root/.docker/desktop/docker.sock. Is the docker daemon running? errors pretty printi…

STM32完全学习——CRC校验

一、STM32F407CRC校验的补充 由于STM32F407的CRC校验&#xff0c;并没有什么配置的选项&#xff0c;就会导致他只能进行32位&#xff0c;且初始值是0XFFFFFFFF&#xff0c;输入和输出都没有反转的CRC校验。为了弥补这些不足点&#xff0c;于是自己编写函数来增加这些功能 //输…

CSS系列(27)- 图形与滤镜详解

前端技术探索系列&#xff1a;CSS 图形与滤镜详解 &#x1f3a8; 致读者&#xff1a;探索CSS的艺术表现力 &#x1f44b; 前端开发者们&#xff0c; 今天我们将深入探讨 CSS 图形和滤镜效果&#xff0c;学习如何创建引人注目的视觉效果。 基础图形 &#x1f680; 几何形状…

四、使用langchain搭建RAG:金融问答机器人--构建web应用,问答链,带记忆功能

经过前面3节完成金融问答机器人基本流程&#xff0c;这章将使用Gradio构建web应用&#xff0c;同时加入memory令提示模板带有记忆的&#xff0c;使用LCEL构建问答链。 加载向量数据库 from langchain.vectorstores import Chroma from langchain_huggingface import HuggingF…

矩阵链乘法【东北大学oj数据结构10-2】C++

矩阵链乘法问题的目标是找到最有效的方法来乘以给定的 n 个矩阵 M1,M2,M3,...,Mn。 编写一个程序&#xff0c;读取 Mi 的维度&#xff0c;并找到最小标量乘法以计算最大链链乘法 M1M2...Mn。 输入 在第一行中&#xff0c;给出了一个整数 n。 在接下来的 n 行中&#xff0c;矩阵…

深度学习之超分辨率算法——SRCNN

网络为基础卷积层 tensorflow 1.14 scipy 1.2.1 numpy 1.16 大概意思就是针对数据&#xff0c;我们先把图片按缩小因子照整数倍进行缩减为小图片&#xff0c;再针对小图片进行插值算法&#xff0c;获得还原后的低分辨率的图片作为标签。 main.py 配置文件 from model im…

基于海思soc的智能产品开发(mcu读保护的设置)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 对于市场上的产品&#xff0c;除了电路之外&#xff0c;软件保护也是非常重要的一个环节。要是自己辛辛苦苦写的软件&#xff0c;被竞争对手轻易地…

Zabbix6.0升级为6.4

为了体验一些新的功能&#xff0c;比如 Webhook 和问题抑制等&#xff0c;升级个小版本。 一、环境信息 1. 版本要求 一定要事先查看官方文档&#xff0c;确认组件要求的版本&#xff0c;否则版本过高或者过低都会出现问题。 2. 升级前后信息 环境升级前升级后操作系统CentOS…