ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2022-09-08 08:31:14  阅读:214  来源: 互联网

标签: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,访问者
来源: https://www.cnblogs.com/harley-chang/p/16667973.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有