【访问者模式】设计模式系列:解锁复杂对象结构的秘密武器

news2024/11/24 20:55:26

文章目录

  • 访问者模式详解:理论与实践
    • 1. 引言
      • 1.1 访问者模式的历史背景
      • 1.2 模式的动机与应用场景
      • 1.3 为什么选择访问者模式
    • 2. 访问者模式概述
      • 2.1 定义
      • 2.2 问题场景
      • 2.3 模式结构
    • 3. 模式优缺点分析
      • 3.1 优点
      • 3.2 缺点
    • 4. 访问者模式实现步骤
      • 4.1 创建抽象元素接口
      • 4.2 实现具体元素类
      • 4.3 设计抽象访问者接口
      • 4.4 开发具体访问者类
      • 4.5 构建对象结构
      • 4.6 示例代码分析
    • 5. 案例研究
      • 5.1 应用场景一:解析XML文档
      • 5.2 应用场景二:代码分析器
    • 6. 扩展与变体
      • 6.1 多态访问者
      • 6.2 双重分派
      • 6.3 使用策略模式作为替代方案
      • 6.4 结合装饰者模式增强功能
    • 7. 相关模式比较
      • 7.1 访问者模式与策略模式
      • 7.2 访问者模式与迭代器模式
      • 7.3 访问者模式与其他结构型模式
    • 8. 最佳实践与建议
      • 8.1 何时使用访问者模式
      • 8.2 避免过度使用模式
      • 8.3 性能优化技巧
    • 9. 总结与展望

访问者模式详解:理论与实践

1. 引言

1.1 访问者模式的历史背景

访问者模式是设计模式中的一种,最初是在1990年代初由Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides四位软件工程师提出,并在他们的著作《设计模式:可复用面向对象软件的基础》(Design Patterns: Elements of Reusable Object-Oriented Software)中进行了详细介绍。这本书通常被称为“四人帮”或“Gang of Four”(GoF)的经典之作,书中定义了一系列的设计模式,其中包括访问者模式。

访问者模式的设计灵感来源于函数式编程语言中的高阶函数概念,即函数可以作为参数传递给其他函数。在面向对象编程中,这种思想被转化为对象可以被传递给其他对象执行特定的操作。

1.2 模式的动机与应用场景

访问者模式的主要动机是解决在对象结构上执行多种操作的问题,特别是在对象结构相对稳定而需要执行的操作频繁变化的情况下。例如,在图形编辑器中,不同的绘图对象(如直线、圆、矩形等)构成了一个复杂的对象结构,用户可能需要对这些对象执行各种操作,如绘制、打印、导出等。访问者模式允许我们轻松地增加新的操作而不修改现有对象结构中的类。

常见的应用场景包括:

  • 图形编辑器:处理复杂的图形对象结构。
  • 编译器和解释器:解析和处理抽象语法树。
  • 报表生成器:从数据集中生成不同类型的报告。
  • 代码分析工具:分析源代码并执行不同的检查或转换任务。

1.3 为什么选择访问者模式

访问者模式的一个主要优点是它使得我们可以遵循开放/封闭原则(Open/Closed Principle, OCP),即软件实体(类、模块、函数等)应该是可扩展的但不可修改的。这意味着我们可以向现有的系统中添加新的功能而不需要改变已有的代码。这对于维护大型系统来说尤其重要,因为频繁修改已存在的代码可能会引入新的错误。

此外,访问者模式还提供了清晰的分离关注点机制,将对象结构与作用于这些对象上的操作分离开来,使得系统更加灵活和易于维护。


2. 访问者模式概述

2.1 定义

访问者模式是一种行为设计模式,它允许我们为一组不同的对象结构定义一个新的操作,而无需修改这些对象的类。它通过将操作封装在一个访问者对象中,并让该对象访问每个对象结构成员来实现这一目标。

2.2 问题场景

假设我们有一个对象结构,其中包含不同类型的对象。对于这些对象,我们需要执行一些操作。随着系统的演进,可能会添加更多类型的操作,比如统计、打印、转换等。如果我们直接在每个对象类中添加新的操作,那么每当我们需要增加一个新操作时,就需要修改所有相关的对象类,这会导致代码变得难以管理和维护。

2.3 模式结构

访问者模式主要包括以下几个组成部分:

  • 抽象元素(Element):声明一个接受操作,它以一个访问者为参数。
  • 具体元素(ConcreteElement):实现抽象元素的接受操作。
  • 抽象访问者(Visitor):定义一个访问方法,它以一个具体的元素作为参数。这个方法对于每个具体元素都有一个版本。
  • 具体访问者(ConcreteVisitor):实现抽象访问者的方法,为每个具体元素提供相应的操作。
  • 对象结构(ObjectStructure):可以是一个集合或组合模式的实例,它包含多个元素对象,并提供一个接受访问者的操作。

在这里插入图片描述


3. 模式优缺点分析

3.1 优点

  • 易于增加新操作:当需要增加新的操作时,只需要添加新的访问者类即可,不需要修改现有的元素类。
  • 遵循OCP原则:访问者模式使得我们可以在不修改现有代码的基础上增加新的功能,符合开放/封闭原则。

3.2 缺点

  • 增加了对象结构的复杂性:为了支持访问者模式,对象结构需要额外的接受方法和一些额外的逻辑来处理访问者对象。
  • 违反了合成复用原则:访问者模式倾向于通过类继承来实现行为的扩展,而不是通过对象组合,这与合成复用原则相悖。
  • 可能导致系统性能下降:访问者模式需要进行额外的查找和间接调用,这可能会稍微影响到程序的运行效率。

4. 访问者模式实现步骤

4.1 创建抽象元素接口

首先,我们需要定义一个抽象元素接口,这个接口通常包含一个接受访问者的方法。这个方法的作用是调用访问者对象的相应访问方法。

public interface Element {
    void accept(Visitor visitor);
}

4.2 实现具体元素类

然后,我们需要实现具体的元素类,这些类代表了对象结构中的不同类型的对象。每个具体元素类都必须实现accept方法,该方法将具体的访问者对象作为参数。

public class ConcreteElementA implements Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

public class ConcreteElementB implements Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

4.3 设计抽象访问者接口

接下来,定义一个抽象访问者接口,该接口包含针对每个具体元素类的方法。这些方法通常被称为访问方法。

public interface Visitor {
    void visit(ConcreteElementA element);
    void visit(ConcreteElementB element);
}

4.4 开发具体访问者类

具体访问者类实现了抽象访问者接口中定义的所有访问方法。每个访问方法都会根据具体的元素执行特定的操作。

public class ConcreteVisitor1 implements Visitor {
    @Override
    public void visit(ConcreteElementA element) {
        System.out.println("ConcreteVisitor1 visiting ConcreteElementA");
    }

    @Override
    public void visit(ConcreteElementB element) {
        System.out.println("ConcreteVisitor1 visiting ConcreteElementB");
    }
}

public class ConcreteVisitor2 implements Visitor {
    @Override
    public void visit(ConcreteElementA element) {
        System.out.println("ConcreteVisitor2 visiting ConcreteElementA");
    }

    @Override
    public void visit(ConcreteElementB element) {
        System.out.println("ConcreteVisitor2 visiting ConcreteElementB");
    }
}

4.5 构建对象结构

对象结构通常是一个容器,它可以是列表、数组或其他集合形式,用于存储具体元素对象。对象结构还需要提供一个方法来接受访问者对象。

import java.util.ArrayList;
import java.util.List;

public class ObjectStructure {
    private List<Element> elements = new ArrayList<>();

    public void add(Element element) {
        elements.add(element);
    }

    public void remove(Element element) {
        elements.remove(element);
    }

    public void accept(Visitor visitor) {
        for (Element element : elements) {
            element.accept(visitor);
        }
    }
}

实现接受方法
接受方法已经在具体元素类中实现了,它负责调用访问者对象的适当方法。

4.6 示例代码分析

这里是一个简单的示例,展示如何使用访问者模式。

public class ClientCode {
    public static void main(String[] args) {
        ObjectStructure objectStructure = new ObjectStructure();
        objectStructure.add(new ConcreteElementA());
        objectStructure.add(new ConcreteElementB());

        System.out.println("Client: Executing the first visitor.");
        ConcreteVisitor1 visitor1 = new ConcreteVisitor1();
        objectStructure.accept(visitor1);

        System.out.println("\nClient: Executing the second visitor.");
        ConcreteVisitor2 visitor2 = new ConcreteVisitor2();
        objectStructure.accept(visitor2);
    }
}

5. 案例研究

5.1 应用场景一:解析XML文档

1. 问题描述
假设我们有一个XML文档,需要解析它并执行一些操作,例如提取数据或者验证格式。

2. 设计决策

  • 使用访问者模式来解析XML文档中的不同元素。
  • 每个XML元素都实现一个accept方法来接受访问者。
  • 不同的访问者类负责执行不同的操作。

3. 代码实现
这里简化了XML解析的过程,只展示了访问者模式的应用。

// XML元素抽象类
public abstract class XMLElement implements Element {
    // 其他XML元素属性...
}

// 具体XML元素
public class XMLElementA extends XMLElement {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

// 具体XML元素
public class XMLElementB extends XMLElement {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

// XML解析器
public class XMLParser extends ObjectStructure {
    // 添加XML元素...
}

// 数据提取访问者
public class DataExtractor implements Visitor {
    @Override
    public void visit(XMLElementA element) {
        // 提取A元素的数据
    }

    @Override
    public void visit(XMLElementB element) {
        // 提取B元素的数据
    }
}

// 格式验证访问者
public class FormatValidator implements Visitor {
    @Override
    public void visit(XMLElementA element) {
        // 验证A元素的格式
    }

    @Override
    public void visit(XMLElementB element) {
        // 验证B元素的格式
    }
}

5.2 应用场景二:代码分析器

1. 问题描述
开发一个代码分析工具,能够对源代码进行语法检查和性能分析。

2. 设计决策

  • 使用访问者模式来处理不同的代码结构。
  • 每种代码结构(如函数、循环、条件语句)都实现accept方法。
  • 不同的访问者类负责语法检查和性能分析。

3. 代码实现
这里同样简化了代码分析的过程。

// 代码结构抽象类
public abstract class CodeStructure implements Element {
    // 其他代码结构属性...
}

// 具体代码结构
public class Function extends CodeStructure {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

// 具体代码结构
public class Loop extends CodeStructure {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

// 代码结构容器
public class CodeAnalyzer extends ObjectStructure {
    // 添加代码结构...
}

// 语法检查访问者
public class SyntaxChecker implements Visitor {
    @Override
    public void visit(Function function) {
        // 检查函数语法
    }

    @Override
    public void visit(Loop loop) {
        // 检查循环语法
    }
}

// 性能分析访问者
public class PerformanceAnalyzer implements Visitor {
    @Override
    public void visit(Function function) {
        // 分析函数性能
    }

    @Override
    public void visit(Loop loop) {
        // 分析循环性能
    }
}

6. 扩展与变体

6.1 多态访问者

多态访问者是指在访问者模式中,访问者本身也可以是多态的。这意味着访问者对象可以是不同类型的访问者,它们可以有不同的行为。这种变体可以更好地适应那些需要根据不同上下文执行不同操作的情况。

示例代码:

public interface Visitor<T> {
    void visit(T element);
}

public class ConcreteVisitor1<T> implements Visitor<T> {
    @Override
    public void visit(T element) {
        if (element instanceof ConcreteElementA) {
            visit((ConcreteElementA) element);
        } else if (element instanceof ConcreteElementB) {
            visit((ConcreteElementB) element);
        }
    }

    void visit(ConcreteElementA element) {
        // Specific behavior for ConcreteElementA
    }

    void visit(ConcreteElementB element) {
        // Specific behavior for ConcreteElementB
    }
}

public class ConcreteVisitor2<T> implements Visitor<T> {
    @Override
    public void visit(T element) {
        if (element instanceof ConcreteElementA) {
            visit((ConcreteElementA) element);
        } else if (element instanceof ConcreteElementB) {
            visit((ConcreteElementB) element);
        }
    }

    void visit(ConcreteElementA element) {
        // Specific behavior for ConcreteElementA
    }

    void visit(ConcreteElementB element) {
        // Specific behavior for ConcreteElementB
    }
}

6.2 双重分派

双重分派是一种技术,它允许基于两个对象的类型来选择方法。在访问者模式中,双重分派通常指的是访问者对象和被访问的对象之间的分派。这种方法可以通过使用泛型和重载方法来实现。

示例代码:

public interface Visitor {
    <T> void visit(T element);
}

public class ConcreteVisitor1 implements Visitor {
    @Override
    public <T> void visit(T element) {
        if (element instanceof ConcreteElementA) {
            visit((ConcreteElementA) element);
        } else if (element instanceof ConcreteElementB) {
            visit((ConcreteElementB) element);
        }
    }

    void visit(ConcreteElementA element) {
        // Specific behavior for ConcreteElementA
    }

    void visit(ConcreteElementB element) {
        // Specific behavior for ConcreteElementB
    }
}

public class ConcreteElementA implements Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

public class ConcreteElementB implements Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

6.3 使用策略模式作为替代方案

策略模式允许算法独立于使用它的客户端。与访问者模式相比,策略模式更适合于那些操作并不依赖于对象结构的情况。在某些情况下,策略模式可以作为访问者模式的一个替代方案,特别是当操作与对象结构无关时。

示例代码:

public interface Strategy {
    void execute();
}

public class ConcreteStrategyA implements Strategy {
    @Override
    public void execute() {
        // Concrete strategy A implementation
    }
}

public class ConcreteStrategyB implements Strategy {
    @Override
    public void execute() {
        // Concrete strategy B implementation
    }
}

public class Context {
    private Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }

    public void doSomeBusinessLogic() {
        // ...
        strategy.execute();
    }
}

6.4 结合装饰者模式增强功能

装饰者模式可以用来动态地给对象添加职责,而无需修改对象本身的结构。结合访问者模式,装饰者模式可以用来增强对象的行为,例如在访问者访问对象之前或之后添加额外的功能。

示例代码:

public abstract class Decorator implements Element {
    protected Element element;

    public Decorator(Element element) {
        this.element = element;
    }

    @Override
    public void accept(Visitor visitor) {
        element.accept(visitor);
    }

    // Additional methods or logic
}

public class LoggingDecorator extends Decorator {
    public LoggingDecorator(Element element) {
        super(element);
    }

    @Override
    public void accept(Visitor visitor) {
        System.out.println("LoggingDecorator: Before visiting.");
        super.accept(visitor);
        System.out.println("LoggingDecorator: After visiting.");
    }
}

7. 相关模式比较

7.1 访问者模式与策略模式

  • 访问者模式适用于当需要在对象结构上执行多种操作,并且这些操作会频繁发生变化时。它将操作封装在访问者对象中,使得可以轻松地添加新的操作。
  • 策略模式则适用于当算法本身可能需要在运行时切换时。策略模式将算法封装在独立的对象中,允许客户端在运行时选择合适的算法。

7.2 访问者模式与迭代器模式

  • 访问者模式关注的是对对象结构中的元素执行操作,这些操作可以是任意类型的,并且可以随时间变化。
  • 迭代器模式则是为遍历集合提供一种方法,它关注的是如何访问集合中的元素,而不在意对这些元素做什么。

7.3 访问者模式与其他结构型模式

  • 适配器模式:适配器模式用于将一个类的接口转换成客户希望的另一个接口。访问者模式可以与适配器模式结合使用,以便在访问者访问不同类型的元素时进行适当的转换。
  • 桥接模式:桥接模式用于分离抽象和实现,使得二者可以独立变化。访问者模式可以与桥接模式结合使用,以提供不同的访问者实现。
  • 装饰者模式:装饰者模式用于动态地给对象添加职责。访问者模式可以与装饰者模式结合使用,以在访问元素前后添加额外的行为。

8. 最佳实践与建议

8.1 何时使用访问者模式

访问者模式适用于以下情况:

  • 当你需要在对象结构上执行多种操作,并且这些操作会频繁变化时。
  • 当对象结构相对稳定,而需要执行的操作却经常变化时。
  • 当你希望避免在对象结构中的每个类中添加新的行为时。
  • 当你需要对对象结构中的元素执行非本职的操作时。

8.2 避免过度使用模式

虽然访问者模式非常有用,但也存在一些潜在的问题,需要注意不要过度使用它:

  • 考虑替代方案:如果只有少数几种操作,并且这些操作不太可能变化,那么使用访问者模式可能过于复杂。在这种情况下,可以考虑使用简单的类扩展或其他设计模式。
  • 评估复杂度:访问者模式可能会增加系统的复杂度,特别是在对象结构很大或有很多不同类型的元素时。确保评估模式带来的复杂度是否值得。
  • 性能考量:访问者模式可能会导致性能下降,尤其是在需要频繁调用访问者的情况下。考虑是否有必要采用其他技术来提高性能。

8.3 性能优化技巧

  • 缓存结果:如果访问者模式中的操作涉及复杂的计算,可以考虑缓存计算结果以避免重复计算。
  • 减少间接调用:尽量减少不必要的间接调用,这可以通过在访问者对象中使用局部变量来实现。
  • 避免使用反射:尽管反射可以提供更大的灵活性,但它可能会显著降低性能。尽可能使用静态类型而不是反射。

9. 总结与展望

本篇文章要点回顾
本文详细介绍了访问者模式的概念、实现步骤、应用场景以及与其他模式的比较。我们探讨了访问者模式的优点,如易于增加新操作和遵循开放/封闭原则;同时也指出了其缺点,包括增加了对象结构的复杂性和可能导致的性能下降。此外,我们还讨论了访问者模式的一些扩展与变体,包括多态访问者、双重分派、使用策略模式作为替代方案以及结合装饰者模式增强功能。

访问者模式的应用前景
随着软件工程领域的不断发展,访问者模式的应用领域也在不断扩展。未来,访问者模式可能会在以下方面得到更多的应用和发展:

  • 高级编程语言:随着新的编程语言和技术的发展,访问者模式可能会以更加高效和灵活的方式实现。
  • 大型系统和微服务架构:访问者模式可以用于处理分布式系统中的对象结构,特别是在需要跨服务执行操作的情况下。
  • 人工智能和机器学习:访问者模式可以用于处理复杂的模型和数据结构,特别是在需要对模型执行多种操作的情况下。

未来的研究方向

  • 性能优化:研究如何在访问者模式中更有效地管理性能问题,特别是在大规模系统中。
  • 模式集成:探索访问者模式与其他设计模式的集成,以创建更加灵活和可扩展的系统。
  • 领域特定语言:研究如何在特定领域中利用访问者模式来创建领域特定语言(DSLs)和其他高级抽象。
  • 自动化工具:开发自动化工具来辅助访问者模式的设计和实现,以减少开发人员的工作量。

本文详细介绍了23种设计模式的基础知识,帮助读者快速掌握设计模式的核心概念,并找到适合实际应用的具体模式:
【设计模式入门】设计模式全解析:23种经典模式介绍与评级指南(设计师必备)

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

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

相关文章

GitHub 与 AWS CodeCommit

代码库对决 欢迎来到雲闪世界。在软件开发领域&#xff0c;高效管理代码至关重要。Git 存储库等版本控制系统 (VCS) 是无名英雄&#xff0c;为代码更改、协作和历史跟踪提供了安全避风港。在选择合适的存储库平台时&#xff0c;出现了两个巨头&#xff1a;GitHub 和 AWS CodeC…

【前端面试】看react源码,解读useState

点击:react git 链接 截止2024.8.22最新版本如下 React hooks 源码好深,hook封装位于packages/react-reconciler/src/ReactFiberHooks.js hook的数据类型: export type Hook = {memoizedState: any,baseState: any,baseQueue: Update<any, any> | null,queue: an…

Vue vue/cli3 与 vue/cli4 v-for 和 v-if 一起使用冲突

问题描述 异常信息&#xff1a;[vue/no-use-v-if-with-v-for] The this.$router.options.routers expression inside v-for directive should be replaced with a computed property that returns filtered array instead. You should not mix v-for with v-if.eslint-plugin-v…

基础算法--高精度数据(1)

高精度数据处理一般内容简单&#xff0c;写代码难度较大&#xff0c;可能部分内容涉及基础数学、初等数论等知识。请小心食用。不过本节不会给大家太难的高精度处理&#xff0c;我们第一次接触&#xff0c;不能劝退大家对吧。 高精度算法是指&#xff0c;利用基础或高级的数学…

pygame—炸弹牌(可做课设)

游戏介绍 在5X5的数字宫格里翻牌&#xff0c;翻出所有的2和3即可获胜每一格只能是0、1、2、3&#xff0c;第六列和最第六行为 X | Y&#xff0c;X代表该列或该行的数字总和&#xff0c;Y代表该列或该行的0的个数控制难度&#xff0c;每行每列的数字总和不超过9该游戏需要一定运…

Vue3学习笔记之数据绑定篇(0823)

学习完Vue2 的C友们&#xff0c;今天继续追赶Vue3的大潮流吧&#xff01; 废话不多说&#xff0c;直接上代码 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"…

MobaXterm接触session会话保存14个的限制

问题描述 在我们使用MobeXterm的过程中&#xff0c;发现session保存了14个之后&#xff0c;再无法继续保存了&#xff1b; 原因是免费版本的MobeXterm的最大个数被限制了&#xff0c;需要进行破解&#xff1b; MobaXterm-keygen解除session保存限制的python脚本 可以使用上面…

计算机的错误计算(七十一)

摘要 计算机的错误计算&#xff08;七十&#xff09;探讨了大数的正割函数的错误计算。本节讨论另外一类数值&#xff1a; 附近数 的正割函数的计算精度问题。 例1. 已知 计算 若用 在 Python下编程计算&#xff0c;则有 若在 Excel 中计算&#xff0c;则有&#xff1a…

Xmind 在线导图上线!多设备实时同步,节约本地空间

在现代职场上&#xff0c;高效的工作方法对于提升个人和团队的生产力至关重要。 Xmind 作为一款领先的思维导图软件&#xff0c;最近推出了其在线版本&#xff0c;旨在帮助我们解决在工作中常见的 「掉线状态」 问题&#xff0c;并提升工作效率。 在日常工作中&#xff0c;我们…

抖音如何去水印导出,3种高效工具让你轻松掌握

在抖音上&#xff0c;我们经常会遇到一些精彩视频想要保存下来&#xff0c;但视频上往往带有水印&#xff0c;影响了观看和分享的体验。下面&#xff0c;我将介绍三种去除抖音视频水印的方法&#xff0c;让你轻松保存无水印视频。 技巧一&#xff1a;奈斯水印助手(小程序) 这是…

基于大语言模型的物联网(artificial intelligence of thing)

与当下热门的AI类似&#xff0c;曾几何时&#xff0c;物联网&#xff08;Internet of thing&#xff09;实现“万物互联"给人类带来了无限的遐想。但是往往事与愿违&#xff0c;美好的愿景并没有如约而至。十几年来&#xff0c;物联网远没有实现”万物互联“的美好愿景。 …

Kafka·Producer

Producer发送原理 拦截器进行拦截 对key和value进行序列化 org.apache.kafka.clients.producer.KafkaProducer#doSend 分区选择 计算消息要发送到topic的哪个分区上 若指定了分区&#xff0c;则使用指定的值没有指定的话则使用分区器计算得到或者使用hash取余的方式 暂存…

Stm32通过SPI读写W25QXX

Printf的重定向 因为printf是c中的库函数&#xff0c;要使用printf输出到串口&#xff0c;需要重定向&#xff0c;将printf定向到HAL_UART_Transmit。 新建一个retarget.c文件。 #include "stdio.h" #include "stm32f1xx_hal.h" #include "usart.h&…

创意无限,尽在掌握:热门视频剪辑软件一览

我们记录生活、分享故事、传播信息用视频的频率越来越高了。而这些视频往往都是通过剪辑之后才能展示出当前的效果。那这次我们就来探索剪辑视频的时候都会用到什么工具吧。 1.福昕视频剪辑 连接直达>>https://www.pdf365.cn/foxit-clip/ 这是一款专为追求高效与创意…

Pytorch 张量运算函数(补充)

mean() mean()函数是进行张量均值计算的函数,常用参数可以设置参数dim来进行对应维度的均值计算 以下是使用一个二维张量进行演示的例子 import numpy as np import torch device torch.device(mps if torch.backends.mps.is_available() else cpu) print(device ) data1 …

【数据管理】数据治理

目录 1、相关概念 2、数据治理和管理职责语境关系图 3、业务驱动因素 4、目标和原则 5、 数据治理和数据管理的关系 6、数据治理组织 7、数据管理职能 8、数据制度 9、数据资产估值 1、相关概念 1&#xff09;战略(Stategy)&#xff1a;定义、交流和驱动数据战略和数…

[数据集][目标检测]电力场景输电线异物检测数据集VOC+YOLO格式2060张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;2060 标注数量(xml文件个数)&#xff1a;2060 标注数量(txt文件个数)&#xff1a;2060 标注…

电脑丢失dll文件一键修复之dll确实损坏影响电脑运行

在使用电脑过程中&#xff0c;DLL文件丢失或损坏是一个常见的问题&#xff0c;它可能导致程序无法正常运行&#xff0c;甚至影响整个系统的稳定性。本文将详细介绍如何一键修复丢失的DLL文件&#xff0c;探讨常见的DLL丢失报错原因&#xff0c;并提供详细的修复步骤和预防措施。…

sklearn回归树

说明&#xff1a;内容来自菜菜的sklearn机器学习和ai生成 回归树 调用对象的参数 class sklearn.tree.DecisionTreeRegressor (criterion’mse’, splitter’best’, max_depthNone, min_samples_split2, min_samples_leaf1, min_weight_fraction_leaf0.0, max_featuresNone…

大数据基础:数仓架构演变

文章目录 数仓架构演变 一、传统离线大数据架构 二、​​​​​​Lambda架构 三、Kappa架构 四、​​​​​​​​​​​​​​混合架构 五、湖仓一体架构 六、流批一体架构 数仓架构演变 20世纪70年代&#xff0c;MIT(麻省理工)的研究员致力于研究一种优化的技术架构&…