【设计模式】行为型模式(五):解释器模式、访问者模式、依赖注入
《设计模式之行为型模式》系列,共包含以下文章:
- 行为型模式(一):模板方法模式、观察者模式
- 行为型模式(二):策略模式、命令模式
- 行为型模式(三):责任链模式、状态模式
- 行为型模式(四):备忘录模式、中介者模式
- 行为型模式(五):解释器模式、访问者模式、依赖注入
😊 如果您觉得这篇文章有用 ✔️ 的话,请给博主一个一键三连 🚀🚀🚀 吧 (点赞 🧡、关注 💛、收藏 💚)!!!您的支持 💖💖💖 将激励 🔥 博主输出更多优质内容!!!
行为型模式(五):解释器模式、访问者模式
- 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%的项目能够成功,显示出项目管理领域存在巨大的改进空间。本文将详细探讨项目经济的背景、项目管理的挑战,以及适应…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...

前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
C++中string流知识详解和示例
一、概览与类体系 C 提供三种基于内存字符串的流,定义在 <sstream> 中: std::istringstream:输入流,从已有字符串中读取并解析。std::ostringstream:输出流,向内部缓冲区写入内容,最终取…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...

NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合
在汽车智能化的汹涌浪潮中,车辆不再仅仅是传统的交通工具,而是逐步演变为高度智能的移动终端。这一转变的核心支撑,来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒(T-Box)方案:NXP S32K146 与…...

springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...

uniapp手机号一键登录保姆级教程(包含前端和后端)
目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号(第三种)后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...