我真的很困惑访客模式及其用途。我似乎无法想象使用这种模式或其目的的好处。如果有人能够用例子来解释,如果可能的话,那会很好。访客模式的用途与例子
回答
曾几何时......
class MusicLibrary {
private Set<Music> collection ...
public Set<Music> getPopMusic() { ... }
public Set<Music> getRockMusic() { ... }
public Set<Music> getElectronicaMusic() { ... }
}
然后你意识到你希望能够通过其他类型过滤图书馆的馆藏。你可以不断添加新的getter方法。或者你可以使用访问者。
interface Visitor<T> {
visit(Set<T> items);
}
interface MusicVisitor extends Visitor<Music>;
class MusicLibrary {
private Set<Music> collection ...
public void accept(MusicVisitor visitor) {
visitor.visit(this.collection);
}
}
class RockMusicVisitor implements MusicVisitor {
private final Set<Music> picks = ...
public visit(Set<Music> items) { ... }
public Set<Music> getRockMusic() { return this.picks; }
}
class AmbientMusicVisitor implements MusicVisitor {
private final Set<Music> picks = ...
public visit(Set<Music> items) { ... }
public Set<Music> getAmbientMusic() { return this.picks; }
}
您将数据从算法中分离出来。您将算法卸载到访客实现。您可以通过创建更多访问者来添加功能,而不是不断修改(并鼓胀)保存数据的类。
的声音对不起,这对Visitor模式来说太简单并不是一个很好的例子。访问者模式的主要机制之一,通过访问元素的类型(双派遣)选择功能没有显示-1 – 2010-04-11 12:50:00
在参加了一个编译器课程之后,我也意识到这个例子是多么毫无意义。 – 2011-05-06 19:27:04
@HaraldScheirich访客可能会也可能不会选择按类型选择功能。即使没有这些,我发现访客是非常有用的。 – DJClayworth 2011-12-01 15:57:29
它提供了另一层抽象。降低对象的复杂性并使其更加模块化。 Sorta就像使用一个接口一样(实现是完全独立的,没有人关心它是如何完成的)。
现在我从来没有使用过它,但它对于:实现一个需要的特定函数在不同的子类中完成,因为每个子类都需要以不同的方式实现它,所以另一个类将实现所有的功能。有点像一个模块,但仅用于一组类。维基百科有一个很好的解释:http://en.wikipedia.org/wiki/Visitor_pattern 他们的例子有助于解释我想说的。
希望能帮助我们澄清一点。
编辑**对不起,我链接到维基百科你的答案,但他们确实有一个体面的例子:)不要试图成为那个说去找到它自己的人。
这种解释类似于战略模式 – Waclock 2016-08-18 16:15:36
它是将数据操作与实际数据分开。作为奖励,您可以在类的整个层次结构中重复使用相同的访问者类,这样可以避免携带与您的实际对象无关的数据操作算法。
因此,您可能已经阅读了关于访问者模式的不同解释,并且您可能仍然在说“但是您什么时候可以使用它!”
传统上,访问者习惯于在不牺牲类型安全性的情况下实施类型测试,只要您的类型事先已知并且事先已知就可以了。比方说,我们有几类,如下所示:
abstract class Fruit { }
class Orange : Fruit { }
class Apple : Fruit { }
class Banana : Fruit { }
而且我们说,我们创建一个Fruit[]
:
var fruits = new Fruit[]
{ new Orange(), new Apple(), new Banana(),
new Banana(), new Banana(), new Orange() };
我想在三个列表分区列表,每个都包含橘子,苹果,或香蕉。你会怎么做?那么,容易解决方案将是一个类型的测试:
List<Orange> oranges = new List<Orange>();
List<Apple> apples = new List<Apple>();
List<Banana> bananas = new List<Banana>();
foreach (Fruit fruit in fruits)
{
if (fruit is Orange)
oranges.Add((Orange)fruit);
else if (fruit is Apple)
apples.Add((Apple)fruit);
else if (fruit is Banana)
bananas.Add((Banana)fruit);
}
它的工作原理,但也有很多与此代码的问题:
- 一开始,它的丑陋。
- 它不是类型安全的,我们不会在运行时捕获类型错误。
- 它不可维护。如果我们添加一个新的水果衍生实例,我们需要对每个执行水果类型测试的地方进行全局搜索,否则我们可能会错过类型。
访客模式优雅地解决问题。通过修改我们的基础水果类开始:
interface IFruitVisitor
{
void Visit(Orange fruit);
void Visit(Apple fruit);
void Visit(Banana fruit);
}
abstract class Fruit { public abstract void Accept(IFruitVisitor visitor); }
class Orange : Fruit { public override void Accept(IFruitVisitor visitor) { visitor.Visit(this); } }
class Apple : Fruit { public override void Accept(IFruitVisitor visitor) { visitor.Visit(this); } }
class Banana : Fruit { public override void Accept(IFruitVisitor visitor) { visitor.Visit(this); } }
看起来我们是复制粘贴代码,但要注意的派生类都调用不同的重载(该Apple
电话Visit(Apple)
的Banana
电话Visit(Banana)
,等等) 。
实现访问者:
class FruitPartitioner : IFruitVisitor
{
public List<Orange> Oranges { get; private set; }
public List<Apple> Apples { get; private set; }
public List<Banana> Bananas { get; private set; }
public FruitPartitioner()
{
Oranges = new List<Orange>();
Apples = new List<Apple>();
Bananas = new List<Banana>();
}
public void Visit(Orange fruit) { Oranges.Add(fruit); }
public void Visit(Apple fruit) { Apples.Add(fruit); }
public void Visit(Banana fruit) { Bananas.Add(fruit); }
}
现在你可以不用型式试验分区的水果:
FruitPartitioner partitioner = new FruitPartitioner();
foreach (Fruit fruit in fruits)
{
fruit.Accept(partitioner);
}
Console.WriteLine("Oranges.Count: {0}", partitioner.Oranges.Count);
Console.WriteLine("Apples.Count: {0}", partitioner.Apples.Count);
Console.WriteLine("Bananas.Count: {0}", partitioner.Bananas.Count);
这样做的优点是:
- 是相对干净,容易阅读代码。
- 类型安全,类型错误在编译时被捕获。
- 可维护性。如果我添加一个删除具体的Fruit类,我可以修改我的IFruitVisitor接口来相应地处理类型,编译器会立即找到我们实现接口的所有地方,以便我们进行适当的修改。
随着中说,游客通常是矫枉过正,而且他们有一种倾向非常复杂的API,它可以是非常繁琐定义一个新的访问者为每一个新类的行为。
通常,应使用类似继承的简单模式来代替访问者。例如,在原则上我可以写一个类,如:
class FruitPricer : IFruitVisitor
{
public double Price { get; private set; }
public void Visit(Orange fruit) { Price = 0.69; }
public void Visit(Apple fruit) { Price = 0.89; }
public void Visit(Banana fruit) { Price = 1.11; }
}
它的工作原理,但有什么优势这个简单的修改:
abstract class Fruit
{
public abstract void Accept(IFruitVisitor visitor);
public abstract double Price { get; }
}
所以,当以下条件成立,你应该使用用户:
你有一个明确的,已知的一组将被访问的类。
对所述类的操作没有明确定义或事先已知。例如,如果某人正在使用您的API,并且想要为消费者提供一种向对象添加新的临时功能的方法。它们也是通过临时功能扩展密封类的一种便捷方式。
您执行一类对象的操作并希望避免运行时类型测试。遍历具有不同属性的不同对象的层次结构时,通常会出现这种情况。
时,不要使用用户:
您支持的一类对象,它们的派生类型事先是不知道的操作。
对象的操作是事先定义好的,特别是如果它们可以从基类继承或在接口中定义。
它更容易让客户使用继承为类添加新功能。
您正在遍历具有相同属性或接口的对象层次结构。
你想要一个相对简单的API。
访客模式的示例。图书,水果蔬菜& 是类型的基本要素“的Visitable” 有两种“访问者”,BillingVisitor & OfferVisitor每个游客都有自己的目的.Algo计算账单和算法中计算这些元素的提议封装在各自的访问者和Visitables(元素)保持不变。
import java.util.ArrayList;
import java.util.List;
public class VisitorPattern {
public static void main(String[] args) {
List<Visitable> visitableElements = new ArrayList<Visitable>();
visitableElements.add(new Book("I123",10,2.0));
visitableElements.add(new Fruit(5,7.0));
visitableElements.add(new Vegetable(25,8.0));
BillingVisitor billingVisitor = new BillingVisitor();
for(Visitable visitableElement : visitableElements){
visitableElement.accept(billingVisitor);
}
OfferVisitor offerVisitor = new OfferVisitor();
for(Visitable visitableElement : visitableElements){
visitableElement.accept(offerVisitor);
}
System.out.println("Total bill " + billingVisitor.totalPrice);
System.out.println("Offer " + offerVisitor.offer);
}
interface Visitor {
void visit(Book book);
void visit(Vegetable vegetable);
void visit(Fruit fruit);
}
//Element
interface Visitable{
public void accept(Visitor visitor);
}
static class OfferVisitor implements Visitor{
StringBuilder offer = new StringBuilder();
@Override
public void visit(Book book) {
offer.append("Book " + book.isbn + " discount 10 %" + " \n");
}
@Override
public void visit(Vegetable vegetable) {
offer.append("Vegetable No discount \n");
}
@Override
public void visit(Fruit fruit) {
offer.append("Fruits No discount \n");
}
}
static class BillingVisitor implements Visitor{
double totalPrice = 0.0;
@Override
public void visit(Book book) {
totalPrice += (book.quantity * book.price);
}
@Override
public void visit(Vegetable vegetable) {
totalPrice += (vegetable.weight * vegetable.price);
}
@Override
public void visit(Fruit fruit) {
totalPrice += (fruit.quantity * fruit.price);
}
}
static class Book implements Visitable{
private String isbn;
private double quantity;
private double price;
public Book(String isbn, double quantity, double price) {
this.isbn = isbn;
this.quantity = quantity;
this.price = price;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
static class Fruit implements Visitable{
private double quantity;
private double price;
public Fruit(double quantity, double price) {
this.quantity = quantity;
this.price = price;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
static class Vegetable implements Visitable{
private double weight;
private double price;
public Vegetable(double weight, double price) {
this.weight = weight;
this.price = price;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
}
我认为访问者模式的主要目的是它具有很高的可扩展性。直觉是你买了一个机器人。机器人已经完全实现了基本功能,如前进,左转,右转,返回,挑选某物,说出相位,...
有一天,你想让你的机器人可以去你的邮局。所有这些基本功能都可以实现,但您需要将机器人带到商店并“更新”您的机器人。商店卖家不需要修改机器人,只需将一个新的更新芯片放到机器人上,它就可以做你想做的事。
有一天,你想让你的机器人去超市。同样的过程,你必须把你的机器人带到商店并更新这个“高级”功能。无需修改机器人本身。
等等...
所以Visitor模式的想法是,给所有实施基本功能,您可以使用访问者模式来添加复杂的功能无限多的。在这个例子中,机器人是你的工人班,而“更新芯片”是访客。每次需要功能的新“更新”时,您都不会修改您的工作人员类别,但会添加访问者。
- 1. 类型列表访客模式示例
- 2. 访客设计模式 - 电子商务
- 3. 访客设计模式与内树
- 4. 单例模式的例子
- 5. 访客模式的实施
- 6. 实例与访问者模式
- 7. 访客模式说明
- 8. 访客模式中使用accept()
- 9. 设计模式滥用的例子
- 10. 设计师设计模式与访客设计模式的区别
- 11. python3单例模式与“用”的声明
- 12. 简单的访客模式故障
- 13. Ruby中的单例模式与单例模式
- 14. 原子方式有哪些用途?
- 15. 与用途声明,呼吁模块
- 16. C++访客模式与访问者通过子类节点失去“是”的关系
- 17. 代理模式 - 实用性与范例
- 18. 访客设计模式 - 返回类型
- 19. 模板和样式位访客
- 20. Objective-C类别==访客模式?
- 21. 访客模式+打开/关闭原理
- 22. 的Java设计模式例子
- 23. Android Studio,Kiosk模式,单用途设备,锁定任务模式
- 24. 访客中的堆栈状态是否会破坏访客模式?
- 25. 在这个例子中飞机的用途是什么
- 26. 在这个例子中qGetPtrHelper的用途是什么?
- 27. 正确的途径,以多种模式
- 28. C#模式匹配的可靠途径?
- 29. 使用访客模式检查派生类的类型?
- 30. 使用模式与模式
您是否检查过关于这个主题的维基百科? http://en.wikipedia.org/wiki/Visitor_pattern哪些部分完全不清楚?或者你在寻找真实世界的例子吗? – BalusC 2010-04-08 23:50:04