简体   繁体   English

访问者模式和策略模式有什么区别?

[英]what is the difference between visitor and strategy pattern?

I have learned both the patterns but did not understand the differences between these two patterns.我已经学习了这两种模式,但不了解这两种模式之间的区别。

I do not know scenarios, when and where to use these patterns.我不知道场景,何时何地使用这些模式。

Can any one explain the differences and use cases?任何人都可以解释差异和用例吗?

The main difference is that the Strategy Pattern encapsulates a single group of related behaviors, while the Visitor Pattern encapsulates multiple such groups.主要区别在于策略模式封装了一组相关的行为,而访问者模式封装了多个这样的组。

  • You should use the Strategy Pattern when you need to encapsulate a behavior - If you have a family of algorithms and you need to choose among them at run time, you should use Strategy Pattern.当你需要封装一个行为时,你应该使用策略模式——如果你有一系列算法并且你需要在运行时从中选择,你应该使用策略模式。 This is very common: it happens every time you program to an interface .这很常见:每次你对接口编程时都会发生。
  • You should use the Visitor Pattern to implement double dispatch - If you have a group of algorithms that need to be virtual in relation to more than one object.您应该使用访问者模式来实现双重分派——如果您有一组算法需要与多个对象相关时是虚拟的。 This is far less common, in part because it is much harder to implement.这种情况不太常见,部分原因是实施起来要困难得多。

Visitor pattern intent:访客模式意图:

Represent an operation to be performed on the elements of an object structure.表示要对对象结构的元素执行的操作。 Visitor lets you define a new operation without changing the classes of the elements on which it operates. Visitor 允许您定义一个新操作,而无需更改它所操作的元素的类。

Use Visitor pattern if:在以下情况下使用访客模式:

  1. Similar operations have to be performed on objects of different types grouped in a structure必须对结构中分组的不同类型的对象执行类似的操作
  2. You need to execute many distinct and unrelated operations.您需要执行许多不同且不相关的操作。 It separates Operation from objects Structure它将操作与对象结构分开
  3. New operations have to be added without change in object structure必须在不改变对象结构的情况下添加新操作
  4. Gather related operations into a single class rather than force you to change or derive classes将相关操作收集到一个类中,而不是强制您更改或派生类
  5. Add functions to class libraries for which you either do not have the source or cannot change the source将函数添加到您没有源代码或无法更改源代码的类库

Even though Visitor pattern provides flexibility to add new operation without changing the existing code in Object, this flexibility has come with a drawback.尽管访问者模式提供了在不更改对象中现有代码的情况下添加新操作的灵活性,但这种灵活性也有一个缺点。

If a new Visitable object has been added, it requires code changes in Visitor & ConcreteVisitor classes.如果添加了新的 Visitable 对象,则需要更改 Visitor 和 ConcreteVisitor 类中的代码。 There is a workaround to address this issue: Use reflection, which will have impact on performance.有一种解决方法可以解决此问题:使用反射,这会对性能产生影响。

Refer to oodesign article and sourcemaking articles for more details有关详细信息,请参阅oodesign 文章sourcemaking文章

Strategy pattern intent:策略模式意图:

Define a family of algorithms, encapsulate each one, and make them interchangeable.定义一系列算法,封装每个算法,并使它们可以互换。 Strategy lets the algorithm vary independently from the clients that use it.策略让算法独立于使用它的客户而变化。

Strategy lets you change the guts of an object.策略让你改变一个对象的内脏。

Refer to below SE questions for more details:有关详细信息,请参阅以下 SE 问题:

Real World Example of the Strategy Pattern 策略模式的真实世界示例

Visitor pattern is used to traverse the object hierarchy and provide some functionality like printing or reporting etc., I used this to provide different formats (Text/HTML) to print an object hierarchy by writing multiple visitors, one for each format.访问者模式用于遍历对象层次结构并提供一些功能,如打印或报告等,我用它来提供不同的格式(文本/HTML),通过编写多个访问者来打印对象层次结构,每种格式一个。 The objects in the hierarchy are the visitables.层次结构中的对象是可访问对象。

Strategy pattern is used to pick a particular logical path based on the input.策略模式用于根据输入选择特定的逻辑路径。 A classic example is authentication filters where based on the value in the Authorization HTTP header, different authentication strategies like NTLM/Negotiate/Basic are picked and run.一个典型的例子是身份验证过滤器,其中基于Authorization HTTP 标头中的值,选择并运行不同的身份验证策略,如 NTLM/Negotiate/Basic。 The filter would hold a reference to the AuthenticationStrategy interface, based on the incoming request, a particular authentication strategy is picked and assigned to this reference and the code that follows doesn't need to know the exact strategy being used.过滤器将持有对 AuthenticationStrategy 接口的引用,基于传入的请求,选择特定的身份验证策略并将其分配给该引用,并且随后的代码不需要知道所使用的确切策略。

Visitor is for when you have a family of classes and you need to add new functionality to every class in that family but not touch the classes themselves (or wish to have that new functionality all defined in one place - the visitor)访问者适用于当你有一个类家族并且你需要向该家族中的每个类添加新功能但不接触类本身(或者希望在一个地方定义所有新功能 - 访问者)

Strategy is for when you have a family of classes that need to be able to do something in order to work properly (such as sort some objects they contain) but you want the client or your dependancy injection to tell them which way to go about doing that.策略适用于当你有一系列类需要能够做一些事情才能正常工作时(例如对它们包含的一些对象进行排序)但你希望客户端或你的依赖注入告诉他们采取哪种方式去做那。

In addition to the behavioural difference mentioned above, I also experienced a difference regarding the dependencies and use cases during a project once I was working on, like the following.除了上面提到的行为差异外,我在进行项目时还遇到了有关依赖项和用例的差异,如下所示。

For example, Visitor knows about concrete classes.例如,Visitor 了解具体类。 So you will be more flexible at the expense of changing your visitor code as you add new concrete classes to the hierarchy.因此,在向层次结构中添加新的具体类时,以更改访问者代码为代价,您将更加灵活。 There's no such thing in Strategy.战略中没有这样的东西。 In this context, Strategy becomes more suitable if you've methods only to return some output with a given input, regardless of the context.在这种情况下,如果您的方法只是通过给定的输入返回一些输出,而不管上下文如何,那么 Strategy 就变得更合适了。

Moreover, Visitor pattern also used to implement SRP of SOLID, to separate concerns.此外,访问者模式还用于实现 SOLID 的 SRP,以分离关注点。

Imagine you are building a cash register app that's building receipts.想象一下,您正在构建一个正在构建收据的收银机应用程序。 And let's say you want to:假设您想:
1. ensure that different kind of items (books, fruit, meat, toiletries etc) are being processed differently 1. 确保对不同种类的物品(书籍、水果、肉类、洗漱用品等)进行不同的处理
2. keep the logic of actual calculation of the price separately from the item definitions 2. 将实际计算价格的逻辑与项目定义分开
3. ensure that if the shop starts selling something completely new (let's say cloth that will be charged by length), you won't have to change too many codes 3.确保如果商店开始销售全新的东西(比方说按长度收费的布料),你不必更改太多代码

Visitor could be a class that defines different kinds of calculations depending on what kind of item is being processed.访问者可以是一个类,它根据正在处理的项目类型定义不同类型的计算。 It's a service that moves away the differences in price calculations away from the hierarchy of items.这是一项将价格计算差异从项目层次结构中移开的服务。
在此处输入图像描述

where the body of getPrice could look like this: getPrice的正文可能如下所示:

getPrice(Calculation c) {
    return c.calculate(this);  // <-- visitor.visit( specific implementation )
}

and in say ShoppingCart you will do:在说ShoppingCart中,您将执行以下操作:

calc = getPriceCalculator()
foreach item in items:
    totalPrice += item.getPrice(calc)

Imagine this scenario: On Black Friday you want to do some crazy discounts, 70% off of all books, 50% off of all fruit.想象一下这个场景:在黑色星期五,你想做一些疯狂的折扣,所有书籍都打折 70%,所有水果都打折 50%。 You can easily do something along the lines:你可以很容易地做一些事情:

BlackFridayCalculator extends PriceCalculator {
    calculate(Book b) { return 0.3 * parent.calculate(b) }
    calculate(Fruit f) { return 0.5 * parent.calculate(f) }
}

// and in getPriceCalculator:
return (black friday time) ? new BlackFridayCalculator() : new PriceCalculator();

Strategy instead could have a hierarchy of a different kind of calculations (strategies) and each item would then define, which calculation (strategy) should be used ("plugged").相反,策略可以具有不同类型的计算(策略)的层次结构,然后每个项目将定义应该使用哪种计算(策略)(“插入”)。

在此处输入图像描述

Now there are many ways how to define which item should use which calculation.现在有很多方法可以定义哪个项目应该使用哪个计算。 The most straightforward would be giving Item method getCalculator and let each item choose which calculation it needs.最直接的方法是提供Item方法getCalculator并让每个项目选择它需要的计算。

This might be a bit less dynamic - in a sense that each item needs to have a predefined calculator that will be used.这可能有点不那么动态——从某种意义上说,每个项目都需要有一个预定义的计算器来使用。 But think of this scenario: Owner of the shop decides that pineapples and watermelons should be sold per piece - we can easily let Fruit use WeightCalculator by default and create a subset of fruit that will be sold by quantity.但是想想这个场景:店主决定菠萝和西瓜应该按件出售——我们可以很容易地让Fruit默认使用WeightCalculator并创建一个按数量出售的水果子集。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM