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

news2024/11/19 0:40:32

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

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

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

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

  • 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 总结

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

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Java Springboot二手家电管理平台

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 数据…

利用SSH中的弱私钥

import paramiko import argparse import os from threading import Thread, BoundedSemaphore # 设置最大连接数 maxConnections 5 # 创建一个有界信号量&#xff0c;用于控制同时进行的连接数 connection_lock BoundedSemaphore(valuemaxConnections) # 用于控制是否停止所…

基于Python的仓库管理系统设计与实现

背景&#xff1a; 基于Python的仓库管理系统功能介绍 本仓库管理系统采用Python语言开发&#xff0c;利用Django框架和MySQL数据库&#xff0c;实现了高效、便捷的仓库管理功能。 用户管理&#xff1a; 支持员工和管理员角色的管理。 用户注册、登录和权限分配功能&#x…

自动驾驶系列—从数据采集到存储:解密自动驾驶传感器数据采集盒子的关键技术

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

vue3【实战】切换白天黑夜(暗黑模式)【组件封装】DarkMode.vue

效果预览 原理解析 切换为暗黑模式时&#xff0c;会在 html 标签上添加样式类 dark导入 ElementPlus 的暗黑模式样式后&#xff0c; ElementPlus 组件会自动响应暗黑模式自定义组件需用 UnoCSS 的 dark: 语法自定义暗黑模式的样式 代码实现 技术方案 vue3 vite ElementPlus …

java八股-jvm入门-程序计数器,堆,元空间,虚拟机栈,本地方法栈,类加载器,双亲委派,类加载执行过程

文章目录 PC Register堆虚拟机栈方法区(Metaspace元空间双亲委派机制类加载器 类装载的执行过程 PC Register 程序计数器&#xff08;Program Counter Register&#xff09;是 Java 虚拟机&#xff08;JVM&#xff09;中的一个组件&#xff0c;它在 JVM 的内存模型中扮演着非常…

Nature Communications 基于触觉手套的深度学习驱动视触觉动态重建方案

在人形机器人操作领域&#xff0c;有一个极具价值的问题&#xff1a;鉴于操作数据在人形操作技能学习中的重要性&#xff0c;如何有效地从现实世界中获取操作数据的完整状态&#xff1f;如果可以&#xff0c;那考虑到人类庞大规模的人口和进行复杂操作的简单直观性与可扩展性&a…

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…

鸿蒙学习生态应用开发能力全景图-赋能套件(1)

文章目录 赋能套件鸿蒙生态应用开发能力全景图 赋能套件 鸿蒙生态白皮书: 全面阐释了鸿蒙生态下应用开发核心理念、关键能力以及创新体验,旨在帮助开发者快速、准确、全面的了解鸿蒙开发套件给开发者提供的能力全景和未来的愿景。 视频课程: 基于真实的开发场景,提供向导式…

Docker 的安装与使用

Docker 的安装 Docker 是一个开源的商业产品&#xff0c;有两个版本&#xff1a;社区版&#xff08;Community Edition&#xff0c;缩写为 CE&#xff09;和企业版&#xff08;Enterprise Edition&#xff0c;缩写为 EE&#xff09;。 Docker CE 的安装请参考官方文档&#xf…

MATLAB向量元素的引用

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

力扣周赛:第424场周赛

&#x1f468;‍&#x1f393;作者简介&#xff1a;爱好技术和算法的研究生 &#x1f30c;上期文章&#xff1a;力扣周赛&#xff1a;第422场周赛 &#x1f4da;订阅专栏&#xff1a;力扣周赛 希望文章对你们有所帮助 第一道题模拟题&#xff0c;第二道题经典拆分数组/线段树都…

SpringBoot配置相关的内容

依赖Starter和Parent 查依赖坐标网站&#xff1a;Maven Repository: Search/Browse/Explorehttps://mvnrepository.com/ 设置配置文件 配置文件相关的配置 yml多个数据的书写 配置文件的读取

第九部分 :1.STM32之通信接口《精讲》(USART,I2C,SPI,CAN,USB)

本芯片使用的是STM32F103C8T6型号 STM32F103C8T6是STM32F1系列中的一种较常用的低成本ARM Cortex-M3内核MCU&#xff0c;具有丰富的通信接口&#xff0c;包括USART、SPI、I2C等。下面是该芯片上通信接口的管脚分布、每个接口的工作模式、常用应用场景和注意事项。 1. USART (通…

【计算机网络】协议定制

一、结构化数据传输流程 这里涉及协议定制、序列化/反序列化的知识 对于序列化和反序列化&#xff0c;有现成的解决方案&#xff1a;①json ②probuff ③xml 二、理解发送接收函数 我们调用的所有发送/接收函数&#xff0c;根本就不是把数据发送到网络中&#xff01;本质都是…

WebRTC视频 02 - 视频采集类 VideoCaptureModule

WebRTC视频 01 - 视频采集整体架构 WebRTC视频 02 - 视频采集类 VideoCaptureModule&#xff08;本文&#xff09; WebRTC视频 03 - 视频采集类 VideoCaptureDS 上篇 WebRTC视频 04 - 视频采集类 VideoCaptureDS 中篇 WebRTC视频 05 - 视频采集类 VideoCaptureDS 下篇 一、前言…

解决Windows远程桌面 “为安全考虑,已锁定该用户账户,原因是登录尝试或密码更改尝试过多。请稍后片刻再重试,或与系统管理员或技术支持联系“问题

当我们远程连接服务器连接不上并提示“为安全考虑&#xff0c;已锁定该用户账户&#xff0c;原因是登录尝试或密码更改尝试过多。请稍候片刻再重试&#xff0c;或与系统管理员或技术支持联系”时&#xff0c;根本原因是当前计算机远程连接时输入了过多的错误密码&#xff0c;触…

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

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

Sping全面复习

Spring框架是一个功能强大且广泛使用的Java平台&#xff0c;它通过提供全面的基础设施支持&#xff0c;使得开发人员能够轻松构建高效、可移植、易于测试的代码。Spring的核心特性包括依赖注入&#xff08;DI&#xff09;、面向切面编程&#xff08;AOP&#xff09;和事件驱动模…

深挖C++赋值

详解赋值 const int a 10; int b a;&a 0x000000b7c6afef34 {56496} &a 0x000000b7c6afef34 {10} 3. &b 0x000000b7c6afef54 {10} 总结&#xff1a; int a 10 是指在内存中&#xff08;栈&#xff09;中创建一个int &#xff08;4 byte&#xff09;大小的空间…