API 中的访客模式有什么好处?

What is the benefit of Visitor pattern in an API?

我正在尝试了解在 API 中使用访问者模式的好处。下面的例子是我看到的一个例子,我想要一个例子来说明为什么模式是有益的,即好处。与此相比,负面的替代实施是什么,为什么。从以下实施中可以获得什么好处。在此 api 中,它联系多所大学以获取他们提供的课程。每个获取课程服务然后使用访问者模式有定义数量的响应:

控制器

[HttpGet]
public async Task<IActionResult> Get()
{
    // CourseService already retrieved for a given uni 
    var result = await courseService.GetCourses(userSession);
    return result.Accept(new CourseVisitor());
}

服务 - 每个 Uni 都有自己的 GetCourses 服务,但由于访问者模式,它们都设置了响应

public async Task<CoursesResult> GetCourses(UserSession userSession) {

// Depending on response from a given uni a set number of responses can be returned across ass uni services e.g
return new CoursesResult.BadRequest(); **or**
return new CoursesResult.Success(); etc
}

元素抽象/具体元素

  public abstract class GetCourses
    {
        public abstract T Accept<T>(ICourseVisitor<T> visitor);

        public class Successful : CoursesResult
        {
            public CourseList Response { get; }

            public Successful(CourseList response)
            {
                Response = response;
            }

            public override T Accept<T>(ICourseVisitor<T> visitor)
            {
                return visitor.Visit(this);
            }
        }
   // Other responses then defined e.g Bad Request

IVisitor

    public interface ICourseVisitor<out T>
{
    T Visit(GetCoursesResult.Successful result);
    T Visit(GetCoursesResult.BadRequest result);

访客

    internal class CourseVisitor : ICourseVisitor<IActionResult>
{
    public IActionResult Visit(GetCourses.Successful result)
    {
        return new OkObjectResult(result.Response);
    }

更新查询 此外,我试图理解为什么该服务不能 return 这样的事情:

//Service returns this: return new Successful(listResponse)

 public interface ICoursesResult
    {
      IActionResult Accept();
    }

 public class Successful : ICoursesResult
    {
        public CourseList Response { get; }

        public Successful(CourseList response)
        {
            Response = response;
        }

        public IActionResult Accept()
        {
            return OkObjectResult(Response);
        }
    }

在代码项目中对此进行了广泛的研究 - Visitor Pattern ReExplained

我来提供标题。

访问者模式是用来解决问题的,主要表现在两个方面:

  • 有迭代机制,知道怎么迭代Object hirerachy.it 对 object 的行为一无所知 层次结构。
  • 需要实现的新行为一无所知 迭代机制,他们不知道如何迭代 object 层次结构。

现在这两个这方面,是相互独立的,不能混为一谈。所以,这就是关于 OOP 原则的全部内容,称为 单一职责原则 ,带大家回到 SOLID architecture


此功能的主要参与者是:

  • Visitor - 定义访问操作的接口。这是 访问者模式的核心。它为每种类型定义了一个 Visit 操作 object 结构中的 Concreate 元素。
  • ConcreateVisitor - 实现Visitor中定义的操作 界面。
  • ElementBase :它是一个定义 Accept 的 Abstract/Interface 以访问者为参数的操作。
  • ConcreateElement - 这些类型实现了 Element 的 Accept 方法 界面。
  • Object 结构 - 它将数据结构的所有元素保存为 一个collection,列表或可以被枚举和使用的东西 访客。它为所有访问者提供访问它的接口 元素。这些元素包括名为“Accept”的方法。 collection然后枚举

现在,所有这些模式的目的,关键,是创建具有有限功能的数据模型和一组具有特定功能的访问者,这些访问者将对数据进行操作。该模式允许访问者访问数据结构的每个元素,将 object 作为参数传递给访问者方法。


毕竟是好处-

将算法与其数据模型分离的关键是能够轻松添加新行为。 classes 数据模型是使用名为 Visit 的通用方法创建的,该方法可以在 运行 时间接受访问者 object。然后可以创建不同的访问者 object 并将其传递给此方法,然后此方法回调访问者方法并将其自身作为参数传递给它。

另外值得一提的是:

将新类型添加到 object 层次结构需要对所有访问者进行更改,这应该被视为一个优势,因为它肯定会迫使我们将新类型添加到您保留一些 type-specific代码。基本上它不会让你忘记它。

访问者模式仅有用:

  • 如果你想要实现的接口是相当静态的,而不是 变化很大。
  • 如果事先知道所有类型,即在设计时所有 objects 必须知道。

最后一行:

访问者实现了以下设计原则

  • 关注点分离 - 访问者模式促进了这一原则, 多个 aspects/concerns 与多个其他 class 分开,如 它鼓励更简洁的代码和代码 re-usability,而且代码是 更可测试。
  • 单一职责原则 - 访问者模式也强制执行此原则 原则。一个object应该有差不多一个职责。无关 行为必须与它分开 class.
  • Open Close Principle - 访问者模式也遵循这个原则, 就好像,我们想扩展一个 object 行为,原始源代码 没有改变。访问者模式为我们提供了一种机制 将它分离到另一个 class 并将这些操作应用于 object 在 运行 时间。

访问者实施的好处是

  • 将数据结构的行为与它们分开。单独的访客 object 是为了实现这种行为而创建的。
  • 它解决了很少遇到但已经遇到的双重调度问题 重要影响。

您可以深入阅读本文以了解其全部含义,但如果我要举一个例子:

首先我们将定义我们称之为 IVisitable 的接口。它将定义一个接受 IVisitor 参数的单一 Accept 方法。该接口将作为产品列表中所有类型的基础。 Book、Car 和 Wine(在我们的示例中)等所有类型都将实现此类型。

/// <summary>
/// Define Visitable Interface.This is to enforce Visit method for all items in product.
/// </summary>
internal interface IVisitable
{
    void Accept(IVisitor visit);
}   

那我们来实现一下:

  #region "Structure Implementations"


    /// <summary>
    /// Define base class for all items in products to share some common state or behaviors.
    /// Thic class implement IVisitable,so it allows products to be Visitable.
    /// </summary>
    internal abstract class Product : IVisitable
    {
        public int Price { get; set; }
        public abstract void Accept(IVisitor visit);
    }

    /// <summary>
    /// Define Book Class which is of Product type.
    /// </summary>
    internal class Book : Product
    {
        // Book specific data

        public Book(int price)
        {
            this.Price = price;
        }
        public override void Accept(IVisitor visitor)
        {
            visitor.Visit(this);
        }
    }

    /// <summary>
    /// Define Car Class which is of Product type.
    /// </summary>
    internal class Car : Product
    {
        // Car specific data

        public Car(int price)
        {
            this.Price = price;
        }

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

    /// <summary>
    /// Define Wine Class which is of Product type.
    /// </summary>
    internal class Wine : Product
    {
        // Wine specific data
        public Wine(int price)
        {
            this.Price = price;
        }

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

    #endregion "Structure Implementations"  

创建访客界面并实现:

/// <summary>
/// Define basic Visitor Interface.
/// </summary>
internal interface IVisitor
{
    void Visit(Book book);
    void Visit(Car car);
    void Visit(Wine wine);
}

#region "Visitor Implementation"


/// <summary>
/// Define Visitor of Basic Tax Calculator.
/// </summary>
internal class BasicPriceVisitor : IVisitor
{
    public int taxToPay { get; private set; }
    public int totalPrice { get; private set; }

    public void Visit(Book book)
    {
        var calculatedTax = (book.Price * 10) / 100;
        totalPrice += book.Price + calculatedTax;
        taxToPay += calculatedTax;
    }

    public void Visit(Car car)
    {
        var calculatedTax = (car.Price * 30) / 100;
        totalPrice += car.Price + calculatedTax;
        taxToPay += calculatedTax;
    }

    public void Visit(Wine wine)
    {
        var calculatedTax = (wine.Price * 32) / 100;
        totalPrice += wine.Price + calculatedTax;
        taxToPay += calculatedTax;
    }
}


#endregion "Visitor Implementation"

执行:

 static void Main(string[] args)
    {
        Program.ShowHeader("Visitor Pattern");

        List<Product> products = new List<Product>
        { 
            new Book(200),new Book(205),new Book(303),new Wine(706)
        };

        ShowTitle("Basic Price calculation");
        BasicPriceVisitor pricevisitor = new BasicPriceVisitor();
        products.ForEach(x =>
        {
            x.Accept(pricevisitor);
        });

        Console.WriteLine("");
    }     

当你有一个多态类型并且你想根据对象的特定子类型执行外部定义的操作时,通常会使用访问者模式。在您的示例中,CoursesResult 是一种多态类型,访问者允许您将 Successful 响应转换为 OkObjectResult,而无需直接耦合这两种类型。

您的替代方法 CoursesResult 直接 return 是 IActionResult 是传统的多态性,它更简单但将域逻辑耦合到 MVC 层。

现在,我不知道你的全套回复是什么样子的,但如果你只有一个成功的回复,其余都是错误的,那么这里最简单的方法是直接 return 成功的其他情况的响应和抛出异常:

public async Task<CourseList> GetCourses(UserSession userSession) {
  return courseList; /* or */ throw new BadRequestException();
}

然后您的控制器可以简单地捕获异常并将它们转换为适当的 IActionResult