訪問者模式(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
英文地址,請注明出處:http://en.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個元素結尾的最長上升子序列的長度。 *初始…