【设计模式】访问者模式

访问者模式(Visitor Pattern)

概念:

· 一种行为型设计模式;

· 将作用于某些对象结构中的操作封装成独立的访问者类,从而实现在不改变元素类的情况下,对元素进行新的操作;

UML结构:

复制代码
          +----------------+
          |    IVisitor    |   ← 访问者接口
          +----------------+
          | visit(ElementA)|
          | visit(ElementB)|
          +----------------+
                 ^
                 |
        +--------------------+
        | ConcreteVisitor    |   ← 具体访问者,实现具体操作
        +--------------------+
        | visit(ElementA)    |
        | visit(ElementB)    |
        +--------------------+

          +----------------+
          |   IElement     |   ← 元素接口
          +----------------+
          | accept(visitor)|
          +----------------+
                 ^
                 |
        +--------------------+
        | ConcreteElementA   |   ← 具体元素
        +--------------------+
        | operationA()       |
        | accept(visitor)    |
        +--------------------+
        
        +--------------------+
        | ConcreteElementB   |
        +--------------------+
        | operationB()       |
        | accept(visitor)    |
        +--------------------+

代码示例:

cs 复制代码
/// <summary>
/// 产品接口类
/// </summary>
public interface IProduct
{
    void Accept(IVisitor visitor);
}

/// <summary>
/// 访问者接口类
/// </summary>
public interface IVisitor
{
    void Visit(Electronics electronics);
    void Visit(Book book);
    void Visit(Food food);
}

/// <summary>
/// 产品基类
/// </summary>
public abstract class ProductBase
{
    public float _TaxRate { get; set; }
    public float _Price { get; set; }
    public string _Description { get; set; }
    public float _Discount { get; set; }

    public ProductBase(float taxRate, float price, string description, float discount)
    {
        _TaxRate = taxRate;
        _Price = price;
        _Description = description;
        _Discount = discount;
    }

    public abstract void Accept(IVisitor visitor);
}

/// <summary>
/// 具体产品1
/// </summary>
public class Electronics : ProductBase, IProduct
{
    public Electronics(float taxRate, float price, string description, float discount) : base(taxRate, price, description, discount) { }

    public override void Accept(IVisitor visitor)
    {
        visitor.Visit(this);
    }
}

/// <summary>
/// 具体产品2
/// </summary>
public class Book : ProductBase, IProduct
{
    public Book(float taxRate, float price, string description, float discount) : base(taxRate, price, description, discount) { }

    public override void Accept(IVisitor visitor)
    {
        visitor.Visit(this);
    }
}

/// <summary>
/// 具体产品3
/// </summary>
public class Food : ProductBase, IProduct
{
    public Food(float taxRate, float price, string description, float discount) : base(taxRate, price, description, discount) { }

    public override void Accept(IVisitor visitor)
    {
        visitor.Visit(this);
    }
}

/// <summary>
/// 具体访问者1
/// </summary>
public class CalculatePrice : IVisitor
{
    public float TotalPrice { get; set; } = 0;

    public void Visit(Electronics electronics)
    {
        TotalPrice += electronics.Price * (1 + electronics.TaxRate);
        Console.WriteLine($"当前总价格为:{TotalPrice}");
    }

    public void Visit(Book book)
    {
        TotalPrice += book.Price * (1 + book.TaxRate);
        Console.WriteLine($"当前总价格为:{TotalPrice}");
    }

    public void Visit(Food food)
    {
        TotalPrice += food.Price * (1 + food.TaxRate);
        Console.WriteLine($"当前总价格为:{TotalPrice}");
    }
}

/// <summary>
/// 具体访问者2
/// </summary>
public class GenerateDescription : IVisitor
{
    public void Visit(Electronics electronics)
    {
        Console.WriteLine($"电子产品描述:{electronics._Description}");
    }

    public void Visit(Book book)
    {
        Console.WriteLine($"书籍描述:{book._Description}");
    }

    public void Visit(Food food)
    {
        Console.WriteLine($"食物描述:{food._Description}");
    }
}

/// <summary>
/// 具体访问者3
/// </summary>
public class ApplyDiscount : IVisitor
{
    public void Visit(Electronics electronics)
    {
        Console.WriteLine($"电子产品打折规则:{electronics._Discount}");
    }

    public void Visit(Book book)
    {
        Console.WriteLine($"书籍打折规则:{book._Discount}");
    }

    public void Visit(Food food)
    {
        Console.WriteLine($"食物打折规则:{food._Discount}");
    }
}

public class Client
{
    public static void Main()
    {
        ProductBase electronics = new Electronics(0.15f, 5000f, "Gaming Laptop", 0.1f);
        ProductBase book = new Book(0.05f, 80f, "Design Patterns", 0.2f);
        ProductBase food = new Food(0.1f, 20f, "Organic Apple", 0.05f);

        IVisitor calculatePrice = new CalculatePrice();
        IVisitor generateDescription = new GenerateDescription();
        IVisitor applyDiscount = new ApplyDiscount();

        electronics.Accept(calculatePrice);
        electronics.Accept(generateDescription);
        electronics.Accept(applyDiscount);

        book.Accept(calculatePrice);
        book.Accept(generateDescription);
        book.Accept(applyDiscount);

        food.Accept(calculatePrice);
        food.Accept(generateDescription);
        food.Accept(applyDiscount);
    }
}

特点:
优点:

· 符合单一职责原则;

· 易于扩展新的操作;

· 结构清晰:把元素和操作分离,逻辑职责明确;
缺点:

· 扩展新的元素时,需要修改已有的访问者接口和已有的访问者类;

· 违反封装:访问者需要访问元素的内部数据,破坏了封装性;

· 增加系统的复杂度:将操作与元素拆分会导致类的增多;

适用场景:

· 对象结构稳定,元素个数相对固定,但是需要不断增加新操作;

· 对象结构中有很多不同类型的对象,并希望对这些对象实现不同的操作;

· 希望能把数据与操作逻辑分离;

· 希望通过向不同的对象传入相同的参数以实现与其对应的不同操作;

举例场景:

· 文档处理系统

元素:段落、图片、表格;

操作(访问者):导出HTML、拼写检查、统计信息;

相关推荐
2501_9167665417 小时前
【面试题1】128陷阱、==和equals的区别
java·开发语言
FuckPatience17 小时前
C# 使用内存映射文件实现进程间通信
c#
a程序小傲17 小时前
蚂蚁Java面试被问:注解的工作原理及如何自定义注解
java·开发语言·python·面试
似水এ᭄往昔18 小时前
【C++】--封装红⿊树实现mymap和myset
开发语言·数据结构·c++·算法·stl
charlie11451419118 小时前
嵌入式现代C++教程:C++98——从C向C++的演化(3)
c语言·开发语言·c++·笔记·学习·嵌入式
TAEHENGV18 小时前
创建目标模块 Cordova 与 OpenHarmony 混合开发实战
android·java·开发语言
程序员zgh18 小时前
C语言 指针用法与区别(指针常量、常量指针、指针函数、函数指针、二级指针)
c语言·开发语言·jvm·c++
是一个Bug18 小时前
如何阅读JDK源码?
java·开发语言
石头dhf18 小时前
大模型配置
开发语言·python
inferno18 小时前
JavaScript 基础
开发语言·前端·javascript