首页 > 其他分享 >【设计模式篇】访问者模式(Visitor)

【设计模式篇】访问者模式(Visitor)

时间:2022-09-08 08:11:07浏览次数:89  
标签:timeElement 对象 Visitor void Visit 设计模式 public 访问者

访问者模式

定义(GoF):表示一个作用于某对象结构中的各种操作,它使你在不改变各个元素类的前提下定义作用于这些元素的新操作。

先通过结构图,来了解访问者模式

应用场景

我们先看一下访问者模式中都有哪些角色
实际上访问者模式中有三类对象,访问者、元素对象、对象结构,核心要做的事情都是在具体的访问者中进行

  • 抽象访问者:定义了一些抽象方法,方法数量通常与具体的元素对象个数相同,同时这些方法会依赖于具体元素对象。
  • 具体访问者对象:继承抽象访问者类,实现抽象方法,并根据当前访问者表达的含义,构建成员方法的逻辑。
  • 抽象元素:可以对某一类对象进行抽象,并且这些对象种类比较稳定,同时定义一个抽象方法,方法会依赖于抽象访问者,通常此方法名被定义为Accept。
  • 具体元素对象:继承抽象元素类,实现抽象方法,通常方法内逻辑比较固定,通过访问者调用访问者方法visitor,并将当前元素类的引用作为参数传入其中。
  • 对象结构:对元素对象进行集合存储,向外提供对元素对象的添加、删除等操作,一般会提供一个方法,参数是访问者对象,通过访问者对象的Visit方法,对集合中元素进行遍历调用。

访问者模式适用于对象种类相对稳定的场景,它解决的实际上是将对象与作用于对象的行为进行分离解耦,使得在不更改对象结构的前提下,可以更灵活地对作用于对象的行为操作进行更改。

示例应用

场景一:
比如一天有三个重要时刻,分别为早中晚,我们对Harley、Baffett进行调查,观察他们在这些时刻都做了什么事情。

因为早中晚是相对稳定的时刻,我们可以将早中晚三个时刻声明三个元素对象,把Harley、Buffett声明为两个具体访问者对象,然后会在具体访问者对象中描述访问者的具体行为。

示例结构图

Environment:
Visual Studio Code、.Net 6

相关命令:

// 创建项目文件夹
mkdir DesignPattern
// 创建项目文件
dotnet new sln
// 创建访问者控制台文件夹
mkdir DesignPattern.Visitor.Console
// 创建访问者控制台项目
dotnet new console
// 运行项目
dotnet run

代码片段一

public abstract class TimeElement
{
    public TimeSpan Begin { get; set; }
    public TimeSpan End { get; set; }
    public abstract void Accept(Visitor visitor);
}

public class MorningTimeElement : TimeElement
{
    public MorningTimeElement()
    {
        Begin = TimeSpan.FromHours(6);
        End = TimeSpan.FromHours(11);
    }
    public override void Accept(Visitor visitor)
    {
        visitor.Visit(this);
    }
}

public class MorningTimeElement : TimeElement
{
    public MorningTimeElement()
    {
        Begin = TimeSpan.FromHours(6);
        End = TimeSpan.FromHours(11);
    }
    public override void Accept(Visitor visitor)
    {
        visitor.Visit(this);
    }
}

public class AfterNoonElement : TimeElement
{
    public AfterNoonElement()
    {

        Begin = TimeSpan.FromHours(13);
        End = TimeSpan.FromHours(18);
    }
    public override void Accept(Visitor visitor)
    {
        visitor.Visit(this);
    }
}

代码片段二

public abstract class Visitor
{
    public abstract void Visit(MorningTimeElement timeElement);
    public abstract void Visit(NoonTimeElement timeElement);
    public abstract void Visit(AfterNoonElement timeElement);
}

public class HarleyVisitor : Visitor
{
    public override void Visit(MorningTimeElement timeElement)
    {
        System.Console.WriteLine($"Harley 在 {timeElement.Begin:hh\\:mm} 起床,然后开始学习、工作,在 {timeElement.End:hh\\:mm}开始午休");
    }

    public override void Visit(NoonTimeElement timeElement)
    {
        System.Console.WriteLine($"Harley 在 {timeElement.Begin:hh\\:mm} 准备吃午餐,并在{timeElement.End:hh\\:mm}前进行午睡");
    }

    public override void Visit(AfterNoonElement timeElement)
    {
        System.Console.WriteLine($"Harley 在 {timeElement.Begin:hh\\:mm} 开始工作,在 {timeElement.End:hh\\:mm} 下班休息");
    }
}

public class BuffettVisitor : Visitor
{
    public override void Visit(MorningTimeElement timeElement)
    {
        System.Console.WriteLine($"Buffett 在 {timeElement.Begin:hh\\:mm} 还在睡梦中,大约45分钟后起床,然后开始准备开会,在 {timeElement.End:hh\\:mm}开始午休");
    }

    public override void Visit(NoonTimeElement timeElement)
    {
        System.Console.WriteLine($"Buffett 在 {timeElement.Begin:hh\\:mm} 准备吃午餐,并在{timeElement.End:hh\\:mm}前进行准备下午会议");
    }

    public override void Visit(AfterNoonElement timeElement)
    {
        System.Console.WriteLine($"Buffett 在 {timeElement.Begin:hh\\:mm} 开始进行下午会议,在 {timeElement.End:hh\\:mm} 下班休息");
    }
}

代码片段三

public class ObjectStructure
{
    private IList<TimeElement> elements = new List<TimeElement>();

    public void Add(TimeElement element) => elements.Add(element);

    public void Delete(TimeElement element) => elements.Remove(element);

    public void Accept(Visitor visitor)
    {
        foreach (var item in elements)
        {
            item.Accept(visitor);
        }
    }
}

客户端

Console.WriteLine("Hello, Harley!");
Console.OutputEncoding = System.Text.Encoding.UTF8;

var morningElement = new MorningTimeElement();
var noonElement = new NoonTimeElement();
var afterNoonElement = new AfterNoonElement();

var objectStructure = new ObjectStructure();
objectStructure.Add(morningElement);
objectStructure.Add(noonElement);
objectStructure.Add(afterNoonElement);

var buffettVisitor = new BuffettVisitor();
var harleyVisitor = new HarleyVisitor();

System.Console.WriteLine("******buffett visitor");
objectStructure.Accept(buffettVisitor);
System.Console.WriteLine("******harley visitor");
objectStructure.Accept(harleyVisitor);

Console.Read();

在控制台执行dotnet run,结果如下:

总结
访问者模式是行为型设计模式,关注的是对象与行为的分离。适用于对象结构相对稳定,作用于对象结构上的操作又可以独立演化的场景。在对象结构上增加操作就是创建一个具体的访问者对象,并且完全遵循了开闭原则,降低了对象与行为的耦合性,但是它同时也有很大的缺点,不适用于对象结构易于变化的场景,同时违反了依赖倒置原则、迪米特法则。

所以,只有在业务比较合适的场景,才能发挥访问者模式的优势。

优点:

  • 增加新的操作很容易,只需要添加一个具体的访问者对象,无需变动对象结构、元素对象。
  • 遵循了开闭原则,使得增加新的处理逻辑变得简单。
  • 遵循了里氏替换原则。

缺点:

  • 对象结构增加新的对象会很困难。
  • 违反了依赖倒置原则,抽象不依赖于细节,细节依赖于抽象,因为在抽象访问者的方法中,依赖了具体元素的实现。
  • 违反了迪米特法则,最小知道原则。

References:

《Head First 设计模式》
《大话设计模式》

标签:timeElement,对象,Visitor,void,Visit,设计模式,public,访问者
From: https://www.cnblogs.com/harley-chang/p/16667973.html

相关文章

  • 13.1 反射 13.2selenium键盘事件13.3文件上传13.4滚动条操作 13.5鼠标事件13.6验证码
    13.1反射#什么是反射?#如果有一个变量名,是字符串的数据类型,你能获取到这个变量的值吗?#反射方法:classStudent:def__init__(self):self.name='张三'......
  • 设计模式_多例模式
    看个例子:有个线程池有五个线程,每次随机使用一个? 定义模拟线程类【SimulationThread】,定义线程名变量【threadName】,定义线程运行方法【runThread】。   定义线程......
  • 设计模式_单例模式
    先看个例子:公司中只能有一个老板。 定义【Boss】类,定义属性【name】。   执行结果:  总结:单例模式主要用于不同线程之间的并发访问或者通信,也可以达到节约资源......
  • 设计模式
    设计模式您是否曾经在一个团队中对如何实现某些功能没有清晰的想法?这是开发人员的常见情况,所以很多时候我们都在处理类似的功能,总是使用“对象”定义,处理表单的新屏幕,或者......
  • 【设计模式】Java设计模式 - 原型模式
    【设计模式】Java设计模式-原型模式......
  • 设计模式-责任链模式(Java实现)
    什么是责任链模式?顾名思义,就是链式的去处理一个请求或者任务,收到请求后,每个处理者均可对请求进行处理,或将其传递给链上的下个处理者。应用场景一个请求需要多种类型......
  • 设计模式原则之“迪米特法则”
    一、是什么迪米特法则:也叫最少知识原则(LoD)如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用如果其中一个类需要调用另一个类的某一个方法的话,可以通......
  • 设计模式之解释器模式
    解释器模式字面意思,也即解释某些内容的含义。这种设计模式是实际开发中最不容易用到的。比如SQL解析,符号处理引擎,会用到解释器模式,属于更底层的开发人员才会用到的设计模式......
  • 设计模式--生成器模式
    简介生成器模式的核心是当构建一个对象的时候,需要包含多个步骤,虽然每个步骤具体的实现不同,但是都遵循一定的流程和规则。比如组装一辆汽车,需要引擎、座位、变速箱、定位器......
  • 设计模式
    1.单例模式:只能创建一个实例的对象。2.单例模式分类两种:饿汉式:类加载就会导致该对象被创建。静态变量的方式:静态代码块方式懒汉式:类加载不会导致该单例对象被创建,而......