【设计模式】行为型模式(五):解释器模式、访问者模式、依赖注入

设计模式之行为型模式》系列,共包含以下文章:

  • 行为型模式(一):模板方法模式、观察者模式
  • 行为型模式(二):策略模式、命令模式
  • 行为型模式(三):责任链模式、状态模式
  • 行为型模式(四):备忘录模式、中介者模式
  • 行为型模式(五):解释器模式、访问者模式、依赖注入

😊 如果您觉得这篇文章有用 ✔️ 的话,请给博主一个一键三连 🚀🚀🚀 吧 (点赞 🧡、关注 💛、收藏 💚)!!!您的支持 💖💖💖 将激励 🔥 博主输出更多优质内容!!!

行为型模式(五):解释器模式、访问者模式

  • 9.解释器模式(Interpreter)
    • 9.1 代码示例
      • 9.1.1 定义表达式接口
      • 9.1.2 实现具体表达式类
      • 9.1.3 客户端
  • 10.访问者模式(Visitor)
    • 10.1 代码示例
      • 10.1.1 元素接口
      • 10.1.2 具体元素
      • 10.1.3 访问者接口
      • 10.1.4 具体访问者
      • 10.1.5 对象结构
      • 10.1.6 客户端
      • 10.1.7 输出
  • 11.依赖注入(Dependency Injection)
    • 11.1 代码示例
      • 11.1.1 构造器注入(Constructor Injection)
      • 11.1.2 设值方法注入(Setter Injection)
      • 11.1.3 接口注入
        • 11.1.3.1 定义注入接口
        • 11.1.3.2 实现注入接口
        • 11.1.3.3 客户端
    • 11.2 总结

9.解释器模式(Interpreter)

解释器模式Interpreter)是一种行为设计模式,它主要用于处理语言、表达式或命令的解析和执行。这种模式定义了如何构建一个解释器来解析特定的语句或命令,并执行相应的操作。下面是解释器模式的一些关键点:

  • 文法定义:首先需要定义一个文法,这个文法描述了语言或表达式的结构。例如,一个简单的算术表达式文法可能包括加法、减法等操作。
  • 表达式接口:定义一个抽象类或接口,所有具体的表达式类都实现这个接口。接口通常包含一个 interpret 方法,用于解释和执行表达式。
  • 具体表达式类:实现表达式接口的具体类,每个类负责解析和执行特定类型的表达式。例如,AddExpression 类负责处理加法操作。
  • 上下文:一个上下文对象,用于存储解析过程中需要的信息,如变量值、中间结果等。
  • 客户端:客户端代码将表达式组合成一个大的表达式树,然后调用 interpret 方法来解析和执行整个表达式。

在这里插入图片描述

9.1 代码示例

假设我们要解析和执行一个简单的算术表达式,如 1 + 2 * 3。我们可以使用解释器模式来实现:

9.1.1 定义表达式接口

public interface Expression {int interpret();
}

9.1.2 实现具体表达式类

public class NumberExpression implements Expression {private int number;public NumberExpression(int number) {this.number = number;}@Overridepublic int interpret() {return number;}
}public class AddExpression implements Expression {private Expression left, right;public AddExpression(Expression left, Expression right) {this.left = left;this.right = right;}@Overridepublic int interpret() {return left.interpret() + right.interpret();}
}public class MultiplyExpression implements Expression {private Expression left, right;public MultiplyExpression(Expression left, Expression right) {this.left = left;this.right = right;}@Overridepublic int interpret() {return left.interpret() * right.interpret();}
}

9.1.3 客户端

public class Client {public static void main(String[] args) {Expression expression = new AddExpression(new NumberExpression(1),new MultiplyExpression(new NumberExpression(2),new NumberExpression(3)));int result = expression.interpret();System.out.println("结果: " + result); // 输出: 结果: 7}
}
  • expression 是一个 AddExpression 对象。
    • 调用 AddExpressioninterpret 方法:
      • left.interpret() 调用 NumberExpression(1)interpret 方法,返回 1。
      • right.interpret() 调用 MultiplyExpression(2, 3)interpret 方法:
        • left.interpret() 调用 NumberExpression(2)interpret 方法,返回 2。
        • right.interpret() 调用 NumberExpression(3)interpret 方法,返回 3。
        • MultiplyExpressioninterpret 方法返回 2 * 3 = 6。
      • AddExpressioninterpret 方法返回 1 + 6 = 7。

通过这种方式,解释器模式可以灵活地解析和执行复杂的表达式,同时保持代码的可扩展性和可维护性。

10.访问者模式(Visitor)

访问者模式Visitor)是一种行为设计模式,它允许你在不改变数据结构的情况下,为数据结构中的元素添加新的操作。这种模式特别适用于数据结构相对稳定,但需要在数据结构上定义很多操作的场景。以下是访问者模式的几个关键点:

  • 元素Element):定义一个接受访问者的方法 accept(Visitor visitor),该方法会调用访问者的方法来访问元素。
  • 访问者Visitor):定义一系列访问方法,每个方法对应一种元素类型。访问方法通常命名为 visit(Element element)
  • 具体元素ConcreteElement):实现 accept 方法,该方法会调用访问者的一个访问方法。
  • 具体访问者ConcreteVisitor):实现访问者接口中的访问方法,对具体元素进行操作。
  • 对象结构ObjectStructure):可以是集合或其他数据结构,包含元素对象,并提供方法让访问者访问这些元素。

在这里插入图片描述

10.1 代码示例

假设你有一个文档编辑器,文档中包含不同类型的对象,如文本段落和图片。你希望在不修改这些对象的情况下,为它们添加新的操作,比如计算字数或生成缩略图。

10.1.1 元素接口

interface Element {void accept(Visitor visitor);
}

10.1.2 具体元素

class Paragraph implements Element {private String text;public Paragraph(String text) {this.text = text;}public String getText() {return text;}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}
}class Image implements Element {private String url;public Image(String url) {this.url = url;}public String getUrl() {return url;}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}
}

10.1.3 访问者接口

interface Visitor {void visit(Paragraph paragraph);void visit(Image image);
}

10.1.4 具体访问者

class WordCountVisitor implements Visitor {private int wordCount = 0;@Overridepublic void visit(Paragraph paragraph) {String[] words = paragraph.getText().split("\\s+");wordCount += words.length;}@Overridepublic void visit(Image image) {// 图片不增加字数}public int getWordCount() {return wordCount;}
}class ThumbnailGeneratorVisitor implements Visitor {@Overridepublic void visit(Paragraph paragraph) {// 文本段落不需要生成缩略图}@Overridepublic void visit(Image image) {System.out.println("Generating thumbnail for image: " + image.getUrl());}
}

10.1.5 对象结构

class Document {private List<Element> elements = new ArrayList<>();public void addElement(Element element) {elements.add(element);}public void accept(Visitor visitor) {for (Element element : elements) {element.accept(visitor);}}
}

10.1.6 客户端

public class VisitorPatternDemo {public static void main(String[] args) {Document document = new Document();document.addElement(new Paragraph("Hello, world!"));document.addElement(new Image("http://example.com/image.jpg"));WordCountVisitor wordCountVisitor = new WordCountVisitor();document.accept(wordCountVisitor);System.out.println("Total word count: " + wordCountVisitor.getWordCount());ThumbnailGeneratorVisitor thumbnailGeneratorVisitor = new ThumbnailGeneratorVisitor();document.accept(thumbnailGeneratorVisitor);}
}

10.1.7 输出

Total word count: 2
Generating thumbnail for image: http://example.com/image.jpg

通过访问者模式,你可以在不修改文档元素的情况下,为它们添加新的操作,如计算字数和生成缩略图。

11.依赖注入(Dependency Injection)

依赖注入Dependency Injection)是一种设计模式,用于实现控制反转(Inversion of ControlIoC)。它的主要目的是减少代码之间的耦合,提高代码的可测试性和可维护性。通过依赖注入,对象的依赖关系由外部提供,而不是由对象自己创建或查找。

在这里插入图片描述

11.1 代码示例

假设有一个 Logger 接口和两个实现类 FileLoggerConsoleLogger,以及一个需要日志功能的 UserService 类。

11.1.1 构造器注入(Constructor Injection)

通过构造器传递依赖对象。

  • 优点:依赖关系清晰,不可变性好。
  • 缺点:构造器参数过多时,代码可读性下降。
// Logger 接口
interface Logger {void log(String message);
}// FileLogger 实现
class FileLogger implements Logger {@Overridepublic void log(String message) {System.out.println("File: " + message);}
}// ConsoleLogger 实现
class ConsoleLogger implements Logger {@Overridepublic void log(String message) {System.out.println("Console: " + message);}
}// UserService 类,通过构造器注入 Logger
class UserService {private final Logger logger;public UserService(Logger logger) {this.logger = logger;}public void createUser(String name) {logger.log("Creating user: " + name);// 其他创建用户的逻辑}
}// 客户端代码
public class Main {public static void main(String[] args) {Logger logger = new ConsoleLogger();UserService userService = new UserService(logger);userService.createUser("Alice");}
}

11.1.2 设值方法注入(Setter Injection)

通过设值方法(setter)传递依赖对象。

  • 优点:灵活性高,便于修改依赖关系。
  • 缺点:依赖关系不那么明显,对象可能处于不完整状态。
// Logger 接口
interface Logger {void log(String message);
}// FileLogger 实现
class FileLogger implements Logger {@Overridepublic void log(String message) {System.out.println("File: " + message);}
}// ConsoleLogger 实现
class ConsoleLogger implements Logger {@Overridepublic void log(String message) {System.out.println("Console: " + message);}
}// UserService 类,通过设值方法注入 Logger
class UserService {private Logger logger;public void setLogger(Logger logger) {this.logger = logger;}public void createUser(String name) {logger.log("Creating user: " + name);// 其他创建用户的逻辑}
}// 客户端代码
public class Main {public static void main(String[] args) {UserService userService = new UserService();Logger logger = new ConsoleLogger();userService.setLogger(logger);userService.createUser("Alice");}
}

11.1.3 接口注入

通过接口方法传递依赖对象。

  • 优点:灵活性高,适用于复杂的依赖关系。
  • 缺点:实现复杂,使用较少。
11.1.3.1 定义注入接口
// Logger 接口
interface Logger {void log(String message);
}// FileLogger 实现
class FileLogger implements Logger {@Overridepublic void log(String message) {System.out.println("File: " + message);}
}// ConsoleLogger 实现
class ConsoleLogger implements Logger {@Overridepublic void log(String message) {System.out.println("Console: " + message);}
}// 注入接口
interface LoggerInjector {void injectLogger(Logger logger);
}
11.1.3.2 实现注入接口

UserService 类需要实现 LoggerInjector 接口,并提供一个方法来注入 Logger 对象。

// UserService 类,实现 LoggerInjector 接口
class UserService implements LoggerInjector {private Logger logger;@Overridepublic void injectLogger(Logger logger) {this.logger = logger;}public void createUser(String name) {logger.log("Creating user: " + name);// 其他创建用户的逻辑}
}
11.1.3.3 客户端
public class Main {public static void main(String[] args) {// 创建 UserService 对象UserService userService = new UserService();// 创建 Logger 对象Logger logger = new ConsoleLogger();// 通过 LoggerInjector 接口注入 Logger 对象((LoggerInjector) userService).injectLogger(logger);// 使用 UserServiceuserService.createUser("Alice");}
}
  • 类型转换
    • userService 是一个 UserService 类的实例。
    • UserService 类实现了 LoggerInjector 接口,因此 userService 也可以被视为 LoggerInjector 类型的对象。
    • 通过 ((LoggerInjector) userService),我们将 userService 强制转换为 LoggerInjector 类型。
  • 调用注入方法
    • LoggerInjector 接口定义了一个 injectLogger 方法。
    • 通过类型转换后,我们可以调用 injectLogger 方法,将 Logger 对象注入到 UserService 中。

11.2 总结

依赖注入是一种强大的设计模式,通过外部提供依赖关系,使得代码更加灵活、可测试和可维护。

  • 降低耦合度:对象不再负责创建或查找其依赖,依赖关系由外部提供。
  • 提高可测试性:可以通过注入不同的依赖来测试对象的行为。
  • 提高可维护性:依赖关系明确,代码更易于理解和维护。

构造器注入、设值方法注入和接口注入是实现依赖注入的三种主要方式,每种方式都有其适用场景和优缺点。

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

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

相关文章

(长期更新)《零基础入门 ArcGIS(ArcMap) 》实验一(下)----空间数据的编辑与处理(超超超详细!!!)

续上篇博客&#xff08;长期更新&#xff09;《零基础入门 ArcGIS(ArcMap) 》实验一&#xff08;上&#xff09;----空间数据的编辑与处理&#xff08;超超超详细&#xff01;&#xff01;&#xff01;&#xff09;-CSDN博客 继续更新 本篇博客内容为道路拓扑检查与修正&#x…

Unity3D 完整直升机控制器(虚拟仿真级别)

采用了MVC框架&#xff0c;以四轴驱动的方式对直升机的启动、飞行做了仿真模拟&#xff0c;包括但不限于参数设置、启动发动机和旋翼、数据显示、HUD、UI、升降、水平移动、转弯等。 文末有完整的工程资源链接。 1.旋翼 直升机飞行过程中&#xff0c;有顶部的主旋翼和尾部的尾…

yum工具的学习

Linux下安装软件的方法 1.源代码安装 2.rmp包安装 3.包管理器进行安装 --- yum/apt Linux下载软件的过程 操作系统的好坏评估 -- 生态问题 yum具体操作 Linux软件安装所有人都能用&#xff0c;是以other的身份去执行可执行程序 文件拷贝&#xff08;sudo&#xff09;-- &g…

Linux:进程的优先级 进程切换

文章目录 前言一、进程优先级1.1 基本概念1.2 查看系统进程1.3 PRI和NI1.4 调整优先级1.4.1 top命令1.4.2 nice命令1.4.3 renice命令 二、进程切换2.1 补充概念2.2 进程的运行和切换步骤&#xff08;重要&#xff09; 二、Linux2.6内核进程O(1)调度队列&#xff08;重要&#x…

MongoDB在现代Web开发中的应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 MongoDB在现代Web开发中的应用 MongoDB在现代Web开发中的应用 MongoDB在现代Web开发中的应用 引言 MongoDB 概述 定义与原理 发展…

爬取链家二手房房价数据存入mongodb并进行分析

感谢您的关注&#xff01;需要完整源码评论区获取~ 【实验目的】 1. 使用 python 将爬虫数据存入 mongodb&#xff1b; 2. 使用 python 读取 mongodb 数据并进行可视化分析。 【实验原理】 MongoDB 是文档数据库&#xff0c;采用 BSON 的结构来存储数据。在文档中可嵌套其…

Solana 区块链的技术解析及未来展望 #dapp开发#公链搭建

随着区块链技术的不断发展和应用场景的扩展&#xff0c;性能和可拓展性成为各大公链竞争的关键因素。Solana&#xff08;SOL&#xff09;因其高吞吐量、低延迟和低成本的技术特性&#xff0c;在众多区块链项目中脱颖而出&#xff0c;被誉为“以太坊杀手”之一。本文将从技术层面…

Vue通过file控件上传文件到Node服务器

功能&#xff1a; 多文件同步上传、拖动上传、实时上传进度条、上传前的删除文件、原生file控件的美化 搁置的功能: 取消上传(上传过程中取消,即取消网络请求abort)、上传文件夹、大文件切片、以及很多限制条件未处理(重复上传、文件格式。。。) bug: 文件总大小(。。。竟然从d…

Element-ui Select选择器自定义搜索方法

效果图 具体实现 <template><div class"home"><el-selectref"currencySelect"v-model"currency"filterable:spellcheck"false"placeholder"请选择":filter-method"handleCurrencyFilter"change&q…

JS的学习与使用

JS的学习与使用 一 什么是Javascript&#xff1f; Javascript是一门跨平台&#xff0c;面向对象的脚本语言&#xff0c;是用来控制网页行为的&#xff0c;它能使网页可以交互 java与Javascript是完全不同的语言&#xff0c;不论是概念还是设计&#xff0c;但是基础语法类似 E…

Docker:查看镜像里的文件

目录 背景步骤1、下载所需要的docker镜像2、创建并运行临时容器3、停止并删除临时容器 背景 在开发过程中&#xff0c;为了更好的理解和开发程序&#xff0c;有时需要确认镜像里的文件是否符合预期&#xff0c;这时就需要查看镜像内容 步骤 1、下载所需要的docker镜像 可以使…

【网络安全 | 漏洞挖掘】通过密码重置污染实现账户接管

未经许可,不得转载。 文章目录 密码重置污染攻击漏洞挖掘的过程目标选择与初步测试绕过 Cloudflare 的尝试发现两个域名利用 Origin 头部污染实现账户接管攻击流程总结在今天的文章中,我们将深入探讨一种 账户接管 漏洞,并详细分析如何绕过 Cloudflare 的保护机制,利用密码…

Redis 5 种基本数据类型详解

Redis 共有 5 种基本数据类型&#xff1a;String&#xff08;字符串&#xff09;、List&#xff08;列表&#xff09;、Set&#xff08;集合&#xff09;、Hash&#xff08;散列&#xff09;、Zset&#xff08;有序集合&#xff09;。 这 5 种数据类型是直接提供给用户使用的&…

AI 提示词(Prompt)入门 十:最佳实践|详细询问,提供细节!

1、原则解释 当与 ChatGPT 交流时&#xff0c;提供具体和详细的信息非常重要。 这样做可以帮助 ChatGPT 更准确地理解你的需求和上下文&#xff0c;从而生成更相关和有用的回答 明确的信息可以包括具体的问题背景、相关领域的说明、你所期望的答案类型等。 2、如何实践 明…

数据库的隔离机制---对MySQL 默认隔离级别的理解

参考&#xff1a; 脏读、幻读和不可重复读_脏读 ​​​​​​ 全网最详细MVCC讲解&#xff0c;一篇看懂 - 知乎全网最详细MVCC讲解&#xff0c;一篇看懂 - 知乎 面试官&#xff1a;MySQL 的默认隔离级别是什么?可以解决幻读问题吗&#xff1f; 目录 一、脏读、幻读、不可…

UNI-APP小程序答题功能开发(左右滑动,判断,填空,问答,答题卡,纠错,做题倒计时等)

原博&#xff1a;uni-app小程序答题功能开发(左右滑动,判断,填空,问答,答题卡,纠错,做题倒计时等)_uniapp答题模板-CSDN博客 标签&#xff1a; 小程序 uni-app 模板链接:答题模板 html部分 这里没啥好说的,就是根据不同的状态显示不同的内容 <template><view>…

SpringBoot多环境配置的实现

前言 开发过程中必然使用到的多环境案例&#xff0c;通过简单的案例分析多环境配置的实现过程。 一、案例 1.1主配置文件 spring:profiles:active: prod server:port: 80801.2多环境配置文件 开发环境 blog:domain: http://localhost:8080测试环境 blog:domain: https:/…

另外一种缓冲式图片组件的用法

文章目录 1. 概念介绍2. 使用方法2.1 基本用法2.2 缓冲原理3. 示例代码4. 内容总结我们在上一章回中介绍了"FadeInImage组件"相关的内容,本章回中将介绍CachedNetworkImage组件.闲话休提,让我们一起Talk Flutter吧。 1. 概念介绍 我们在本章回中介绍的CachedNetwo…

Linux下多线程

在Linux下的底层里并没有多线程这个概念&#xff0c;取而代之的是轻量级进程的概念。应为在Llinu下内核下并没有TCB,而只有PCB。 线程是什么 在⼀个程序⾥的⼀个执⾏路线就叫做线程&#xff08;thread&#xff09;。更准确的定义是&#xff1a;线程是“⼀个进程内部 的控制序…

Win10/11 安装使用 Neo4j Community Edition

如果你下载的是 Neo4j Community Edition 的压缩包&#xff0c;意味着你需要手动解压并配置 Neo4j。以下是详细的使用步骤&#xff1a; 0. 下载压缩包 访问Neo4j官网&#xff0c;找到 Community Edition 版本并选择 4.x 或者 5.x 下载&#xff1a;https://neo4j.com/deployme…