訪问者模式是对象的行为模式。訪问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作须要改动的话,接受这个操作的数据结构则能够保持不变。

分派的概念

  变量被声明时的类型叫做变量的静态类型(Static Type),有些人又把静态类型叫做明显类型(Apparent Type);而变量所引用的对象的真实类型又叫做变量的实际类型(Actual Type)。比方

  1. List list = null;
  2. list = new ArrayList();
List list = null;
list = new ArrayList();

  声明了一个变量list。它的静态类型(也叫明显类型)是List,而它的实际类型是ArrayList。

  依据对象的类型而对方法进行的选择,就是分派(Dispatch),分派(Dispatch)又分为两种。即静态分派动态分派

  静态分派(Static Dispatch)发生在编译时期。分派依据静态类型信息发生。静态分派对于我们来说并不陌生,方法重载就是静态分派。

  动态分派(Dynamic Dispatch)发生在执行时期,动态分派动态地置换掉某个方法。

 静态分派

  Java通过方法重载支持静态分派。

用墨子骑马的故事作为样例。墨子能够骑白马或者黑马。

墨子与白马、黑马和马的类图例如以下所看到的:

  在这个系统中,墨子由Mozi类代表

  1. package com.bankht.Visitor;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-7-3 上午10:41:24
  5. *
  6. * @类说明 :在这个系统中。墨子由Mozi类代表
  7. */
  8. public class Mozi {
  9. public void ride(Horse h) {
  10. System.out.println("骑马");
  11. }
  12. public void ride(WhiteHorse wh) {
  13. System.out.println("骑白马");
  14. }
  15. public void ride(BlackHorse bh) {
  16. System.out.println("骑黑马");
  17. }
  18. public static void main(String[] args) {
  19. Horse wh = new WhiteHorse();
  20. Horse bh = new BlackHorse();
  21. Mozi mozi = new Mozi();
  22. mozi.ride(wh);
  23. mozi.ride(bh);
  24. }
  25. }
package com.bankht.Visitor;

/**
* @author: 特种兵—AK47
* @创建时间:2012-7-3 上午10:41:24
*
* @类说明 :在这个系统中。墨子由Mozi类代表
*/
public class Mozi { public void ride(Horse h) {
System.out.println("骑马");
} public void ride(WhiteHorse wh) {
System.out.println("骑白马");
} public void ride(BlackHorse bh) {
System.out.println("骑黑马");
} public static void main(String[] args) {
Horse wh = new WhiteHorse();
Horse bh = new BlackHorse();
Mozi mozi = new Mozi();
mozi.ride(wh);
mozi.ride(bh);
} }

  显然,Mozi类的ride()方法是由三个方法重载而成的。

这三个方法分别接受马(Horse)、白马(WhiteHorse)、黑马(BlackHorse)等类型的參数。

  那么在执行时,程序会打印出什么结果呢?结果是程序会打印出同样的两行“骑马”。换言之,墨子发现他所骑的都是马。

  为什么呢?两次对ride()方法的调用传入的是不同的參数。也就是wh和bh。

它们尽管具有不同的真实类型。可是它们的静态类型都是一样的,均是Horse类型。

  重载方法的分派是依据静态类型进行的,这个分派过程在编译时期就完毕了。

 动态分派

  Java通过方法的重写支持动态分派。

用马吃草的故事作为样例。代码例如以下所看到的:

  1. package com.bankht.Visitor;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-7-3 上午10:41:46
  5. *
  6. * @类说明 :Java通过方法的重写支持动态分派。

    用马吃草的故事作为样例

  7. */
  8. public class Horse {
  9. public void eat() {
  10. System.out.println("马吃草");
  11. }
  12. }
package com.bankht.Visitor;

/**
* @author: 特种兵—AK47
* @创建时间:2012-7-3 上午10:41:46
*
* @类说明 :Java通过方法的重写支持动态分派。 用马吃草的故事作为样例
*/
public class Horse { public void eat() {
System.out.println("马吃草");
} }
  1. package com.bankht.Visitor;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-7-3 上午10:43:15
  5. *
  6. * @类说明 :
  7. */
  8. public class WhiteHorse extends Horse {
  9. @Override
  10. public void eat() {
  11. System.out.println("白马吃草");
  12. }
  13. }
package com.bankht.Visitor;

/**
* @author: 特种兵—AK47
* @创建时间:2012-7-3 上午10:43:15
*
* @类说明 :
*/
public class WhiteHorse extends Horse { @Override
public void eat() {
System.out.println("白马吃草");
}
}
  1. package com.bankht.Visitor;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-7-3 上午10:42:08
  5. *
  6. * @类说明 :
  7. */
  8. public class BlackHorse extends Horse {
  9. @Override
  10. public void eat() {
  11. System.out.println("黑马吃草");
  12. }
  13. }
package com.bankht.Visitor;

/**
* @author: 特种兵—AK47
* @创建时间:2012-7-3 上午10:42:08
*
* @类说明 :
*/
public class BlackHorse extends Horse { @Override
public void eat() {
System.out.println("黑马吃草");
}
}
  1. package com.bankht.Visitor;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-7-3 上午10:42:34
  5. *
  6. * @类说明 :
  7. */
  8. public class Client {
  9. public static void main(String[] args) {
  10. Horse h = new BlackHorse();
  11. h.eat();
  12. }
  13. }
package com.bankht.Visitor;

/**
* @author: 特种兵—AK47
* @创建时间:2012-7-3 上午10:42:34
*
* @类说明 :
*/
public class Client { public static void main(String[] args) {
Horse h = new BlackHorse();
h.eat();
} }

执行一下:

  1. 黑马吃草
黑马吃草

  变量h的静态类型是Horse,而真实类型是BlackHorse。假设上面最后一行的eat()方法调用的是BlackHorse类的eat()方法,那么上面打印的就是“黑马吃草”;相反,假设上面的eat()方法调用的是Horse类的eat()方法。那么打印的就是“马吃草”。

  所以,问题的核心就是Java编译器在编译时期并不总是知道哪些代码会被运行,由于编译器只知道对象的静态类型,而不知道对象的真实类型。而方法的调用则是依据对象的真实类型,而不是静态类型。

这样一来。上面最后一行的eat()方法调用的是BlackHorse类的eat()方法,打印的是“黑马吃草”。

 分派的类型

  一个方法所属的对象叫做方法的接收者,方法的接收者与方法的參数统称做方法的宗量。比方以下样例中的Test类

  1. public class Test {
  2. public void print(String str){
  3. System.out.println(str);
  4. }
  5. }
public class Test {

    public void print(String str){
System.out.println(str);
}
}

  在上面的类中,print()方法属于Test对象,所以它的接收者也就是Test对象了。print()方法有一个參数是str,它的类型是String。

  依据分派能够基于多少种宗量。能够将面向对象的语言划分为单分派语言(Uni-Dispatch)和多分派语言(Multi-Dispatch)。单分派语言依据一个宗量的类型进行对方法的选择,多分派语言依据多于一个的宗量的类型对方法进行选择。

  C++和Java均是单分派语言,多分派语言的样例包含CLOS和Cecil。依照这样的区分,Java就是动态的单分派语言。由于这样的语言的动态分派只会考虑到方法的接收者的类型,同一时候又是静态的多分派语言。由于这样的语言对重载方法的分派会考虑到方法的接收者的类型以及方法的全部參数的类型。

  在一个支持动态单分派的语言里面,有两个条件决定了一个请求会调用哪一个操作:一是请求的名字。而是接收者的真实类型。单分派限制了方法的选择过程,使得仅仅有一个宗量能够被考虑到。这个宗量通常就是方法的接收者。在Java语言里面,假设一个操作是作用于某个类型不明的对象上面,那么对这个对象的真实类型測试仅会发生一次,这就是动态的单分派的特征。

 双重分派

  一个方法依据两个宗量的类型来决定运行不同的代码。这就是“双重分派”。Java语言不支持动态的多分派,也就意味着Java不支持动态的双分派。可是通过使用设计模式,也能够在Java语言里实现动态的双重分派。

  在Java中能够通过两次方法调用来达到两次分派的目的。类图例如以下所看到的:

  在图中有两个对象,左边的叫做West。右边的叫做East。如今West对象首先调用East对象的goEast()方法,并将它自己传入。在East对象被调用时,马上依据传入的參数知道了调用者是谁。于是反过来调用“调用者”对象的goWest()方法。通过两次调用将程序控制权轮番交给两个对象。其时序图例如以下所看到的:

  这样就出现了两次方法调用。程序控制权被两个对象像传球一样,首先由West对象传给了East对象。然后又被返传给了West对象。

  可是只返传了一下球,并不能解决双重分派的问题。关键是如何利用这两次调用,以及Java语言的动态单分派功能,使得在这样的传球的过程中,可以触发两次单分派。

  动态单分派在Java语言中是在子类重写父类的方法时发生的。换言之,West和East都必须分别置身于自己的类型等级结构中。例如以下图所看到的:

  源码

  West类

  1. public abstract class West {
  2. public abstract void goWest1(SubEast1 east);
  3. public abstract void goWest2(SubEast2 east);
  4. }
public abstract class West {

    public abstract void goWest1(SubEast1 east);

    public abstract void goWest2(SubEast2 east);
}

  SubWest1类

  1. public class SubWest1 extends West{
  2. @Override
  3. public void goWest1(SubEast1 east) {
  4. System.out.println("SubWest1 + " + east.myName1());
  5. }
  6. @Override
  7. public void goWest2(SubEast2 east) {
  8. System.out.println("SubWest1 + " + east.myName2());
  9. }
  10. }
public class SubWest1 extends West{

    @Override
public void goWest1(SubEast1 east) { System.out.println("SubWest1 + " + east.myName1());
} @Override
public void goWest2(SubEast2 east) { System.out.println("SubWest1 + " + east.myName2());
}
}

 SubWest2类

  1. public class SubWest2 extends West{
  2. @Override
  3. public void goWest1(SubEast1 east) {
  4. System.out.println("SubWest2 + " + east.myName1());
  5. }
  6. @Override
  7. public void goWest2(SubEast2 east) {
  8. System.out.println("SubWest2 + " + east.myName2());
  9. }
  10. }
public class SubWest2 extends West{
@Override
public void goWest1(SubEast1 east) { System.out.println("SubWest2 + " + east.myName1());
} @Override
public void goWest2(SubEast2 east) { System.out.println("SubWest2 + " + east.myName2());
}
}

 East类

  1. public abstract class East {
  2. public abstract void goEast(West west);
  3. }
public abstract class East {

    public abstract void goEast(West west);
}

  SubEast1类

  1. public class SubEast1 extends East{
  2. @Override
  3. public void goEast(West west) {
  4. west.goWest1(this);
  5. }
  6. public String myName1(){
  7. return "SubEast1";
  8. }
  9. }
public class SubEast1 extends East{
@Override
public void goEast(West west) {
west.goWest1(this);
} public String myName1(){
return "SubEast1";
}
}

  SubEast2类

  1. public class SubEast2 extends East{
  2. @Override
  3. public void goEast(West west) {
  4. west.goWest2(this);
  5. }
  6. public String myName2(){
  7. return "SubEast2";
  8. }
  9. }
public class SubEast2 extends East{
@Override
public void goEast(West west) {
west.goWest2(this);
} public String myName2(){
return "SubEast2";
}
}

  client类

  1. public class Client {
  2. public static void main(String[] args) {
  3. //组合1
  4. East east = new SubEast1();
  5. West west = new SubWest1();
  6. east.goEast(west);
  7. //组合2
  8. east = new SubEast1();
  9. west = new SubWest2();
  10. east.goEast(west);
  11. }
  12. }
public class Client {

    public static void main(String[] args) {
//组合1
East east = new SubEast1();
West west = new SubWest1();
east.goEast(west);
//组合2
east = new SubEast1();
west = new SubWest2();
east.goEast(west);
} }

执行结果例如以下

--------------------------------------------------------------------------------  

SubWest1 + SubEast1

SubWest2 + SubEast1

--------------------------------------------------------------------------------

  系统执行时。会首先创建SubWest1和SubEast1对象,然后client调用SubEast1的goEast()方法,并将SubWest1对象传入。因为SubEast1对象重写了其超类East的goEast()方法。因此。这个时候就发生了一次动态的单分派。当SubEast1对象接到调用时,会从參数中得到SubWest1对象。所以它就马上调用这个对象的goWest1()方法,并将自己传入。因为SubEast1对象有权选择调用哪一个对象。因此,在此时又进行一次动态的方法分派。

  这个时候SubWest1对象就得到了SubEast1对象。通过调用这个对象myName1()方法,就能够打印出自己的名字和SubEast对象的名字,其时序图例如以下所看到的:

  因为这两个名字一个来自East等级结构,还有一个来自West等级结构中,因此。它们的组合式是动态决定的。

这就是动态双重分派的实现机制。

訪问者模式的结构

  訪问者模式适用于数据结构相对未定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合能够相对自由地演化。訪问者模式的简略图例如以下所看到的:

数据结构的每个节点都能够接受一个訪问者的调用。此节点向訪问者对象传入节点对象,而訪问者对象则反过来运行节点对象的操作。这种过程叫做“双重分派”。

节点调用訪问者,将它自己传入,訪问者则将某算法针对此节点运行。

訪问者模式的示意性类图例如以下所看到的:

  訪问者模式涉及到的角色例如以下:

  ●  抽象訪问者(Visitor)角色:声明了一个或者多个方法操作。形成全部的详细訪问者角色必须实现的接口。

  ●  详细訪问者(ConcreteVisitor)角色:实现抽象訪问者所声明的接口,也就是抽象訪问者所声明的各个訪问操作。

  ●  抽象节点(Node)角色:声明一个接受操作。接受一个訪问者对象作为一个參数。

  ●  详细节点(ConcreteNode)角色:实现了抽象节点所规定的接受操作。

  ●  结构对象(ObjectStructure)角色:有例如以下的责任,能够遍历结构中的全部元素。假设须要,提供一个高层次的接口让訪问者对象能够訪问每个元素。假设须要,能够设计成一个复合对象或者一个聚集,如List或Set。

  源码

  能够看到,抽象訪问者角色为每个详细节点都准备了一个訪问操作。因为有两个节点,因此,相应就有两个訪问操作。

  1. package com.bankht.Visitor.visitor;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-7-3 上午10:50:06
  5. *
  6. * @类说明 :能够看到,抽象訪问者角色为每个详细节点都准备了一个訪问操作。因为有两个节点,因此,相应就有两个訪问操作。

  7. */
  8. public interface Visitor {
  9. /**
  10. * 相应于NodeA的訪问操作
  11. */
  12. public void visit(NodeA node);
  13. /**
  14. * 相应于NodeB的訪问操作
  15. */
  16. public void visit(NodeB node);
  17. }
package com.bankht.Visitor.visitor;

/**
* @author: 特种兵—AK47
* @创建时间:2012-7-3 上午10:50:06
*
* @类说明 :能够看到。抽象訪问者角色为每个详细节点都准备了一个訪问操作。 因为有两个节点。因此。相应就有两个訪问操作。 */
public interface Visitor {
/**
* 相应于NodeA的訪问操作
*/
public void visit(NodeA node); /**
* 相应于NodeB的訪问操作
*/
public void visit(NodeB node);
}

  详细訪问者VisitorA类

  1. package com.bankht.Visitor.visitor;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-7-3 上午10:58:04
  5. *
  6. * @类说明 :
  7. */
  8. public class VisitorA implements Visitor {
  9. /**
  10. * 相应于NodeA的訪问操作
  11. */
  12. @Override
  13. public void visit(NodeA node) {
  14. System.out.println("VisitorA:" + node.operationA());
  15. }
  16. /**
  17. * 相应于NodeB的訪问操作
  18. */
  19. @Override
  20. public void visit(NodeB node) {
  21. System.out.println("VisitorA:" + node.operationB());
  22. }
  23. }
package com.bankht.Visitor.visitor;

/**
* @author: 特种兵—AK47
* @创建时间:2012-7-3 上午10:58:04
*
* @类说明 :
*/
public class VisitorA implements Visitor {
/**
* 相应于NodeA的訪问操作
*/
@Override
public void visit(NodeA node) {
System.out.println("VisitorA:" + node.operationA());
} /**
* 相应于NodeB的訪问操作
*/
@Override
public void visit(NodeB node) {
System.out.println("VisitorA:" + node.operationB());
} }

  详细訪问者VisitorB类

  1. package com.bankht.Visitor.visitor;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-7-3 上午10:58:21
  5. *
  6. * @类说明 :
  7. */
  8. public class VisitorB implements Visitor {
  9. /**
  10. * 相应于NodeA的訪问操作
  11. */
  12. @Override
  13. public void visit(NodeA node) {
  14. System.out.println("VisitorB:" + node.operationA());
  15. }
  16. /**
  17. * 相应于NodeB的訪问操作
  18. */
  19. @Override
  20. public void visit(NodeB node) {
  21. System.out.println("VisitorB:" + node.operationB());
  22. }
  23. }
package com.bankht.Visitor.visitor;

/**
* @author: 特种兵—AK47
* @创建时间:2012-7-3 上午10:58:21
*
* @类说明 :
*/
public class VisitorB implements Visitor {
/**
* 相应于NodeA的訪问操作
*/
@Override
public void visit(NodeA node) {
System.out.println("VisitorB:" + node.operationA());
} /**
* 相应于NodeB的訪问操作
*/
@Override
public void visit(NodeB node) {
System.out.println("VisitorB:" + node.operationB());
} }

  抽象节点类

  1. package com.bankht.Visitor.visitor;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-7-3 上午10:58:57
  5. *
  6. * @类说明 :
  7. */
  8. public abstract class Node {
  9. /**
  10. * 接受操作
  11. */
  12. public abstract void accept(Visitor visitor);
  13. }
package com.bankht.Visitor.visitor;

/**
* @author: 特种兵—AK47
* @创建时间:2012-7-3 上午10:58:57
*
* @类说明 :
*/
public abstract class Node {
/**
* 接受操作
*/
public abstract void accept(Visitor visitor);
}

 详细节点类NodeA

  1. package com.bankht.Visitor.visitor;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-7-3 上午10:59:45
  5. *
  6. * @类说明 :
  7. */
  8. public class NodeA extends Node {
  9. /**
  10. * 接受操作
  11. */
  12. @Override
  13. public void accept(Visitor visitor) {
  14. visitor.visit(this);
  15. }
  16. /**
  17. * NodeA特有的方法
  18. */
  19. public String operationA() {
  20. return "NodeA";
  21. }
  22. }
package com.bankht.Visitor.visitor;

/**
* @author: 特种兵—AK47
* @创建时间:2012-7-3 上午10:59:45
*
* @类说明 :
*/
public class NodeA extends Node {
/**
* 接受操作
*/
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
} /**
* NodeA特有的方法
*/
public String operationA() {
return "NodeA";
} }

  详细节点类NodeB

  1. package com.bankht.Visitor.visitor;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-7-3 上午10:59:59
  5. *
  6. * @类说明 :
  7. */
  8. public class NodeB extends Node {
  9. /**
  10. * 接受方法
  11. */
  12. @Override
  13. public void accept(Visitor visitor) {
  14. visitor.visit(this);
  15. }
  16. /**
  17. * NodeB特有的方法
  18. */
  19. public String operationB() {
  20. return "NodeB";
  21. }
  22. }
package com.bankht.Visitor.visitor;

/**
* @author: 特种兵—AK47
* @创建时间:2012-7-3 上午10:59:59
*
* @类说明 :
*/
public class NodeB extends Node {
/**
* 接受方法
*/
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
} /**
* NodeB特有的方法
*/
public String operationB() {
return "NodeB";
}
}

  结构对象角色类。这个结构对象角色持有一个聚集,并向外界提供add()方法作为对聚集的管理操作。通过调用这种方法,能够动态地添加一个新的节点。

  1. package com.bankht.Visitor.visitor;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. /**
  5. * @author: 特种兵—AK47
  6. * @创建时间:2012-7-3 上午11:00:15
  7. *
  8. * @类说明 :
  9. */
  10. public class ObjectStructure {
  11. private List<Node> nodes = new ArrayList<Node>();
  12. /**
  13. * 运行方法操作
  14. */
  15. public void action(Visitor visitor) {
  16. for (Node node : nodes) {
  17. node.accept(visitor);
  18. }
  19. }
  20. /**
  21. * 加入一个新元素
  22. */
  23. public void add(Node node) {
  24. nodes.add(node);
  25. }
  26. }
package com.bankht.Visitor.visitor;

import java.util.ArrayList;
import java.util.List; /**
* @author: 特种兵—AK47
* @创建时间:2012-7-3 上午11:00:15
*
* @类说明 :
*/
public class ObjectStructure { private List<Node> nodes = new ArrayList<Node>(); /**
* 运行方法操作
*/
public void action(Visitor visitor) { for (Node node : nodes) {
node.accept(visitor);
} } /**
* 加入一个新元素
*/
public void add(Node node) {
nodes.add(node);
}
}

  client类

  1. package com.bankht.Visitor.visitor;
  2. /**
  3. * @author: 特种兵—AK47
  4. * @创建时间:2012-7-3 上午11:00:29
  5. *
  6. * @类说明 :
  7. */
  8. public class Client {
  9. public static void main(String[] args) {
  10. // 创建一个结构对象
  11. ObjectStructure os = new ObjectStructure();
  12. // 给结构添加一个节点
  13. os.add(new NodeA());
  14. // 给结构添加一个节点
  15. os.add(new NodeB());
  16. // 创建一个訪问者
  17. Visitor visitor = new VisitorA();
  18. os.action(visitor);
  19. }
  20. }
package com.bankht.Visitor.visitor;

/**
* @author: 特种兵—AK47
* @创建时间:2012-7-3 上午11:00:29
*
* @类说明 :
*/
public class Client { public static void main(String[] args) {
// 创建一个结构对象
ObjectStructure os = new ObjectStructure();
// 给结构添加一个节点
os.add(new NodeA());
// 给结构添加一个节点
os.add(new NodeB());
// 创建一个訪问者
Visitor visitor = new VisitorA();
os.action(visitor);
} }

  尽管在这个示意性的实现里并没有出现一个复杂的具有多个树枝节点的对象树结构,可是。在实际系统中訪问者模式一般是用来处理复杂的对象树结构的,并且訪问者模式能够用来处理跨越多个等级结构的树结构问题。这正是訪问者模式的功能强大之处。

  准备过程时序图

  首先,这个示意性的client创建了一个结构对象。然后将一个新的NodeA对象和一个新的NodeB对象传入。

  其次。client创建了一个VisitorA对象,并将此对象传给结构对象。

  然后,client调用结构对象聚集管理方法,将NodeA和NodeB节点增加到结构对象中去。

  最后,client调用结构对象的行动方法action(),启动訪问过程。

  訪问过程时序图

  

  结构对象会遍历它自己所保存的聚集中的全部节点,在本系统中就是节点NodeA和NodeB。

首先NodeA会被訪问到,这个訪问是由下面的操作组成的:

  (1)NodeA对象的接受方法accept()被调用,并将VisitorA对象本身传入;

  (2)NodeA对象反过来调用VisitorA对象的訪问方法。并将NodeA对象本身传入。

  (3)VisitorA对象调用NodeA对象的特有方法operationA()。

  从而就完毕了双重分派过程。接着,NodeB会被訪问。这个訪问的过程和NodeA被訪问的过程是一样的,这里不再叙述。

訪问者模式的长处

  ●  好的扩展性

  可以在不改动对象结构中的元素的情况下。为对象结构中的元素加入新的功能。

  ●  好的复用性

  能够通过訪问者来定义整个对象结构通用的功能。从而提高复用程度。

  ●  分离无关行为

  能够通过訪问者来分离无关的行为,把相关的行为封装在一起。构成一个訪问者,这样每个訪问者的功能都比較单一。

訪问者模式的缺点

  ●  对象结构变化非常困难

  不适用于对象结构中的类常常变化的情况,由于对象结构发生了改变,訪问者的接口和訪问者的实现都要发生对应的改变。代价太高。

  ●  破坏封装

  訪问者模式通常须要对象结构开放内部数据给訪问者和ObjectStructrue,这破坏了对象的封装性。

  

本文借鉴:http://blog.csdn.net/m13666368773/article/details/7711326

《Java设计模式》之訪问者模式的更多相关文章

  1. JAVA设计模式之 訪问者模式【Visitor Pattern】

    一.概述 訪问者模式是一种较为复杂的行为型设计模式,它包括訪问者和被訪问元素两个主要组成部分.这些被訪问的元素通常具有不同的类型,且不同的訪问者能够对它们进行不同的訪问操作.在使用訪问者模式时,被訪问 ...

  2. Java设计模式(三) Visitor(訪问者)模式及多分派场景应用

    基本概念 Visitor 封装一些作用于数据结构中的各元素的操作,不同的操作能够借助新的visitor实现.减少了操作间的耦合性 訪问者能够将数据结构和对数据的操作解耦,使得添加对数据结构的操作不须要 ...

  3. JAVA设计模式之:訪问者模式

    訪问者模式: 一个作用于某对象结构中各元素的操作,使你能够在不改变各元素类数据结构的前提下添加作用于这些元素的新操作. 结构对象是訪问者模式必备条件.且这个结构对象必须存在遍历自身各个对象的方法. 适 ...

  4. 设计模式之十五:訪问者模式(Visitor Pattern)

    訪问者模式(Visitor Pattern)是GoF提出的23种设计模式中的一种,属于行为模式. 据<大话设计模式>中说算是最复杂也是最难以理解的一种模式了. 定义(源于GoF<De ...

  5. PHP设计模式——訪问者模式

    声明:本系列博客參考资料<大话设计模式>,作者程杰. 訪问者模式表示一个作用于某对象结构中的各元素的操作. 它使你能够在不改变各元素类的前提下定义作用于这些元素的新操作. UML类图: w ...

  6. 设计模式之二十四:訪问者模式(Visitor)

    訪问者模式: 定义了一个作用于一个类的一些操作,訪问者模式同意在不改变类的前提下添加一些操作. Represent an operation to be performed on the elemen ...

  7. C++设计模式实现--訪问者(Visitor)模式

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/L_Andy/article/details/36896645 一. 訪问者模式 定义:表示一个作用于 ...

  8. 设计模式入门之訪问者模式Visitor

    //訪问者模式定义:表示一个作用于某对象结构中的各个元素的操作,它使你能够在不改变各元素类的前提下定义作用于这些元素的新操作. //从定义上看.这个模式跟装饰模式的定义非常类似(动态地给一个对象加入一 ...

  9. Java设计模式之《职责链模式》及应用场景

    原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6530089.html 职责链模式(称责任链模式)将请求的处理对象像一条长链一般组合起来,形 ...

随机推荐

  1. ios之coredata

    Core Data数据持久化是对SQLite的一个升级,它是ios集成的,在说Core Data之前,我们先说说在CoreData中使用的几个类. (1)NSManagedObjectModel(被管 ...

  2. ie8不支持伪类选择器的解决方案

    引用jQuery的插件jquery.pseudo.js插件内容: (function($){ var patterns = { text: /^['"]?(.+?)["']?$/, ...

  3. 天梯赛L1 题解

    L1-001 Hello World (5 分) 这道超级简单的题目没有任何输入. 你只需要在一行中输出著名短句“Hello World!”就可以了. AC代码:(直接输出记性) #include & ...

  4. NOI2018_Day1_T1_归程

    题目描述 本题的故事发生在魔力之都,在这里我们将为你介绍一些必要的设定. 魔力之都可以抽象成一个 n 个节点.m 条边的无向连通图(节点的编号从 1 至 n). 我们依次用 l,a 描述一条边的长度. ...

  5. 3. 对系统表空间使用Raw磁盘分区

    3. 对系统表空间使用Raw磁盘分区 可以将raw磁盘分区用作InnoDB系统表空间中的数据文件.此技术可在Windows和某些Linux和Unix系统上启用非缓冲I/O,而无需文件系统开销.使用和不 ...

  6. QEMU支持的几种常见的镜像文件格式

    qemu-img支持非常多种的文件格式,可以通过"qemu-img  -h"查看其命令帮助得到,它支持二十多种格式:blkdebug.blkverify.bochs.cloop.c ...

  7. ES6中Generator

    ES6中Generator Generator是ES6一个很有意思的特性,也是不容易理解的特性.不同于let/const提供了块级作用域这样明显的目的,这玩意儿被搞出来到底是干嘛的? 首先我们需要明确 ...

  8. finally块的问题(finally block does not complete normally)

    http://blog.csdn.net/chh_jiang/article/details/4557461 当finall块中包含return语句时,Eclipse会给出警告“finally blo ...

  9. set()集合基本操作

    运用频次:☆☆ set是一个无序且不重复元素集,基本操作如下: 1. 创建set集合,会自动转换成set类型 2. add():添加元素 def add(self, *args, **kwargs): ...

  10. Linux常用命令大全--有关磁盘空间的命令

    1.mount 命令的功能是挂载文件系统,可以挂载硬盘.光盘.软盘,也可以挂载NFS网络文件系统 mount -t 设备类型 存放目录 mount IP地址:/所提供的目录 存放目录 (无) 不加任何 ...