我无法理解这两种设计模式。
你能否给我一些背景信息或一个例子,这样我就可以得到一个清晰的想法,并能够绘制两者之间的差异。
感谢。
答案 0 :(得分:77)
以下是我的看法。 策略模式就像 1:很多关系。当有一种类型的对象并且我想对它应用多个操作时,我使用策略模式。例如,如果我有一个封装视频剪辑的Video类,我可能想以不同的方式压缩它。所以我创建了一堆策略类:
MpegCompression
AviCompression
QuickTimeCompression
等等。
我认为访客模式是很多:很多关系。假设我的应用程序不仅包括视频,还包括音频剪辑。如果我坚持使用策略模式,我必须复制我的压缩类 - 一个用于视频,一个用于音频:
MpegVideoCompression
MpegAudioCompression
依旧......
如果我切换到访客模式,我不必复制策略类。我通过添加方法来实现我的目标:
MpegCompression::compressVideo(Video object)
MpegCompression::compressAudio(Audio object)
答案 1 :(得分:11)
策略模式用于将各种算法暴露给标准化接口。一个典型的例子可以是一个排序实用程序,它允许用户(程序员)在各种排序算法之间进行选择,每个排序算法都通过相同的接口调用。
访客模式生活在不同的层面。它详细说明了一种机制,通过该机制,对象可以接受对另一个对象(访问者)的引用,该对象公开了目标对象可以调用的预定接口。当然,不同的访问者会呈现相同的界面,但具有不同的实现。
回到我们的例子,可以通过策略模式或访问者模式实现排序算法的集合。
使用策略方法,每个算法都呈现相同的接口,并将目标对象的数组作为参数。使用访问者模式,它将是将“访问”算法作为参数的目标数组。在这种情况下,目标将“接受()”选定的访问者,并在我们的示例中调用目标的排序方法时调用其“visit()”方法。
同一枚硬币的两面......
这有意义吗?
答案 2 :(得分:5)
访客就像一夜情 - 你在调用accept函数时创建它然后它们被分开并且访问者可以从内存中清除,它不会占用使用它的类的任何空间
战略就像一场婚姻 - 你创造了一个对象,它生活在一个使用它的类中,占用记忆,有一个房间并在早上让自己变成咖啡:)。 当然,他们可以离婚并转到另一个班级,但该班级也会生活在其所有者的背景下。
希望它能帮助你记住:)
答案 3 :(得分:5)
访问者是一种策略,但是具有多种方法,并且它允许Double调度。访客还允许在运行时在两个具体对象之间进行安全绑定。
注意:这是用Java编写的示例。例如,C#引入了dynamic
关键字,因此在C#中,双重分派的示例没有用。
考虑以下示例和输出:
package DesignPatterns;
public class CarGarageStrategyDemo
{
public static interface RepairStrategy
{
public void repair(Car car);
}
public static interface Car
{
public String getName();
public void repair(RepairStrategy repairStrategy);
}
public static class PorscheRepairStrategy implements RepairStrategy
{
@Override
public void repair(Car car) {
System.out.println("Repairing " + car.getName() + " with the Porsche repair strategy");
}
}
public static class FerrariRepairStrategy implements RepairStrategy
{
@Override
public void repair(Car car) {
System.out.println("Repairing " + car.getName() + " with the Ferrari repair strategy");
}
}
public static class Porsche implements Car
{
public String getName()
{
return "Porsche";
}
@Override
public void repair(RepairStrategy repairStrategy) {
repairStrategy.repair(this);
}
}
public static void main(String[] args)
{
Car porsche = new Porsche();
porsche.repair(new PorscheRepairStrategy()); //Repairing Porsche with the porsche repair strategy
}
}
如果策略与主题之间没有直接关系,则Strategy
模式可以正常工作。例如,我们不希望发生以下情况:
...
public static void main(String[] args)
{
Car porsche = new Porsche();
porsche.repair(new FerrariRepairStrategy()); //We cannot repair a Porsche as a Ferrari!
}
...
因此,在这种情况下,我们可以使用访客模式。
考虑以下代码:
public class CarGarageVisitorProblem
{
public static interface Car
{
public String getName();
}
public static class Porsche implements Car
{
public String getName()
{
return "Porsche";
}
}
public static class Ferrari implements Car
{
public String getName()
{
return "Ferrari";
}
}
public void repair(Car car)
{
System.out.println("Applying a very generic and abstract repair");
}
public void repair(Porsche car)
{
System.out.println("Applying a very specific Porsche repair");
}
public void repair(Ferrari car)
{
System.out.println("Applying a very specific Ferrari repair");
}
public static void main(String[] args)
{
CarGarageVisitorProblem garage = new CarGarageVisitorProblem();
Porsche porsche = new Porsche();
garage.repair(porsche); //Applying a very specific Porsche repair
}
}
输出为Applying a very specific Porsche repair
。
问题在于,这条线不是抽象的,而是具体的:
Porsche porsche = new Porsche();
我们想将其编写为(或在构造函数中注入Car的实例,我们要应用Dependency Inversion Principle
):
Car porsche = new Porsche();
但是当我们更改此行时,输出将是:
Applying a very generic and abstract repair
不是我们想要的!
package DesignPatterns;
public class CarGarageVisitorExample
{
public static interface Car
{
public String getName();
public void repair(RepairVisitorInterface repairVisitor);
}
public static class Porsche implements Car
{
public String getName()
{
return "Porsche";
}
public void repair(RepairVisitorInterface repairVisitor)
{
repairVisitor.repair(this);
}
}
public static class Ferrari implements Car
{
public String getName()
{
return "Ferrari";
}
public void repair(RepairVisitorInterface repairVisitor)
{
repairVisitor.repair(this);
}
}
public static interface RepairVisitorInterface
{
public void repair(Car car);
public void repair(Porsche car);
public void repair(Ferrari car);
}
public static class RepairVisitor implements RepairVisitorInterface
{
public void repair(Car car)
{
System.out.println("Applying a very generic and abstract repair");
}
public void repair(Porsche car)
{
System.out.println("Applying a very specific Porsche repair");
}
public void repair(Ferrari car)
{
System.out.println("Applying a very specific Ferrari repair");
}
}
public static void main(String[] args)
{
CarGarageVisitor garage = new CarGarageVisitor();
Car porsche = new Porsche();
porsche.repair(new RepairVisitor()); //Applying a very specific Porsche repair
}
}
由于方法重载,访问者与主题(汽车)之间存在具体的约束。由于保时捷使用方法重载,因此无法将其作为法拉利进行维修。我们还通过实现以下方法解决了先前说明的问题(我们不能使用Dependency Inversion
):
public void repair(RepairVisitorInterface repairVisitor)
{
repairVisitor.repair(this);
}
this
引用将返回对象的具体类型,而不是抽象(Car)类型。
答案 4 :(得分:2)
定义的区别在于,使用运算符重载,Visitor为元素的子类提供了不同的行为。它知道它正在处理或访问的事情。
与此同时,战略将在其所有实施中保持一致的界面。访问者用于允许对象的子部分使用一致的做法。策略用于允许依赖注入如何做某事。
所以这将成为访客:
class LightToucher : IToucher{
string Touch(Head head){return "touched my head";}
string Touch(Stomach stomach){return "hehehe!";}
}
使用此类型的另一个
class HeavyToucher : IToucher{
string Touch(Head head){return "I'm knocked out!";}
string Touch(Stomach stomach){return "oooof you bastard!";}
}
我们有一个类可以使用此访问者来完成其工作,并根据它进行更改:
class Person{
IToucher visitor;
Head head;
Stomach stomach;
public Person(IToucher toucher)
{
visitor = toucher;
//assume we have head and stomach
}
public string Touch(bool aboveWaist)
{
if(aboveWaist)
{
visitor.Touch(head);
}
else
{
visitor.Touch(stomach);
}
}
}
所以,如果我们这样做 var person1 = new Person(new LightToucher()); var person2 = new Person(new HeavyToucher());
person1.Touch(true); //touched my head
person2.Touch(true); //knocked me out!
答案 5 :(得分:1)
我将策略模式视为将方法/策略注入对象的一种方式,但通常该方法的签名需要一些值参数并返回结果,因此它不会与策略的用户耦合: 来自Wikipedia:
class Minus : ICalculateStrategy {
public int Calculate(int value1, int value2) {
return value1 - value2;
}
}
访问者通过双重调度与用户耦合,通常保持状态。 好的例子here,我只是从那里复制:
public class BlisterPack
{
// Pairs so x2
public int TabletPairs { get; set; }
}
public class Bottle
{
// Unsigned
public uint Items { get; set; }
}
public class Jar
{
// Signed
public int Pieces { get; set; }
}
public class PillCountVisitor : IVisitor
{
public int Count { get; private set; }
#region IVisitor Members
public void Visit(BlisterPack blisterPack)
{
Count += blisterPack.TabletPairs * 2;
}
public void Visit(Bottle bottle)
{
Count += (int) bottle.Items;
}
public void Visit(Jar jar)
{
Count += jar.Pieces;
}
#endregion
}
public class BlisterPack : IAcceptor
{
public int TabletPairs { get; set; }
#region IAcceptor Members
public void Accept(IVisitor visitor)
{
visitor.Visit(this);
}
#endregion
}
正如您所看到的,访问者具有状态(public int Count),并且它在已知类型的列表上运行BlisterPack,Bottle,Jar。因此,如果您想支持新类型,则需要通过添加该类型来更改所有访问者。
此外,由于" visitor.Visit(此);",它与其操作的类型相结合。如果我删除或更改"项目"会发生什么?物业形式瓶? ......所有访客都会失败。
答案 6 :(得分:0)
似乎第二个图是访问者模式给我...因为对于策略模式,类包含的数据结构往往只有一个,没有子类(或者子类保持这部分的相同行为)。该策略适用于同一结构的不同操作。
答案 7 :(得分:0)
如果您只有一个上下文或元素,并且需要对该上下文执行不同的操作,那么您可以选择Strategy Pattern
。这是上面回答中提到的1:M
relationship
。 java.util.Comparator
是战略设计模式的一个很好的例子。在那里,我们可以为同一个集合(上下文或元素)提供不同的排序策略。
另一方面,假设您有多个elenents都符合共同合同,并且需要对每个elenents执行不同的操作。例如,考虑使用车身,发动机和车轮等的洗车用品,每个都可以使用蒸汽或水清洗。这是Visitor Pattern
的好用。但请确保您的上下文元素保持不变并且永远不会更改。如果元素将要更改,请说Door
元素添加到Car
,那么您需要更改所有访问者,在每个访问者中添加一个新方法并违反OCP
性质图案。所以,这是上述答案中陈述的M:N
关系。
如果您对进一步了解这两个Design Patterns
之间的微妙差异感兴趣,建议您阅读this article。
答案 8 :(得分:0)
我会尽力回答最短的问题。
这两种模式是相辅相成的:例如,您可以使用访问者来更改图的所有节点上的策略。
答案 9 :(得分:0)
如果我们从GoF书中查看这两种模式的UML,就会发现它们完全不同。
访问者:
策略:
图中有一些重要的区别。
UML本身并不能捕捉驱动这些模式的不同动机。
答案 10 :(得分:-6)