14

我已经学习了这两种模式,但不了解这两种模式之间的区别。

我不知道使用这些模式的场景、时间和地点。

任何人都可以解释差异和用例吗?

4

6 回答 6

25

主要区别在于策略模式封装了一组相关行为,而访问者模式封装了多个这样的组。

  • 当您需要封装行为时,您应该使用策略模式 - 如果您有一系列算法并且需要在运行时在它们之间进行选择,您应该使用策略模式。这很常见:每次对接口编程时都会发生这种情况。
  • 您应该使用访问者模式来实现双重分派- 如果您有一组算法需要相对于多个对象是虚拟的。这要少得多,部分原因是实施起来要困难得多。
于 2012-11-28T13:43:48.850 回答
5

访客模式意图:

表示要对对象结构的元素执行的操作。Visitor 允许您定义一个新的操作,而无需更改它所操作的元素的类。

在以下情况下使用访客模式:

  1. 必须对结构中分组的不同类型的对象执行类似的操作
  2. 您需要执行许多不同且不相关的操作。它将操作与对象结构分开
  3. 必须在不改变对象结构的情况下添加新操作
  4. 将相关操作收集到单个类中,而不是强迫您更改或派生类
  5. 将函数添加到您没有源或无法更改源的类库

尽管访问者模式提供了在不更改 Object 中现有代码的情况下添加新操作的灵活性,但这种灵活性也有一个缺点。

如果添加了新的 Visitable 对象,则需要更改 Visitor 和 ConcreteVisitor 类中的代码。有一个解决方法可以解决这个问题:使用反射,这会对性能产生影响。

有关详细信息,请参阅oodesign 文章sourcemaking文章

策略模式意图:

定义一系列算法,封装每个算法,并使它们可互换。策略让算法独立于使用它的客户端而变化。

策略可以让你改变一个对象的胆量。

有关详细信息,请参阅以下 SE 问题:

策略模式的真实示例

于 2016-06-03T10:16:54.087 回答
3

访问者模式用于遍历对象层次结构并提供一些功能,例如打印或报告等,我使用它来提供不同的格式(文本/HTML)来通过编写多个访问者来打印对象层次结构,每种格式一个。层次结构中的对象是可访问对象。

策略模式用于根据输入选择特定的逻辑路径。一个典型的例子是身份验证过滤器,其中基于AuthorizationHTTP 标头中的值,选择并运行不同的身份验证策略,如 NTLM/Negotiate/Basic。过滤器将持有对 AuthenticationStrategy 接口的引用,根据传入的请求,选择特定的身份验证策略并将其分配给该引用,并且随后的代码不需要知道正在使用的确切策略。

于 2012-11-28T13:43:22.533 回答
3

访问者适用于当您有一个类族并且您需要为该族中的每个类添加新功能但不涉及类本身(或希望将新功能全部定义在一个地方 - 访问者)时

策略适用于当您有一系列类需要能够做某事才能正常工作(例如对它们包含的一些对象进行排序)但您希望客户端或您的依赖注入告诉他们该做些什么时那。

于 2018-02-18T16:46:16.017 回答
0

想象一下,您正在构建一个用于生成收据的收银机应用程序。假设您想要:
1. 确保不同种类的物品(书籍、水果、肉类、洗漱用品等)的处理方式不同
2. 将实际价格计算的逻辑与物品定义分开
3. 确保如果商店开始销售全新的东西(比方说按长度收费的布料),您不必更改太多代码

访问者可以是一个类,它根据正在处理的项目类型定义不同类型的计算。这是一项将价格计算中的差异从项目层次结构中移开的服务。
在此处输入图像描述

的身体getPrice可能看起来像这样:

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

并说ShoppingCart你会做:

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

想象一下这样的场景:在黑色星期五,你想做一些疯狂的折扣,所有书籍的 70% 折扣,所有水果的 50% 折扣。您可以轻松地做一些事情:

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();

相反,策略可以具有不同类型的计算(策略)的层次结构,然后每个项目将定义应该使用哪个计算(策略)(“插入”)。

在此处输入图像描述

现在有很多方法可以定义哪个项目应该使用哪个计算。最直接的方法是给出Item方法getCalculator,让每个项目选择它需要的计算。

这可能不太动态 - 从某种意义上说,每个项目都需要有一个预定义的计算器才能使用。但是想想这种情况:商店的老板决定菠萝和西瓜应该按件出售——我们可以很容易地默认Fruit使用WeightCalculator并创建一个按数量出售的水果子集。

于 2020-01-19T07:29:21.090 回答
0

除了上面提到的行为差异之外,我还经历了一个项目期间在依赖项和用例方面的差异,如下所示。

例如,Visitor 知道具体的类。因此,在向层次结构中添加新的具体类时,您会以更改访问者代码为代价变得更加灵活。战略中没有这样的东西。在这种情况下,如果您的方法只返回带有给定输入的一些输出,而不管上下文如何,则 Strategy 变得更合适。

此外,访问者模式还用于实现 SOLID 的 SRP,以分离关注点。

于 2016-11-19T12:45:37.763 回答