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

文章目录

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


前言

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


一、命令模式

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

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

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

/*** 命令抽象类*/
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是什么异常…

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…

本地如何启动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…

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

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

深度学习之超分辨率算法——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…

GitLab的卸载与重装

目录 一、GitLab的卸载 二、 GitLab的安装与配置 1. 创建安装目录 2. 安装 3. 使用 3.1 初始化 3.2 创建空白项目 ​编辑 3.3 配置SSH 3.3.1 配置公钥 ​编辑 3.3.2 配置私钥 3.4 配置本地git库 一、GitLab的卸载 1. 停止gitlab sudo gitlab-ctl stop 2. 卸载…

Linux快速入门-Linux的常用命令

Linux的常用命令 1. Linux的终端与工作区1.1 终端概述1.2 切换终端 2. Shell语言解释器2.1 Shell概述 3. 用户登录与身份切换3.1 su 命令3.2 sudo 命令 4. 文件、目录操作命令4.1 pwd 命令4.2 cd 命令4.3 ls 命令4.3.1 ls 指令叠加使用 4.4 mkdir 命令4.5 rmdir 命令4.6 cp 命令…

三、ubuntu18.04安装docker

1.使用默认ubuntu存储库安装docker 更新软件存储库 更新本地软件数据库确保可以访问最新版本。打开终端输入&#xff1a;sudo apt-get update 卸载旧版本的docker 建议继续之前卸载任何旧的docker软件。打开终端输入&#xff1a;sudo apt-get remove docker docker-engine …

【Linux系统编程】:信号(2)——信号的产生

1.前言 我们会讲解五种信号产生的方式: 通过终端按键产生信号&#xff0c;比如键盘上的CtrlC。kill命令。本质上是调用kill()调用函数接口产生信号硬件异常产生信号软件条件产生信号 前两种在前一篇文章中做了介绍&#xff0c;本文介绍下面三种. 2. 调用函数产生信号 2.1 k…

专业电脑数据恢复软件 iFind Data Recovery v9.2.3 绿色便携版

前言 iFinD Data Recovery一款特别实用的数据找回工具&#xff0c;它很厉害&#xff0c;能帮你在SSD硬盘和Windows10系统上找回丢失的数据。而且&#xff0c;它还能深度扫描并恢复各种主流数码相机里的RAW格式照片&#xff0c;速度超快&#xff0c;用起来也很稳定顺畅&#xf…

QT:Widgets中的数据库应用

SQL数据库驱动 pro文件中添加如下一行代码 QT sql widgetsmain.cpp #include <QApplication> #include <QSqlDatabase> #include <QStringList> int main(int argc, char *argv[]) {QApplication a(argc, argv);qDebug() << "Available driver…