访问者模式(Visitor Pattern)详解

文章目录

    • 1. 访问者模式概述
      • 1.1 定义
      • 1.2 基本思想
    • 2. 访问者模式的结构
    • 3. 访问者模式的UML类图
    • 4. 访问者模式的工作原理
    • 5. Java实现示例
      • 5.1 基本实现示例
      • 5.2 访问者模式处理复杂对象层次结构
      • 5.3 访问者模式在文件系统中的应用
    • 6. 访问者模式的优缺点
      • 6.1 优点
      • 6.2 缺点
    • 7. 访问者模式的适用场景
    • 8. 访问者模式在框架中的应用
      • 8.1 Java的反射API
      • 8.2 Java ASM库
      • 8.3 Spring框架中的BeanDefinitionVisitor
    • 9. 访问者模式与其他设计模式的区别与联系
      • 9.1 访问者模式与策略模式
      • 9.2 访问者模式与组合模式
      • 9.3 访问者模式与命令模式
    • 10. 实战案例:电商订单处理系统
    • 11. 总结

1. 访问者模式概述

1.1 定义

访问者模式是一种行为型设计模式,它允许在不改变各元素类的前提下定义作用于这些元素的新操作。访问者模式通过将操作与对象结构分离,使得我们可以在不修改对象结构的情况下向现有对象结构添加新的操作。

1.2 基本思想

访问者模式的核心思想是:

  • 将数据结构与数据操作分离
  • 针对不同的元素类型,访问者可以执行不同的操作
  • 在不修改已有类的情况下,通过添加新的访问者实现对元素的新操作

2. 访问者模式的结构

访问者模式包含以下角色:

  • 抽象访问者(Visitor):声明了一组访问方法,用于访问不同类型的具体元素
  • 具体访问者(ConcreteVisitor):实现抽象访问者接口中声明的方法,为不同类型的元素提供具体操作实现
  • 抽象元素(Element):声明一个接受访问者的方法(accept),以供访问者访问
  • 具体元素(ConcreteElement):实现抽象元素接口,实现accept方法接受访问者访问
  • 对象结构(ObjectStructure):包含元素集合,可以提供让访问者遍历其内部元素的方法

3. 访问者模式的UML类图

┌─────────────────┐          ┌─────────────────┐
│ ObjectStructure │          │ <<interface>>   │
├─────────────────┤          │     Element     │
│ +elements       │<>────────├─────────────────┤
│ +accept(Visitor)│          │ +accept(Visitor)│
└─────────────────┘          └────────┬────────┘│                            ││                            ││                ┌───────────┴───────────┐│                │                       ││      ┌─────────▼────────┐   ┌──────────▼─────────┐│      │  ConcreteElementA│   │  ConcreteElementB  ││      ├──────────────────┤   ├────────────────────┤│      │+accept(Visitor)  │   │+accept(Visitor)    ││      └──────────────────┘   └────────────────────┘│                │                      ││                │                      ││                ▼                      ▼│         "visitor.visitA(this)"   "visitor.visitB(this)"│││      ┌─────────────────────┐└─────>│    <<interface>>    ││       Visitor       │├─────────────────────┤│+visitA(ElementA)    ││+visitB(ElementB)    │└──────────┬──────────┘││┌─────────────┴────────────┐│                          │
┌────────────▼────────────┐  ┌──────────▼────────────┐
│   ConcreteVisitor1      │  │    ConcreteVisitor2   │
├─────────────────────────┤  ├─────────────────────────┤
│+visitA(ElementA)        │  │+visitA(ElementA)        │
│+visitB(ElementB)        │  │+visitB(ElementB)        │
└─────────────────────────┘  └─────────────────────────┘

4. 访问者模式的工作原理

  1. 当需要对一个对象结构中的元素进行操作时,访问者模式可以在不修改元素类的前提下实现新功能
  2. 每个具体元素都实现accept方法,该方法通过调用访问者对象的对应方法来实现功能
  3. 具体访问者针对每种具体元素类型实现相应的访问方法
  4. 在访问者模式中,双重分派是一个核心概念:
    • 第一次分派:根据元素的实际类型选择调用哪个accept方法
    • 第二次分派:在accept方法内部,根据访问者的实际类型选择调用哪个visit方法

5. Java实现示例

5.1 基本实现示例

下面是一个简单的访问者模式实现示例,假设我们有一个简单的计算机部件系统:

// 抽象访问者
interface ComputerPartVisitor {void visit(Computer computer);void visit(Mouse mouse);void visit(Keyboard keyboard);void visit(Monitor monitor);
}// 抽象元素
interface ComputerPart {void accept(ComputerPartVisitor visitor);
}// 具体元素类:鼠标
class Mouse implements ComputerPart {@Overridepublic void accept(ComputerPartVisitor visitor) {visitor.visit(this);}
}// 具体元素类:键盘
class Keyboard implements ComputerPart {@Overridepublic void accept(ComputerPartVisitor visitor) {visitor.visit(this);}
}// 具体元素类:显示器
class Monitor implements ComputerPart {@Overridepublic void accept(ComputerPartVisitor visitor) {visitor.visit(this);}
}// 具体元素类:计算机(复合元素)
class Computer implements ComputerPart {ComputerPart[] parts;public Computer() {parts = new ComputerPart[] {new Mouse(), new Keyboard(), new Monitor()};}@Overridepublic void accept(ComputerPartVisitor visitor) {for (ComputerPart part : parts) {part.accept(visitor);}visitor.visit(this);}
}// 具体访问者:显示访问者
class ComputerPartDisplayVisitor implements ComputerPartVisitor {@Overridepublic void visit(Computer computer) {System.out.println("展示计算机。");}@Overridepublic void visit(Mouse mouse) {System.out.println("展示鼠标。");}@Overridepublic void visit(Keyboard keyboard) {System.out.println("展示键盘。");}@Overridepublic void visit(Monitor monitor) {System.out.println("展示显示器。");}
}// 具体访问者:维护访问者
class ComputerPartMaintainVisitor implements ComputerPartVisitor {@Overridepublic void visit(Computer computer) {System.out.println("维护计算机。");}@Overridepublic void visit(Mouse mouse) {System.out.println("清洁鼠标。");}@Overridepublic void visit(Keyboard keyboard) {System.out.println("清洁并检查键盘按键。");}@Overridepublic void visit(Monitor monitor) {System.out.println("校准并清洁显示器屏幕。");}
}// 客户端
public class ComputerPartClient {public static void main(String[] args) {ComputerPart computer = new Computer();System.out.println("展示操作:");computer.accept(new ComputerPartDisplayVisitor());System.out.println("\n维护操作:");computer.accept(new ComputerPartMaintainVisitor());}
}

5.2 访问者模式处理复杂对象层次结构

下面是一个处理不同形状的访问者模式示例:

// 形状接口 - 抽象元素
interface Shape {void accept(ShapeVisitor visitor);
}// 圆形 - 具体元素
class Circle implements Shape {private double radius;public Circle(double radius) {this.radius = radius;}public double getRadius() {return radius;}@Overridepublic void accept(ShapeVisitor visitor) {visitor.visit(this);}
}// 矩形 - 具体元素
class Rectangle implements Shape {private double width;private double height;public Rectangle(double width, double height) {this.width = width;this.height = height;}public double getWidth() {return width;}public double getHeight() {return height;}@Overridepublic void accept(ShapeVisitor visitor) {visitor.visit(this);}
}// 三角形 - 具体元素
class Triangle implements Shape {private double sideA;private double sideB;private double sideC;public Triangle(double sideA, double sideB, double sideC) {this.sideA = sideA;this.sideB = sideB;this.sideC = sideC;}public double getSideA() {return sideA;}public double getSideB() {return sideB;}public double getSideC() {return sideC;}@Overridepublic void accept(ShapeVisitor visitor) {visitor.visit(this);}
}// 形状访问者接口 - 抽象访问者
interface ShapeVisitor {void visit(Circle circle);void visit(Rectangle rectangle);void visit(Triangle triangle);
}// 计算面积的访问者 - 具体访问者
class AreaCalculator implements ShapeVisitor {@Overridepublic void visit(Circle circle) {double area = Math.PI * circle.getRadius() * circle.getRadius();System.out.println("圆的面积:" + area);}@Overridepublic void visit(Rectangle rectangle) {double area = rectangle.getWidth() * rectangle.getHeight();System.out.println("矩形的面积:" + area);}@Overridepublic void visit(Triangle triangle) {// 使用海伦公式计算三角形面积double s = (triangle.getSideA() + triangle.getSideB() + triangle.getSideC()) / 2;double area = Math.sqrt(s * (s - triangle.getSideA()) * (s - triangle.getSideB()) * (s - triangle.getSideC()));System.out.println("三角形的面积:" + area);}
}// 计算周长的访问者 - 具体访问者
class PerimeterCalculator implements ShapeVisitor {@Overridepublic void visit(Circle circle) {double perimeter = 2 * Math.PI * circle.getRadius();System.out.println("圆的周长:" + perimeter);}@Overridepublic void visit(Rectangle rectangle) {double perimeter = 2 * (rectangle.getWidth() + rectangle.getHeight());System.out.println("矩形的周长:" + perimeter);}@Overridepublic void visit(Triangle triangle) {double perimeter = triangle.getSideA() + triangle.getSideB() + triangle.getSideC();System.out.println("三角形的周长:" + perimeter);}
}// 绘制形状的访问者 - 具体访问者
class ShapeDrawer implements ShapeVisitor {@Overridepublic void visit(Circle circle) {System.out.println("绘制圆形,半径:" + circle.getRadius());}@Overridepublic void visit(Rectangle rectangle) {System.out.println("绘制矩形,宽:" + rectangle.getWidth() + ",高:" + rectangle.getHeight());}@Overridepublic void visit(Triangle triangle) {System.out.println("绘制三角形,边长:" + triangle.getSideA() + ", " + triangle.getSideB() + ", " + triangle.getSideC());}
}// 形状集合 - 对象结构
class ShapeCollection {private List<Shape> shapes = new ArrayList<>();public void addShape(Shape shape) {shapes.add(shape);}public void accept(ShapeVisitor visitor) {for (Shape shape : shapes) {shape.accept(visitor);}}
}// 客户端代码
public class ShapeClient {public static void main(String[] args) {ShapeCollection shapes = new ShapeCollection();shapes.addShape(new Circle(5.0));shapes.addShape(new Rectangle(4.0, 6.0));shapes.addShape(new Triangle(3.0, 4.0, 5.0));System.out.println("计算所有形状的面积:");shapes.accept(new AreaCalculator());System.out.println("\n计算所有形状的周长:");shapes.accept(new PerimeterCalculator());System.out.println("\n绘制所有形状:");shapes.accept(new ShapeDrawer());}
}

5.3 访问者模式在文件系统中的应用

下面是一个使用访问者模式处理文件系统的示例:

import java.util.ArrayList;
import java.util.List;// 文件系统元素 - 抽象元素
interface FileSystemElement {void accept(FileVisitor visitor);String getName();
}// 文件 - 具体元素
class File implements FileSystemElement {private String name;private long size; // 文件大小(字节)public File(String name, long size) {this.name = name;this.size = size;}@Overridepublic String getName() {return name;}public long getSize() {return size;}@Overridepublic void accept(FileVisitor visitor) {visitor.visit(this);}
}// 目录 - 具体元素
class Directory implements FileSystemElement {private String name;private List<FileSystemElement> elements = new ArrayList<>();public Directory(String name) {this.name = name;}@Overridepublic String getName() {return name;}public void addElement(FileSystemElement element) {elements.add(element);}public List<FileSystemElement> getElements() {return elements;}@Overridepublic void accept(FileVisitor visitor) {visitor.visit(this);// 递归访问所有子元素for (FileSystemElement element : elements) {element.accept(visitor);}}
}// 文件访问者 - 抽象访问者
interface FileVisitor {void visit(File file);void visit(Directory directory);
}// 文件统计访问者 - 具体访问者
class FileStatisticsVisitor implements FileVisitor {private int fileCount = 0;private int directoryCount = 0;private long totalSize = 0;@Overridepublic void visit(File file) {fileCount++;totalSize += file.getSize();System.out.println("文件: " + file.getName() + ", 大小: " + file.getSize() + " 字节");}@Overridepublic void visit(Directory directory) {directoryCount++;System.out.println("目录: " + directory.getName());}public void printStatistics() {System.out.println("\n统计信息:");System.out.println("文件数量: " + fileCount);System.out.println("目录数量: " + directoryCount);System.out.println("总大小: " + totalSize + " 字节");}
}// 查找访问者 - 具体访问者
class SearchVisitor implements FileVisitor {private String searchKeyword;private List<FileSystemElement> foundElements = new ArrayList<>();public SearchVisitor(String searchKeyword) {this.searchKeyword = searchKeyword;}@Overridepublic void visit(File file) {if (file.getName().contains(searchKeyword)) {foundElements.add(file);}}@Overridepublic void visit(Directory directory) {if (directory.getName().contains(searchKeyword)) {foundElements.add(directory);}}public void printResults() {System.out.println("\n搜索结果,关键词: \"" + searchKeyword + "\"");if (foundElements.isEmpty()) {System.out.println("未找到匹配项");} else {for (FileSystemElement element : foundElements) {System.out.println("找到: " + element.getName());}}}
}// 客户端代码
public class FileSystemDemo {public static void main(String[] args) {// 创建文件系统结构Directory root = new Directory("root");Directory documents = new Directory("documents");Directory pictures = new Directory("pictures");File file1 = new File("report.doc", 2000);File file2 = new File("presentation.ppt", 5000);File pic1 = new File("vacation.jpg", 3000);File pic2 = new File("family.jpg", 2000);Directory downloads = new Directory("downloads");File file3 = new File("game.exe", 10000);// 构建文件系统树root.addElement(documents);root.addElement(pictures);root.addElement(downloads);documents.addElement(file1);documents.addElement(file2);pictures.addElement(pic1);pictures.addElement(pic2);downloads.addElement(file3);// 使用统计访问者FileStatisticsVisitor statisticsVisitor = new FileStatisticsVisitor();root.accept(statisticsVisitor);statisticsVisitor.printStatistics();// 使用搜索访问者SearchVisitor searchVisitor = new SearchVisitor("jpg");root.accept(searchVisitor);searchVisitor.printResults();}
}

6. 访问者模式的优缺点

6.1 优点

  1. 扩展性好:在不修改现有元素类的情况下,通过添加新的访问者类来增加新功能
  2. 符合单一职责原则:将数据结构和数据操作分离,使得职责更清晰
  3. 集中相关操作:针对不同元素类型的操作被集中在各个访问者类中,便于管理
  4. 适合复杂对象结构:对于复杂对象结构(如组合模式结构)特别适用
  5. 增强数据结构的灵活性:相同的数据结构可以被不同访问者用于完成不同操作

6.2 缺点

  1. 元素类变更困难:如果经常添加新的元素类,需要修改所有访问者类,违反了开闭原则
  2. 违反了依赖倒置原则:具体元素对具体访问者产生了依赖
  3. 访问者需要了解元素内部细节:可能会破坏封装性
  4. 增加复杂度:结构较复杂,理解和实现难度较高
  5. 循环依赖风险:如果处理不当,可能会导致元素和访问者之间形成循环依赖

7. 访问者模式的适用场景

  1. 对象结构比较稳定,但经常需要在此对象结构上定义新的操作
  2. 需要对一个对象结构中的对象进行很多不同且不相关的操作,而不想让这些操作"污染"类
  3. 数据结构和数据操作需要分离的场景
  4. 适合复杂对象结构(如组合模式构成的结构)的操作

常见应用场景:

  • 文件系统遍历和操作
  • 编译器设计(语法树访问)
  • XML文档解析
  • 复杂图形结构绘制
  • 数据结构的多种算法实现

8. 访问者模式在框架中的应用

8.1 Java的反射API

Java的反射API中的AnnotatedElement接口及其实现就体现了访问者模式的思想:

public interface AnnotatedElement {boolean isAnnotationPresent(Class<? extends Annotation> annotationClass);<T extends Annotation> T getAnnotation(Class<T> annotationClass);Annotation[] getAnnotations();Annotation[] getDeclaredAnnotations();
}

8.2 Java ASM库

Java ASM库使用访问者模式来处理Java字节码:

// ASM的ClassVisitor示例
public class ClassPrinter extends ClassVisitor {public ClassPrinter() {super(ASM4);}@Overridepublic void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {System.out.println(name + " extends " + superName + " {");}@Overridepublic FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {System.out.println("    " + desc + " " + name);return null;}@Overridepublic MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {System.out.println("    " + name + desc);return null;}@Overridepublic void visitEnd() {System.out.println("}");}
}

8.3 Spring框架中的BeanDefinitionVisitor

Spring框架中的BeanDefinitionVisitor用于访问Bean定义:

public class BeanDefinitionVisitor {public void visitBeanDefinition(BeanDefinition beanDefinition) {visitParentName(beanDefinition.getParentName());visitBeanClassName(beanDefinition.getBeanClassName());visitFactoryBeanName(beanDefinition.getFactoryBeanName());visitFactoryMethodName(beanDefinition.getFactoryMethodName());visitScope(beanDefinition.getScope());if (beanDefinition.hasPropertyValues()) {visitPropertyValues(beanDefinition.getPropertyValues());}if (beanDefinition.hasConstructorArgumentValues()) {visitConstructorArgumentValues(beanDefinition.getConstructorArgumentValues());}}protected void visitParentName(String parentName) {// ...}protected void visitBeanClassName(String beanClassName) {// ...}// 其他访问方法...
}

9. 访问者模式与其他设计模式的区别与联系

9.1 访问者模式与策略模式

  • 相似点:都将算法从主体类中抽离
  • 区别
    • 策略模式针对单一对象提供不同算法
    • 访问者模式针对不同类型对象提供不同算法,且通常处理复杂对象结构

9.2 访问者模式与组合模式

  • 相似点:都适用于处理复杂对象结构
  • 区别
    • 组合模式侧重于构建树形对象结构
    • 访问者模式侧重于在已有对象结构上执行操作,两者经常一起使用

9.3 访问者模式与命令模式

  • 相似点:都将操作封装成对象
  • 区别
    • 命令模式封装单一请求为对象
    • 访问者模式封装一组相关的操作,能够处理复杂对象结构

10. 实战案例:电商订单处理系统

import java.util.ArrayList;
import java.util.List;// 订单元素 - 抽象元素
interface OrderElement {void accept(OrderVisitor visitor);
}// 订单项 - 具体元素
class OrderItem implements OrderElement {private String productName;private int quantity;private double price;private boolean isImported;private boolean isFragile;public OrderItem(String productName, int quantity, double price, boolean isImported, boolean isFragile) {this.productName = productName;this.quantity = quantity;this.price = price;this.isImported = isImported;this.isFragile = isFragile;}public String getProductName() {return productName;}public int getQuantity() {return quantity;}public double getPrice() {return price;}public boolean isImported() {return isImported;}public boolean isFragile() {return isFragile;}@Overridepublic void accept(OrderVisitor visitor) {visitor.visit(this);}
}// 订单 - 具体元素(也是对象结构)
class Order implements OrderElement {private String orderNumber;private String customerName;private List<OrderItem> items = new ArrayList<>();public Order(String orderNumber, String customerName) {this.orderNumber = orderNumber;this.customerName = customerName;}public String getOrderNumber() {return orderNumber;}public String getCustomerName() {return customerName;}public void addItem(OrderItem item) {items.add(item);}public List<OrderItem> getItems() {return items;}@Overridepublic void accept(OrderVisitor visitor) {visitor.visit(this);for (OrderItem item : items) {item.accept(visitor);}}
}// 订单访问者 - 抽象访问者
interface OrderVisitor {void visit(Order order);void visit(OrderItem orderItem);
}// 税费计算访问者 - 具体访问者
class TaxCalculationVisitor implements OrderVisitor {private double totalTax = 0;private double importTaxRate = 0.05; // 5%的进口税private double generalTaxRate = 0.07; // 7%的普通消费税@Overridepublic void visit(Order order) {System.out.println("计算订单 " + order.getOrderNumber() + " 的税费");}@Overridepublic void visit(OrderItem orderItem) {double itemTotal = orderItem.getPrice() * orderItem.getQuantity();double taxAmount = itemTotal * generalTaxRate;if (orderItem.isImported()) {taxAmount += itemTotal * importTaxRate;}totalTax += taxAmount;System.out.println("商品: " + orderItem.getProductName() + ", 数量: " + orderItem.getQuantity() + ", 税额: " + taxAmount);}public double getTotalTax() {return totalTax;}
}// 配送成本计算访问者 - 具体访问者
class ShippingCostVisitor implements OrderVisitor {private double totalShippingCost = 0;private double standardShippingCost = 5.0; // 基础配送费private double fragileItemSurcharge = 3.0; // 易碎品附加费private double importedItemSurcharge = 2.0; // 进口商品附加费@Overridepublic void visit(Order order) {System.out.println("计算订单 " + order.getOrderNumber() + " 的配送费用");}@Overridepublic void visit(OrderItem orderItem) {double itemShippingCost = standardShippingCost;if (orderItem.isFragile()) {itemShippingCost += fragileItemSurcharge;}if (orderItem.isImported()) {itemShippingCost += importedItemSurcharge;}// 多件商品按照数量计算总配送费用itemShippingCost *= orderItem.getQuantity();totalShippingCost += itemShippingCost;System.out.println("商品: " + orderItem.getProductName() + ", 数量: " + orderItem.getQuantity() + ", 配送费: " + itemShippingCost);}public double getTotalShippingCost() {return totalShippingCost;}
}// 订单统计访问者 - 具体访问者
class OrderSummaryVisitor implements OrderVisitor {private int totalItems = 0;private double orderTotal = 0;@Overridepublic void visit(Order order) {System.out.println("\n订单摘要 - 订单号: " + order.getOrderNumber() + ", 客户: " + order.getCustomerName());}@Overridepublic void visit(OrderItem orderItem) {int quantity = orderItem.getQuantity();double itemTotal = orderItem.getPrice() * quantity;totalItems += quantity;orderTotal += itemTotal;System.out.println("商品: " + orderItem.getProductName() + ", 数量: " + quantity + ", 单价: " + orderItem.getPrice() + ", 小计: " + itemTotal);}public void printSummary() {System.out.println("\n总计商品数量: " + totalItems);System.out.println("订单总金额: " + orderTotal);}public int getTotalItems() {return totalItems;}public double getOrderTotal() {return orderTotal;}
}// 客户端代码
public class ECommerceOrderDemo {public static void main(String[] args) {// 创建一个订单Order order = new Order("ORD-12345", "张三");// 添加订单项order.addItem(new OrderItem("笔记本电脑", 1, 5999.99, true, true));order.addItem(new OrderItem("无线鼠标", 2, 99.99, false, true));order.addItem(new OrderItem("程序设计书籍", 3, 89.99, false, false));order.addItem(new OrderItem("进口咖啡", 5, 32.99, true, false));// 使用摘要访问者生成订单摘要OrderSummaryVisitor summaryVisitor = new OrderSummaryVisitor();order.accept(summaryVisitor);summaryVisitor.printSummary();// 使用税费计算访问者计算税费TaxCalculationVisitor taxVisitor = new TaxCalculationVisitor();order.accept(taxVisitor);System.out.println("\n总税费: " + taxVisitor.getTotalTax());// 使用配送成本访问者计算配送费用ShippingCostVisitor shippingVisitor = new ShippingCostVisitor();order.accept(shippingVisitor);System.out.println("\n总配送费: " + shippingVisitor.getTotalShippingCost());// 计算最终应付金额double finalTotal = summaryVisitor.getOrderTotal() + taxVisitor.getTotalTax() + shippingVisitor.getTotalShippingCost();System.out.println("\n最终应付金额: " + finalTotal);}
}

11. 总结

访问者模式是一种强大的设计模式,特别适用于需要在不修改现有类结构的情况下添加新操作的场景。它通过将数据结构与操作分离,实现了良好的扩展性和职责分离。

访问者模式的关键点:

  1. 利用"双重分派"机制,根据元素和访问者的实际类型来确定执行的具体方法
  2. 适合元素类结构稳定但操作多变的场景
  3. 不适合元素类经常变化的场景,因为每增加一个元素类都需要修改所有访问者

使用访问者模式时需要注意的问题:

  1. 注意元素类的稳定性,确保不会频繁地添加新元素类型
  2. 在设计访问者接口时,应考虑将来可能的扩展
  3. 避免访问者和元素之间的过度耦合,不要过度暴露元素的内部细节

访问者模式是一种相对高级和复杂的设计模式,使用时需要权衡其带来的灵活性和增加的复杂度。在适当的场景下,它能够有效地提高代码的可维护性和扩展性。

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

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

相关文章

matlab介绍while函数

MATLAB 中的 while 语句介绍 在 MATLAB 中&#xff0c;while 语句是一种循环结构&#xff0c;用于在满足特定条件时反复执行一段代码块。与 for 循环不同&#xff0c;while 循环的执行次数是动态的&#xff0c;取决于循环条件是否为真。 语法 while condition% 循环体代码 e…

数字信号处理|| 快速傅里叶变换(FFT)

一、实验目的 &#xff08;1&#xff09;加深对快速傅里叶变换&#xff08;FFT&#xff09;基本理论的理解。 &#xff08;2&#xff09;了解使用快速傅里叶变换&#xff08;FFT&#xff09;计算有限长序列和无限长序列信号频谱的方法。 &#xff08;3&#xff09;掌握用MATLA…

.Net Mqtt协议-MQTTNet(一)简介

一、MQTTNet 简介 MQTTnet 是一个高性能的MQTT类库&#xff0c;支持.NET Core和.NET Framework。 二、MQTTNet 原理 MQTTnet 是一个用于.NET的高性能MQTT类库&#xff0c;实现了MQTT协议的各个层级&#xff0c;包括连接、会话、发布/订阅、QoS&#xff08;服务质量&#xff0…

时钟晶振锁相环pll方向技术要点和大厂题目解析

本专栏预计更新60期左右。当前第9期。 本专栏不仅适用于硬件的笔试面试,同样也适用于梳理硬件核心的知识点。 通过本文能得到什么? 首先,根据实战经验总结时钟晶振,锁相环的主要知识点,技术要点,面试考点; 然后,列出时钟晶振,锁相环的笔试面试的主要题型真题和模拟题,…

机器学习 day6 -线性回归练习

题目‌&#xff1a; 从Kaggle的“House Prices - Advanced Regression Techniques”数据集使用Pandas读取数据&#xff0c;并查看数据的基本信息。选择一些你认为对房屋价格有重要影响的特征&#xff0c;并进行数据预处理&#xff08;如缺失值处理、异常值处理等&#xff09;。…

缓存(2):数据一致性

概述 一致性就是数据保持一致,在分布式系统中,可以理解为多个节点中数据的值是一致的。 强一致性:这种一致性级别是最符合用户直觉的,它要求系统写入什么,读出来的也会是什么,用户体验好,但实现起来往往对系统的性能影响大弱一致性:这种一致性级别约束了系统在写入成功…

CH579 CH573 CH582 CH592 蓝牙主机(Central)实例应用讲解

蓝牙主机&#xff08;Central&#xff09;&#xff0c;顾名思义&#xff0c;就是一个蓝牙主设备&#xff0c;与从机&#xff08;Peripheral&#xff09;建立连接进行通信&#xff0c;可以接收从机通知&#xff0c;也可以给从机发送信息&#xff0c;通常Central和Peripheral结合…

不同类型的 SAP 项目

目录 1 实施项目 2 SAP S/4 HANA 升级项目 3 数据迁移项目 4 优化项目 5 Rollout 项目 6 运维项目 1 实施项目 企业第一次用 SAP 系统&#xff0c;从硬件搭建到安装 SAP、根据业务流程做配置、开发、培训业务、测试系统直到系统上线。 SAP S/4 HANA ACTIVATE 实施方法论…

【uniapp】errMsg: “navigateTo:fail timeout“

项目场景&#xff1a; 在点击编辑的时候不能跳转的编辑的页面&#xff0c;然后直接报错errMsg: "navigateTo:fail timeout" 解决方案&#xff1a; 看看是否是出现了盒子的冒泡事件导致了两次调用跳转路径 tap.stop

记录学习的第三十五天

今天主攻单源最短路Dijkstra算法。不过&#xff0c;还是没有完全掌握。 首先是书本的例题我理解了一遍。 然后其实在力扣上做了三道题的&#xff0c;但是我看题解的情况就不太会。然后试着用上面的方法敲了一下↓的题&#xff0c;但是不对啊&#xff0c;我也不知道为什么呀。

Spring-博客系统项目

一,实现效果 登录: 注册: 博客列表 个人博客中心 博客详情: 更新博客 编写博客 二,数据库的建立和连接 首先,需要建库,需要两个实体,一个是用户,一个是博客,需要如下属性,需要注意的是需要将密码的变长字符创设置的长一些,因为之后会对用户的密码进行加密,该博客中密码…

依赖注入详解与案例(前端篇)

依赖注入详解与案例&#xff08;前端篇&#xff09; 一、依赖注入核心概念与前端价值 依赖注入&#xff08;Dependency Injection, DI&#xff09; 是一种通过外部容器管理组件/类间依赖关系的设计模式&#xff0c;其核心是控制反转&#xff08;Inversion of Control, IoC&…

diy装机成功录

三天前&#xff0c;我正式开启了这次装机之旅&#xff0c;购入了一颗性能强劲的 i5-12400 CPU&#xff0c;一块绘图能力出色的 3060ti 显卡&#xff0c;还有技嘉主板、高效散热器、16G 内存条、2T 固态硬盘&#xff0c;以及气派的机箱和风扇&#xff0c;满心期待能亲手打造一台…

计算机三大主流操作系统的前世今生 - Linux|macOS|Windows

全文目录 1 引言2 起源之路2.1 Linux 起源2.2 macOS 起源2.3 Windows 起源 3 综合解析3.1 Linux系统综合解析3.1.1 系统定义与核心架构3.1.2 发展历程3.1.3 核心特点3.1.4 主流发行版3.1.5 应用场景 3.2 macOS系统综合解析3.2.1 系统定义与核心架构3.2.2 发展历程3.2.3 核心特点…

【AI智能推荐系统】第七篇:跨领域推荐系统的技术突破与应用场景

第七篇:跨领域推荐系统的技术突破与应用场景 提示语:🔥 “打破数据孤岛,实现1+1>2的推荐效果!深度解析美团、亚马逊如何用跨领域推荐技术实现业务协同,知识迁移核心技术全公开!” 目录 跨领域推荐的商业价值跨领域推荐技术体系 2.1 基于共享表征的学习2.2 迁移学习…

R 语言科研绘图 --- 桑基图-汇总

在发表科研论文的过程中&#xff0c;科研绘图是必不可少的&#xff0c;一张好看的图形会是文章很大的加分项。 为了便于使用&#xff0c;本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中&#xff0c;获取方式&#xff1a; R 语言科研绘图模板 --- sciRplothttps://mp.…

LintCode第485题-生成给定大小的数组,第220题-冰雹猜想,第235题-分解质因数

第485题 描述 给你一个大小size,生成一个元素从1 到 size的数组 样例 1:输入: size 4输出: [1, 2, 3, 4]样例解释: 返回一个顺序填充1到4的数组。样例 2:输入: size 1输出: [1]样例解释: 返回一个顺序填充1到1的数组 代码如下: public class Solution { /** * param s…

Pandas:数据处理与分析

目录 一、Pandas 简介 二、Pandas 的安装与导入 三、Pandas 的核心数据结构 &#xff08;一&#xff09;Series &#xff08;二&#xff09;DataFrame 四、Pandas 数据读取与写入 &#xff08;一&#xff09;读取数据 &#xff08;二&#xff09;写入数据 五、数据清洗…

Linux云计算训练营笔记day05(Rocky Linux中的命令:管道操作 |、wc、find、vim)

管道操作 | 作用: 将前面命令的输出&#xff0c;传递给后面命令&#xff0c;作为后面命令的参数 head -3 /etc/passwd | tail -1 取第三行 head -8 /etc/passwd | tail -3 | cat -n 取6 7 8行 ifconfig | head -2 | tail -1 只查看IP地址 ifconfig | grep 192 过滤192的ip…

动态规划:最长递增子序列

给定一个数组&#xff0c;求最长递增子序列的长度,就是要求我们求出一个序列中最长的上升子序列的长度&#xff0c;最长上升子序列的定义就是从原序列中按照孙旭去除一些数字&#xff0c;这些数字是逐渐增大的。 *定义dp[i]表示以第i个元素结尾的最长上升子序列的长度。 *初始…