访客和策略模式之间有什么区别?

时间:2012-11-28 13:37:26

标签: java design-patterns strategy-pattern visitor-pattern

我已经学会了这两种模式但却不理解这两种模式之间的差异。

我不知道场景,何时何地使用这些模式。

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

6 个答案:

答案 0 :(得分:17)

主要区别在于策略模式封装了一组相关行为,而访客模式封装了多个此类组。

  • 当您需要封装行为时,您应该使用策略模式 - 如果您有一系列算法,并且需要在运行时选择它们,应该使用战略模式。这很常见:每次program to an interface时都会发生这种情况。
  • 您应该使用访客模式来实施双重调度 - 如果您有一组需要虚拟的算法不止一个对象。这种情况不太常见,部分原因是因为实施起来要困难得多。

答案 1 :(得分:3)

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

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

答案 2 :(得分:2)

访客模式意图:

  

表示要对对象结构的元素执行的操作。访问者允许您定义新操作,而无需更改其操作的元素的类。

在以下情况下使用访问者模式:

  1. 必须对在结构
  2. 中分组的不同类型的对象执行类似的操作
  3. 您需要执行许多不同且不相关的操作。 它将操作与对象分离
  4. 必须添加新操作而不更改对象结构
  5. 将相关操作集中到单个类中,而不是强迫您更改或派生类
  6. 将函数添加到没有源或无法更改源的类库
  7. 尽管访问者模式提供了在不更改Object中现有代码的情况下添加新操作的灵活性,但这种灵活性还存在缺陷。

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

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

    策略模式意图:

      

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

    策略可让您更改对象的内容。

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

    Real World Example of the Strategy Pattern

答案 3 :(得分:0)

除了上面提到的行为差异之外,我在处理项目期间的依赖关系和用例方面也遇到了不同,如下所示。

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

此外,访客模式也用于实施SOLID的SRP,以分离关注点。

答案 4 :(得分:0)

访问者适用于您拥有一系列课程,并且您需要为该系列中的每个课程添加新功能,但不要自行触摸课程(或希望将所有新功能都添加到一个地方 - 访客)

策略适用于您有一系列需要能够正常工作才能正常工作的类(例如对其中包含的某些对象进行排序),但您希望客户端或您的依赖性注射告诉他们这样做的方法。

答案 5 :(得分:0)

想象一下,您正在构建一个用于创建收据的收银应用。假设您要:
1.确保对不同种类的物品(书籍,水果,肉,洗漱用品等)进行不同的处理
2.将实际计算价格的逻辑与项目定义分开
3.确保如果商店开始销售全新的商品(假设布料将按长度收费),则无需更改太多代码

访问者可以是一个类,它根据要处理的项目类型定义不同的计算类型。这是一项服务,可以消除价格计算中的差异,而不会导致商品层次结构的变化。
enter image description here

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();
相反,

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

enter image description here

现在有很多方法可以定义应该使用哪种计算的项目。最简单的方法是提供Item方法getCalculator,然后让每个项目选择所需的计算方式。

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