【设计模式】行为型模式(五):解释器模式、访问者模式、依赖注入
《设计模式之行为型模式》系列,共包含以下文章:
- 行为型模式(一):模板方法模式、观察者模式
- 行为型模式(二):策略模式、命令模式
- 行为型模式(三):责任链模式、状态模式
- 行为型模式(四):备忘录模式、中介者模式
- 行为型模式(五):解释器模式、访问者模式、依赖注入
😊 如果您觉得这篇文章有用 ✔️ 的话,请给博主一个一键三连 🚀🚀🚀 吧 (点赞 🧡、关注 💛、收藏 💚)!!!您的支持 💖💖💖 将激励 🔥 博主输出更多优质内容!!!
行为型模式(五):解释器模式、访问者模式
- 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
对象。- 调用
AddExpression
的interpret
方法:left.interpret()
调用NumberExpression(1)
的interpret
方法,返回 1。right.interpret()
调用MultiplyExpression(2, 3)
的interpret
方法:left.interpret()
调用NumberExpression(2)
的interpret
方法,返回 2。right.interpret()
调用NumberExpression(3)
的interpret
方法,返回 3。MultiplyExpression
的interpret
方法返回 2 * 3 = 6。
AddExpression
的interpret
方法返回 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 Control
,IoC
)。它的主要目的是减少代码之间的耦合,提高代码的可测试性和可维护性。通过依赖注入,对象的依赖关系由外部提供,而不是由对象自己创建或查找。
11.1 代码示例
假设有一个 Logger
接口和两个实现类 FileLogger
和 ConsoleLogger
,以及一个需要日志功能的 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 总结
依赖注入是一种强大的设计模式,通过外部提供依赖关系,使得代码更加灵活、可测试和可维护。
- 降低耦合度:对象不再负责创建或查找其依赖,依赖关系由外部提供。
- 提高可测试性:可以通过注入不同的依赖来测试对象的行为。
- 提高可维护性:依赖关系明确,代码更易于理解和维护。
构造器注入、设值方法注入和接口注入是实现依赖注入的三种主要方式,每种方式都有其适用场景和优缺点。
相关文章:

【设计模式】行为型模式(五):解释器模式、访问者模式、依赖注入
《设计模式之行为型模式》系列,共包含以下文章: 行为型模式(一):模板方法模式、观察者模式行为型模式(二):策略模式、命令模式行为型模式(三):责…...

使用nossl模式连接MySQL数据库详解
使用nossl模式连接MySQL数据库详解 摘要一、引言二、nossl模式概述2.1 SSL与nossl模式的区别2.2 选择nossl模式的场景三、在nossl模式下连接MySQL数据库3.1 准备工作3.2 C++代码示例3.3 代码详解3.3.1 初始化MySQL连接对象3.3.2 连接到MySQL数据库3.3.3 执行查询操作3.3.4 处理…...

【MySQL】ubantu 系统 MySQL的安装与免密码登录的配置
🍑个人主页:Jupiter. 🚀 所属专栏:MySQL初阶探索:构建数据库基础 欢迎大家点赞收藏评论😊 目录 📚mysql的安装📕MySQL的登录🌏MySQL配置免密码登录 📚mysql的…...

高级 SQL 技巧讲解
大家好,我是程序员小羊! 前言: SQL(结构化查询语言)是管理和操作数据库的核心工具。从基本的查询语句到复杂的数据处理,掌握高级 SQL 技巧不仅能显著提高数据分析的效率,还能解决业务中的复…...

浅论AI大模型在电商行业的发展未来
随着人工智能(AI)技术的快速发展,AI大模型在电商行业中扮演着越来越重要的角色。本文旨在探讨AI大模型如何赋能电商行业,包括提升销售效率、优化用户体验、增强供应链管理等方面。通过分析AI大模型在电商领域的应用案例和技术进展…...

【python笔记03】《类》
文章目录 面向对象基本概念对象的概念类的概念 类的定义类的创建(实例的模板)类的实例化--获取对象对象方法中的self关键字面试题请描述什么是对象,什么是类。请观阅读如下代码,判断是否能正常运行,如果不能正常运行&a…...

Flutter 应用在真机上调试的流程
在真机上调试 Flutter 应用的方法有很多,可以使用 USB 数据线连接设备到电脑进行调试,也可以通过无线方式进行 Flutter 真机调试。 1. 有线调试 设备准备 启用开发者模式: Android:进入 设置 > 关于手机,连续点击…...

以太坊基础知识结构详解
以太坊的历史和发展 初创阶段 2013年:Vitalik Buterin 发表了以太坊白皮书,提出了一个通用的区块链平台,不仅支持比特币的货币功能,还能支持更复杂的智能合约。2014年:以太坊项目启动,进行了首次ICO&…...

安全见闻(完整版)
目录 安全见闻1 编程语言和程序 编程语言 函数式编程语言: 数据科学和机器学习领域: Web 全栈开发: 移动开发: 嵌入式系统开发: 其他: 编程语言的方向: 软件程序 操作系统 硬件设备…...

LeetCode100之反转链表(206)--Java
1.问题描述 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表 示例1 输入:head [1,2,3,4,5] 输出:[5,4,3,2,1] 示例2 输入:head [1,2] 输出:[2,1] 示例3 输入:head [] 输…...

牛客周赛第一题2024/11/17日
链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网 时间限制:C/C/Rust/Pascal 1秒,其他语言2秒 空间限制:C/C/Rust/Pascal 256 M,其他语言512 M 64bit IO Format: %lld 题目描述 小红这天来到了三…...

麒麟Server下安装东方通TongLINK/Q
环境 系统:麒麟Server SP3 2403 应用:TLQ8.1(Install_TLQ_Standard_Linux2.6.32_x86_64_8.1.17.0.tar.gz) 安装Server 将文件解压到/usr/local/tlq。 cd /opt/tlq/ mkdir /usr/local/tlq/ tar -zxvf Install_TLQ_Standard_Linux2.6.32_x86_64_8.1.1…...

BERT的中文问答系统33
我们在现有的代码基础上增加网络搜索的功能。我们使用 requests 和 BeautifulSoup 来从百度搜索结果中提取信息。以下是完整的代码,包括项目结构、README.md 文件以及所有必要的代码。 项目结构 xihe241117/ ├── data/ │ └── train_data.jsonl ├── lo…...

Ubuntu下的Eigen库的安装及基本使用教程
一、Eigen库介绍 简介 Eigen [1]目前最新的版本是3.4,除了C标准库以外,不需要任何其他的依赖包。Eigen使用的CMake建立配置文件和单元测试,并自动安装。如果使用Eigen库,只需包特定模块的的头文件即可。 基本功能 Eigen适用范…...

【spring 】Spring Cloud Gateway 的Filter学习
介绍和使用场景 Spring Cloud Gateway 是一个基于 Spring Framework 5 和 Project Reactor 的 API 网关,它旨在为微服务架构提供一种简单而有效的方式来处理请求路由、过滤、限流等功能。在 Spring Cloud Gateway 中,Filter 扮演着非常重要的角色&#…...

每秒交易数(Transactions Per Second:TPS)详细拆解
每秒交易数(TPS)是指计算机网络每秒可以处理的交易数量。TPS是衡量不同区块链和其他计算机系统速度的关键指标。然而,TPS并不是用来衡量区块链速度的唯一指标。许多人认为,虽然TPS很重要,但最终性实际上是一个更重要的…...

【初阶数据结构与算法】链表刷题之链表分割、相交链表、环形链表1、环形链表I、环形链表II
文章目录 一、链表分割二、相交链表三、环形链表I四、环形链表|| 一、链表分割 题目链接:https://www.nowcoder.com/practice/0e27e0b064de4eacac178676ef9c9d70 我们来看看链表分割的题目描述和它给出的函数: 这个题虽然是以C形式来做࿰…...

【STL】set,multiset,map,multimap的介绍以及使用
关联式容器 在C的STL中包含序列式容器和关联式容器 1.关联式容器:它里面存储的是元素本身,其底层是线性序列的数据结构,比如:vector,list,deque,forward_list(C11)等 2.关联式容器里面储存的…...

新能源二手车交易量有望破百万,二手车市场回暖了吗?
这些年,伴随着新能源汽车市场的高速发展,各种新能源车的二手车也在逐渐增加,不过之前的二手车市场相对比较冷清,就在最近一则新闻传出新能源二手车交易量有望破百万,二手车市场这是回暖了吗? 一、新能源二手…...

哈佛商业评论 | 项目经济的到来:组织变革与管理革新的关键
在21世纪,项目经济(Project Economy)逐步取代传统运营,成为全球经济增长的核心动力。项目已不再是辅助工具,而是推动创新和变革的重要载体。然而,只有35%的项目能够成功,显示出项目管理领域存在巨大的改进空间。本文将详细探讨项目经济的背景、项目管理的挑战,以及适应…...

web浏览器环境下使用window.open()打开PDF文件不是预览,而是下载文件?
如果你使用 window.open() 方法打开 PDF 文件,但浏览器不是预览而是下载文件,这可能是由于以下几个原因: 服务器配置:服务器可能将 PDF 文件配置为下载而不是预览。例如,服务器可能设置了 Content-Disposition 响应头…...

【GeekBand】C++设计模式笔记12_Singleton_单件模式
1. “对象性能” 模式 面向对象很好地解决了 “抽象” 的问题, 但是必不可免地要付出一定的代价。对于通常情况来讲,面向对象的成本大都可以忽略不计。但是某些情况,面向对象所带来的成本必须谨慎处理。典型模式 SingletonFlyweight 2. Si…...

Pyhon基础数据结构(列表)【蓝桥杯】
a [1,2,3,4,5] a.reverse() print("a ",a) a.reverse() print("a ",a)# 列表 列表(list)有由一系列按照特定顺序排序的元素组成 列表是有顺序的,访问任何元素需要通过“下标访问” 所谓“下标”就是指元素在列表从左…...

Linux篇(权限管理命令)
目录 一、权限概述 1. 什么是权限 2. 为什么要设置权限 3. Linux中的权限类别 4. Linux中文件所有者 4.1. 所有者分类 4.2. 所有者的表示方法 属主权限 属组权限 其他权限 root用户(超级管理员) 二、普通权限管理 1. ls查看文件权限 2. 文件…...

深入理解 Spark 中的 Shuffle
Spark 的介绍与搭建:从理论到实践_spark环境搭建-CSDN博客 Spark 的Standalone集群环境安装与测试-CSDN博客 PySpark 本地开发环境搭建与实践-CSDN博客 Spark 程序开发与提交:本地与集群模式全解析-CSDN博客 Spark on YARN:Spark集群模式…...

leetcode-8-字符串转整数
题解: 代码:...

SQL注入注入方式(大纲)
SQL注入注入方式(大纲) 常规注入 通常没有任何过滤,直接把参数存放到SQL语句中。 宽字节注入 GBK 编码 两个字节表示一个字符ASCII 编码 一个字节表示一个字符MYSQL默认字节集是GBK等宽字节字符集 原理: 设置MySQL时错误配置…...

OpenCV基础(1)
1.图像读写与窗口显示 1.1.imread读取图像文件 Mat cv::imread(const string &filename,int flags IMREAD_COLOR); filename:要读取的图像文件名flags:读取模式,可以从枚举cv::ImreadModes中取值,默认取值是IMREAD_COLOR&am…...

【freertos】FreeRTOS信号量的介绍及使用
FreeRTOS信号量 一、概述二、PV原语三、函数接口1.创建一个计数信号量2.删除一个信号量3.信号量释放4.在中断释放信号量5.获取一个信号量,可以是二值信号量、计数信号量、互斥量。6.在中断获取一个信号量,可以是二值信号量、计数信号量7.创建一个二值信号…...

React Native 全栈开发实战班 - 图片加载与优化
在移动应用中,图片加载与优化 是提升用户体验和减少资源消耗的重要环节。图片加载不当可能导致应用卡顿、内存泄漏甚至崩溃。本章节将介绍 React Native 中常用的图片加载方法,包括 Image 组件的使用、第三方图片加载库(如 react-native-fast…...