我一直在博客中看到访客模式的参考,但我不得不承认,我就是不明白。我读了维基百科上关于这个模式的文章,我理解了它的机制,但我仍然不知道什么时候使用它。
作为一个最近才真正了解装饰器模式的人,现在看到它在任何地方都有使用,我希望能够真正直观地理解这个看似方便的模式。
我一直在博客中看到访客模式的参考,但我不得不承认,我就是不明白。我读了维基百科上关于这个模式的文章,我理解了它的机制,但我仍然不知道什么时候使用它。
作为一个最近才真正了解装饰器模式的人,现在看到它在任何地方都有使用,我希望能够真正直观地理解这个看似方便的模式。
当前回答
Visitor设计模式非常适用于目录树、XML结构或文档概要等“递归”结构。
Visitor对象访问递归结构中的每个节点:每个目录、每个XML标记等等。Visitor对象不遍历结构。相反,Visitor方法应用于结构的每个节点。
这是一个典型的递归节点结构。可以是目录或XML标记。 [如果你是一个Java人,想象一下有很多额外的方法来构建和维护子列表。]
class TreeNode( object ):
def __init__( self, name, *children ):
self.name= name
self.children= children
def visit( self, someVisitor ):
someVisitor.arrivedAt( self )
someVisitor.down()
for c in self.children:
c.visit( someVisitor )
someVisitor.up()
visit方法将Visitor对象应用于结构中的每个节点。在本例中,它是一个自顶向下的访问者。您可以更改visit方法的结构,以进行自底向上或其他排序。
这里有一个供访问者使用的超类。它被visit方法所使用。它“到达”结构中的每个节点。由于visit方法调用了up和down,因此访问者可以跟踪深度。
class Visitor( object ):
def __init__( self ):
self.depth= 0
def down( self ):
self.depth += 1
def up( self ):
self.depth -= 1
def arrivedAt( self, aTreeNode ):
print self.depth, aTreeNode.name
子类可以做一些事情,比如在每个级别上计算节点并积累一个节点列表,生成一个良好的路径分层节号。
这是申请表。它构建了一个树结构,someTree。它创建了一个Visitor, dumpNodes。
然后它将dumpNodes应用到树中。dumpNode对象将“访问”树中的每个节点。
someTree= TreeNode( "Top", TreeNode("c1"), TreeNode("c2"), TreeNode("c3") )
dumpNodes= Visitor()
someTree.visit( dumpNodes )
TreeNode访问算法将确保每个TreeNode都被用作Visitor的arrivedAt方法的参数。
其他回答
Visitor设计模式非常适用于目录树、XML结构或文档概要等“递归”结构。
Visitor对象访问递归结构中的每个节点:每个目录、每个XML标记等等。Visitor对象不遍历结构。相反,Visitor方法应用于结构的每个节点。
这是一个典型的递归节点结构。可以是目录或XML标记。 [如果你是一个Java人,想象一下有很多额外的方法来构建和维护子列表。]
class TreeNode( object ):
def __init__( self, name, *children ):
self.name= name
self.children= children
def visit( self, someVisitor ):
someVisitor.arrivedAt( self )
someVisitor.down()
for c in self.children:
c.visit( someVisitor )
someVisitor.up()
visit方法将Visitor对象应用于结构中的每个节点。在本例中,它是一个自顶向下的访问者。您可以更改visit方法的结构,以进行自底向上或其他排序。
这里有一个供访问者使用的超类。它被visit方法所使用。它“到达”结构中的每个节点。由于visit方法调用了up和down,因此访问者可以跟踪深度。
class Visitor( object ):
def __init__( self ):
self.depth= 0
def down( self ):
self.depth += 1
def up( self ):
self.depth -= 1
def arrivedAt( self, aTreeNode ):
print self.depth, aTreeNode.name
子类可以做一些事情,比如在每个级别上计算节点并积累一个节点列表,生成一个良好的路径分层节号。
这是申请表。它构建了一个树结构,someTree。它创建了一个Visitor, dumpNodes。
然后它将dumpNodes应用到树中。dumpNode对象将“访问”树中的每个节点。
someTree= TreeNode( "Top", TreeNode("c1"), TreeNode("c2"), TreeNode("c3") )
dumpNodes= Visitor()
someTree.visit( dumpNodes )
TreeNode访问算法将确保每个TreeNode都被用作Visitor的arrivedAt方法的参数。
访问者模式作为方面对象编程的地下实现。
例如,如果您定义一个新操作,而不改变其操作的元素的类
游客
Visitor允许用户在不修改类本身的情况下向类族中添加新的虚函数;相反,创建一个实现虚函数的所有适当专门化的访问者类
参观者结构:
在以下情况下使用访问者模式:
必须对结构中分组的不同类型的对象执行类似的操作 您需要执行许多不同且不相关的操作。它将操作从对象结构中分离出来 必须在不改变对象结构的情况下添加新操作 将相关操作集合到一个类中,而不是强迫您更改或派生类 向没有源或不能更改源的类库中添加函数
尽管访问者模式提供了在不改变Object中现有代码的情况下添加新操作的灵活性,但这种灵活性也带来了缺点。
如果添加了新的Visitable对象,则需要在Visitor和ConcreteVisitor类中修改代码。有一种变通方法可以解决这个问题:使用反射,这将对性能产生影响。
代码片段:
import java.util.HashMap;
interface Visitable{
void accept(Visitor visitor);
}
interface Visitor{
void logGameStatistics(Chess chess);
void logGameStatistics(Checkers checkers);
void logGameStatistics(Ludo ludo);
}
class GameVisitor implements Visitor{
public void logGameStatistics(Chess chess){
System.out.println("Logging Chess statistics: Game Completion duration, number of moves etc..");
}
public void logGameStatistics(Checkers checkers){
System.out.println("Logging Checkers statistics: Game Completion duration, remaining coins of loser");
}
public void logGameStatistics(Ludo ludo){
System.out.println("Logging Ludo statistics: Game Completion duration, remaining coins of loser");
}
}
abstract class Game{
// Add game related attributes and methods here
public Game(){
}
public void getNextMove(){};
public void makeNextMove(){}
public abstract String getName();
}
class Chess extends Game implements Visitable{
public String getName(){
return Chess.class.getName();
}
public void accept(Visitor visitor){
visitor.logGameStatistics(this);
}
}
class Checkers extends Game implements Visitable{
public String getName(){
return Checkers.class.getName();
}
public void accept(Visitor visitor){
visitor.logGameStatistics(this);
}
}
class Ludo extends Game implements Visitable{
public String getName(){
return Ludo.class.getName();
}
public void accept(Visitor visitor){
visitor.logGameStatistics(this);
}
}
public class VisitorPattern{
public static void main(String args[]){
Visitor visitor = new GameVisitor();
Visitable games[] = { new Chess(),new Checkers(), new Ludo()};
for (Visitable v : games){
v.accept(visitor);
}
}
}
解释:
Visitable (Element)是一个接口,该接口方法必须添加到一组类中。 Visitor是一个接口,它包含对可访问元素执行操作的方法。 GameVisitor是一个实现Visitor接口(ConcreteVisitor)的类。 每个可访问元素接受Visitor并调用Visitor接口的相关方法。 你可以将游戏视为元素,而将象棋、跳棋和Ludo等具体游戏视为具体元素。
在上述例子中,国际象棋、西洋跳棋和卢多是三种不同的游戏(以及可访问的职业)。在一个晴朗的日子里,我遇到了一个记录每款游戏统计数据的场景。所以无需修改单个类来实现统计功能,你可以将这一职责集中到GameVisitor类中,这样你就无需修改每个游戏的结构。
输出:
Logging Chess statistics: Game Completion duration, number of moves etc..
Logging Checkers statistics: Game Completion duration, remaining coins of loser
Logging Ludo statistics: Game Completion duration, remaining coins of loser
指
oodesign文章
sourcemaking文章
欲知详情
装饰
模式允许将行为静态或动态地添加到单个对象,而不影响来自同一类的其他对象的行为
相关文章:
IO的装饰器模式
什么时候使用装饰器模式?
你的问题是什么时候知道:
我不首先编码访问者模式。我编写标准代码,等待需求的出现,然后重构。假设你有多个支付系统,一次安装一个。在签出时,你可以有很多if条件(或instanceOf),例如:
//psuedo code
if(payPal)
do paypal checkout
if(stripe)
do strip stuff checkout
if(payoneer)
do payoneer checkout
现在假设我有10种支付方式,这有点难看。因此,当你看到这种模式发生时,访问者会很容易地将所有这些分离出来,然后你最终会调用这样的东西:
new PaymentCheckoutVistor(paymentType).visit()
你可以看到如何实现它从这里的例子的数量,我只是向你展示一个用例。
感谢@Federico A. Ramponi的精彩解释,我只是在java版本中做了这个。希望对大家有所帮助。
正如@Konrad Rudolph指出的那样,这实际上是使用两个具体实例一起确定运行时方法的双重分派。
因此,实际上,只要正确定义了操作接口,就不需要为操作执行器创建公共接口。
import static java.lang.System.out;
public class Visitor_2 {
public static void main(String...args) {
Hearen hearen = new Hearen();
FoodImpl food = new FoodImpl();
hearen.showTheHobby(food);
Katherine katherine = new Katherine();
katherine.presentHobby(food);
}
}
interface Hobby {
void insert(Hearen hearen);
void embed(Katherine katherine);
}
class Hearen {
String name = "Hearen";
void showTheHobby(Hobby hobby) {
hobby.insert(this);
}
}
class Katherine {
String name = "Katherine";
void presentHobby(Hobby hobby) {
hobby.embed(this);
}
}
class FoodImpl implements Hobby {
public void insert(Hearen hearen) {
out.println(hearen.name + " start to eat bread");
}
public void embed(Katherine katherine) {
out.println(katherine.name + " start to eat mango");
}
}
正如您所期望的那样,公共接口将为我们带来更多的清晰度,尽管它实际上并不是这个模式的基本部分。
import static java.lang.System.out;
public class Visitor_2 {
public static void main(String...args) {
Hearen hearen = new Hearen();
FoodImpl food = new FoodImpl();
hearen.showHobby(food);
Katherine katherine = new Katherine();
katherine.showHobby(food);
}
}
interface Hobby {
void insert(Hearen hearen);
void insert(Katherine katherine);
}
abstract class Person {
String name;
protected Person(String n) {
this.name = n;
}
abstract void showHobby(Hobby hobby);
}
class Hearen extends Person {
public Hearen() {
super("Hearen");
}
@Override
void showHobby(Hobby hobby) {
hobby.insert(this);
}
}
class Katherine extends Person {
public Katherine() {
super("Katherine");
}
@Override
void showHobby(Hobby hobby) {
hobby.insert(this);
}
}
class FoodImpl implements Hobby {
public void insert(Hearen hearen) {
out.println(hearen.name + " start to eat bread");
}
public void insert(Katherine katherine) {
out.println(katherine.name + " start to eat mango");
}
}