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

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

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

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

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

  • 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;
    }

    @Override
    public 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;
    }

    @Override
    public 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;
    }

    @Override
    public 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;
    }

    @Override
    public 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;
    }

    @Override
    public 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;

    @Override
    public void visit(Paragraph paragraph) {
        String[] words = paragraph.getText().split("\\s+");
        wordCount += words.length;
    }

    @Override
    public void visit(Image image) {
        // 图片不增加字数
    }

    public int getWordCount() {
        return wordCount;
    }
}

class ThumbnailGeneratorVisitor implements Visitor {
    @Override
    public void visit(Paragraph paragraph) {
        // 文本段落不需要生成缩略图
    }

    @Override
    public 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 {
    @Override
    public void log(String message) {
        System.out.println("File: " + message);
    }
}

// ConsoleLogger 实现
class ConsoleLogger implements Logger {
    @Override
    public 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 {
    @Override
    public void log(String message) {
        System.out.println("File: " + message);
    }
}

// ConsoleLogger 实现
class ConsoleLogger implements Logger {
    @Override
    public 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 {
    @Override
    public void log(String message) {
        System.out.println("File: " + message);
    }
}

// ConsoleLogger 实现
class ConsoleLogger implements Logger {
    @Override
    public 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;

    @Override
    public 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);

        // 使用 UserService
        userService.createUser("Alice");
    }
}
  • 类型转换
    • userService 是一个 UserService 类的实例。
    • UserService 类实现了 LoggerInjector 接口,因此 userService 也可以被视为 LoggerInjector 类型的对象。
    • 通过 ((LoggerInjector) userService),我们将 userService 强制转换为 LoggerInjector 类型。
  • 调用注入方法
    • LoggerInjector 接口定义了一个 injectLogger 方法。
    • 通过类型转换后,我们可以调用 injectLogger 方法,将 Logger 对象注入到 UserService 中。

11.2 总结

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

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

.NET 9.0 中 System.Text.Json 的全面使用指南

以下是一些 System.Text.Json 在 .NET 9.0 中的使用方式&#xff0c;包括序列化、反序列化、配置选项等&#xff0c;并附上输出结果。 基本序列化和反序列化 using System; using System.Text.Json; public class Program {public class Person{public string Name { get; se…

《InsCode AI IDE:编程新时代的引领者》

《InsCode AI IDE&#xff1a;编程新时代的引领者》 一、InsCode AI IDE 的诞生与亮相二、独特功能与优势&#xff08;一&#xff09;智能编程体验&#xff08;二&#xff09;多语言支持与功能迭代 三、实际应用与案例&#xff08;一&#xff09;游戏开发案例&#xff08;二&am…

优选算法 - 5 ( 栈 队列 + 宽搜 优先级队列 9000 字详解 )

一&#xff1a;栈 1.1 删除字符串中的所有相邻重复项 题目链接&#xff1a;删除字符串中的所有相邻重复项 class Solution {public String removeDuplicates(String _s) {// 用 StringBuffer 模拟一下栈结构StringBuffer ret new StringBuffer();// 接着把 _s 转换为字符数组…

【linux012】文件操作命令篇 - more 命令

文章目录 more 命令1、基本用法2、常见选项3、交互式键盘命令4、举例5、注意事项 more 命令 more 是 Linux 中的一个分页查看命令&#xff0c;用于逐屏显示文件内容。它特别适合用于查看较长的文件&#xff0c;与 cat 不同&#xff0c;more 不会一次性输出所有内容&#xff0c…

企业BI工具如何选择?主流5款BI工具多维对比

数据大爆炸时代&#xff0c;企业数据爆发式增长&#xff0c;来自产品、运营、价值链以及外部的数据都成指数级增长趋势。利用大数据分析实现精细化运营&#xff0c;驱动业务增长是企业的理想蓝图。而BI工具能够整合、分析并可视化复杂的数据集&#xff0c;帮助管理层和决策者快…

Qt 5.6.3 手动配置 mingw 环境

- 安装 qt 5.6.3 mingw 版 - 打开 qt creator - 找到选项 工具 - 选项- 构建和运行 - 找到 “编译器” 选项卡 ,点击 "添加" “编译器路径” 设置为 qt 安装目录下&#xff0c; tool 文件夹内的 g.exe 设置完成后&#xff0c;点击 "apply" ,使选项生…

linux使用scp和密钥在不同服务器传输文件

将源服务密钥中公钥&#xff08;以pub结尾的&#xff09;复制或拷贝密文&#xff0c;粘贴到目标服务器中的/root/.ssh/authorized_keys文件中&#xff1b; 测试连接&#xff1a;ssh -p2129 root172.129.162.537&#xff0c;如果使用默认端口22 -p参数可省略&#xff0c;注意这…

德克萨斯扑克(德扑)笔记

文章目录 比牌方法(大小)发牌下注位置一些牌面的简称QT是什么意思89s是什么意思AT是什么意思ATs是什么意思 89o是什么意思 其他术语Action 叫注/说话 - 一个玩家的决定Betting Rounds 押注圈其他术语 团建或和小伙伴聚会的时候经常玩德扑&#xff0c;一是凑手&#xff0c;二是聚…

[ 网络安全介绍 5 ] 为什么要学习网络安全?

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 &#x1f389;点赞➕评论➕收藏 养成习…

C++: string(二)

✨✨ 欢迎大家来到我的文章✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 分类专栏&#xff1a;c 我的主页&#xff1a;tyler s blog 文章目录 一 string的成员函数1 insert2 resize3assign4erase5replace6 find(1) find(2)rfind…

鸿蒙应用权限控制与位置服务(Location Kit)

11_11日学习笔记 文章目录 [toc] 一、应用权限管控授权方式分类&#xff1a;1、[system_grant&#xff08;系统授权&#xff09;](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/permissions-for-all-V5#system_grant系统授权权限列表)2、[user_grant&…

Ubuntu 18 EDK2 环境编译

视频&#xff1a;在全新的Ubuntu上从零搭建UEFI的EDK2开发环境 开始&#xff1a;git clone https://github.com/tianocore/edk2.git 开始编译BaseTools前先更新一下子模块&#xff1a;git submodule update --init &#xff0c;然后&#xff1a;make -C BaseTools/ 问题1&a…

网络安全SQL初步注入2

六.报错注入 mysql函数 updatexml(1,xpath语法,0) xpath语法常用concat拼接 例如: concat(07e,(查询语句),07e) select table_name from information_schema.tables limit 0,1 七.宽字节注入(如果后台数据库的编码为GBK) url编码:为了防止提交的数据和url中的一些有特殊意…

Git 搭建远程仓库、在 IDEA 工具中的配置和使用

Git的概念、安装、操作与分支管理和图形化界面TortoiseGit&#xff08;小乌龟 &#xff09;的安装与使用-CSDN博客 目录 一、远程仓库 1&#xff09;在github上创建仓库 2&#xff09;在gitee上创建项目 3&#xff09;如何将远程的项目clone 到本地 4&#xff09;公司自己…

自然语言处理技术之细粒度实体识别

细粒度实体识别(Fine-Grained Named Entity Recognition, FG-NER) 1. 概述 细粒度实体识别是自然语言处理(NLP)领域中的一个重要研究方向,其目标是从文本中识别出更加具体和详细的实体类型。相比于传统的实体识别(NER),细粒度实体识别不仅关注常见的实体类别(如 人名…

k8s 1.28.2 集群部署 docker registry 接入 MinIO 存储

文章目录 [toc]docker registry 部署生成 htpasswd 文件生成 secret 文件 生成 registry 配置文件创建 service创建 statefulset创建 ingress验证 docker registry docker registry 监控docker registry ui docker registry dockerfile docker registry 配置文件 S3 storage dr…

为什么要使用Ansible实现Linux管理自动化?

自动化和Linux系统管理 多年来&#xff0c;大多数系统管理和基础架构管理都依赖于通过图形或命令行用户界面执行的手动任务。系统管理员通常使用清单、其他文档或记忆的例程来执行标准任务。 这种方法容易出错。系统管理员很容易跳过某个步骤或在某个步骤上犯错误。验证这些步…

一文了解Android的核心系统服务

在 Android 系统中&#xff0c;核心系统服务&#xff08;Core System Services&#xff09;是应用和系统功能正常运行的基石。它们负责提供系统级的资源和操作支持&#xff0c;包含了从启动设备、管理进程到提供应用基础组件的方方面面。以下是 Android 中一些重要的核心系统服…

MATLAB向量元素的引用

我们定义一个向量后&#xff0c;如果想引用的话&#xff0c;可以通过索引 i n d ind ind来实现。 注意&#xff1a;MATLAB中向量的开始索引是1&#xff0c;与许多编程语言不同。 例如&#xff1a; 如果想引用多个的话&#xff0c;可以用索引 i n d ind ind来提取多个位置 例如…

让空间计算触手可及,VR手套何以点石成金?

引言 如何让一位母亲与她去世的小女儿“重逢”&#xff1f;韩国MBC电视台《I Met You》节目实现了一个“不可能”心愿。 在空旷的绿幕中&#xff0c;母亲Jang Ji-sung透过VR头显&#xff0c;看到了三年前因白血病去世的女儿Nayeon。当她伸出双手&#xff0c;居然能摸到女儿的…