访问者模式2
(2)应用访问者模式来解决的思路
仔细分析上面的示例,对于客户这个对象结构,不想改变类,又要添加新的功能,很明显就需要一种动态的方式,在运行期间把功能动态地添加到对象结构中去。
有些朋友可能会想起装饰模式,装饰模式可以实现为一个对象透明的添加功能,但装饰模式基本上是在现有的功能的基础之上进行功能添加,实际上是对现有功能的加强或者改造。并不是在现有功能不改动的情况下,为对象添加新的功能。
?????? 看来需要另外寻找新的解决方式了,可以应用访问者模式来解决这个问题,访问者模式实现的基本思路如下:
这样一来,只要提供实现新功能的对象给对象结构,就可以为这些对象添加新的功能,由于在对象结构中定义的方法是通用的功能方法,所以什么新功能都可以加入。
访问者模式的结构如图25.3所示:
?
图25.3? 访问者模式结构示意图
Visitor
?????? 访问者接口,为所有的访问者对象声明一个visit方法,用来代表为对象结构添加的功能,理论上可以代表任意的功能。
ConcreteVisitor
?????? 具体的访问者实现对象,实现要真正被添加到对象结构中的功能。
Element
?????? 抽象的元素对象,对象结构的顶层接口,定义接受访问的操作。
ConcreteElement
?????? 具体元素对象,对象结构中具体的对象,也是被访问的对象,通常会回调访问者的真实功能,同时开放自身的数据供访问者使用。
ObjectStructure
对象结构,通常包含多个被访问的对象,它可以遍历这多个被访问的对象,也可以让访问者访问它的元素。可以是一个复合或是一个集合,如一个列表或无序集合。
但是请注意:这个ObjectStructure并不是我们在前面讲到的对象结构,前面一直讲的对象结构是指的一系列对象的定义结构,是概念上的东西;而ObjectStructure可以看成是对象结构中的一系列对象的一个集合,是用来辅助客户端访问这一系列对象的,所以为了不造成大家的困惑,后面提到ObjectStructure的时候,就用英文名称来代替,不把它翻译成中文。
(1)首先需要定义一个接口来代表要新加入的功能,把它称作访问者,访问谁呢?当然是访问对象结构中的对象了。既然是访问,不能空手而去吧,这些访问者在进行访问的时候,就会携带新的功能,也就是说,访问者携带着需要添加的新的功能去访问对象结构中的对象,就相当于给对象结构中的对象添加了新的功能。示例代码如下:
/**
?* 访问者接口
?*/
public interface Visitor {
??? /**
??? ?* 访问元素A,相当于给元素A添加访问者的功能
??? ?* @param elementA 元素A的对象
??? ?*/
??? public void visitConcreteElementA(ConcreteElementA elementA);
??? /**
??? ?* 访问元素B,相当于给元素B添加访问者的功能
??? ?* @param elementB 元素B的对象
??? ?*/
??? public void visitConcreteElementB(ConcreteElementB elementB);
}
(2)看看抽象的元素对象定义,示例代码如下:
/**
?* 被访问的元素的接口
?*/
public abstract class Element {
??? /**
??? ?* 接受访问者的访问
??? ?* @param visitor 访问者对象
??? ?*/
??? public abstract void accept(Visitor visitor);
}
(3)接下来看看元素对象的具体实现,先看元素A的实现,示例代码如下:
/**
?* 具体元素的实现对象
?*/
public class ConcreteElementA extends Element {
??? public void accept(Visitor visitor) {
?????? //回调访问者对象的相应方法
?????? visitor.visitConcreteElementA(this);
??? }
??? /**
??? ?* 示例方法,表示元素已有的功能实现
??? ?*/
??? public void opertionA(){
?????? //已有的功能实现
??? }
}
再看看元素B的实现,示例代码如下:
/**
?* 具体元素的实现对象
?*/
public class ConcreteElementB extends Element {
??? public void accept(Visitor visitor) {
?????? //回调访问者对象的相应方法
?????? visitor.visitConcreteElementB(this);
??? }
??? /**
??? ?* 示例方法,表示元素已有的功能实现
??? ?*/
??? public void opertionB(){
?????? //已有的功能实现
??? }
}
(4)接下来看看访问者的具体实现,先看访问者1的实现,示例代码如下:
/**
?* 具体的访问者实现
?*/
public class ConcreteVisitor1 implements Visitor {
??? public void visitConcreteElementA(ConcreteElementA element) {
?????? //把去访问ConcreteElementA时,需要执行的功能实现在这里
?????? //可能需要访问元素已有的功能,比如:
?????? element.opertionA();
??? }
??? public void visitConcreteElementB(ConcreteElementB element) {
?????? //把去访问ConcreteElementB时,需要执行的功能实现在这里
?????? //可能需要访问元素已有的功能,比如:
?????? element.opertionB();
??? }
}
访问者2的实现和访问者1的示意代码是一样的,就不去赘述了。
(5)该来看看ObjectStructure的实现了,示例代码如下:
/**
?* 对象结构,通常在这里对元素对象进行遍历,让访问者能访问到所有的元素
?*/
public class ObjectStructure {
??? /**
??? ?* 示意,表示对象结构,可以是一个组合结构或是集合
??? ?*/
??? private Collection<Element> col = new ArrayList<Element>();
??? /**
??? ?* 示意方法,提供给客户端操作的高层接口
??? ?* @param visitor 客户端需要使用的访问者
??? ?*/
??? public void handleRequest(Visitor visitor){
?????? //循环对象结构中的元素,接受访问
?????? for(Element ele : col){
?????????? ele.accept(visitor);
?????? }
??? }
??? /**
??? ?* 示意方法,组建对象结构,向对象结构中添加元素。
??? ?* 不同的对象结构有不同的构建方式
??? ?* @param ele 加入到对象结构的元素
??? ?*/
??? public void addElement(Element ele){
?????? this.col.add(ele);
??? }
}
(6)接下来看看客户端的示意实现,示例代码如下:
public class Client {
??? public static void main(String[] args) {
?????? //创建ObjectStructure
?????? ObjectStructure os = new ObjectStructure();
?????? //创建要加入对象结构的元素
?????? Element eleA = new ConcreteElementA();
?????? Element eleB = new ConcreteElementB();
?????? //把元素加入对象结构
?????? os.addElement(eleA);
?????? os.addElement(eleB);????
?????? //创建访问者
?????? Visitor visitor = new ConcreteVisitor1();????
?????? //调用业务处理的方法
?????? os.handleRequest(visitor);?????
??? }
}
?????? 要使用访问者模式来重写示例,首先就要按照访问者模式的结构,分离出两个类层次来,一个是对应于元素的类层次,一个是对应于访问者的类层次。
?????? 对于对应于元素的类层次,现在已经有了,就是客户的对象层次。而对应于访问者的类层次,现在还没有,不过,按照访问者模式的结构,应该是先定义一个访问者接口,然后把每种业务实现成为一个单独的访问者对象,也就是说应该使用一个访问者对象来实现对客户的偏好分析,而用另外一个访问者对象来实现对客户的价值分析。
?????? 在分离好两个类层次过后,为了方便客户端的访问,定义一个ObjectStructure,其实就类似于前面示例中的客户管理的业务对象。新的示例的结构如图25.4所示:
图25.4? 使用访问者模式的示例程序结构示意图
?????? 仔细查看图25.4所示的程序结构示意图,细心的朋友会发现,在图上没有出现对客户进行价值分析的功能了。这是为了示范“使用访问者模式来实现示例功能过后,可以很容易的给对象结构增加新的功能”,所以先不做这个功能,等都实现好了,再来扩展这个功能。接下来还是看看代码实现,以更好的体会访问者模式。
(1)先来看看Customer的代码,Customer相当于访问者模式中的Element,它的实现跟以前相比有如下的改变:
示例代码如下:
public abstract class Customer {
??? private String customerId;
??? private String name;
??? /**
??? ?* 接受访问者的访问
??? ?* @param visitor 访问者对象
??? ?*/
??????
?
?
?
}
(2)看看两种客户的实现,先看企业客户的实现,示例代码如下:
public class EnterpriseCustomer extends Customer{
??? private String linkman;
??? private String linkTelephone;
??? private String registerAddress;
????
?
?
?
???
public class PersonalCustomer extends Customer{
??? private String telephone;
??? private int age;
???
?
?
?
???
/**
?* 访问者接口
?*/
public interface Visitor {
??? /**
??? ?* 访问企业客户,相当于给企业客户添加访问者的功能
??? ?* @param ec 企业客户的对象
??? ?*/
??? public void visitEnterpriseCustomer(EnterpriseCustomer ec);
??? /**
??? ?* 访问个人客户,相当于给个人客户添加访问者的功能
??? ?* @param pc 个人客户的对象
??? ?*/
??? public void visitPersonalCustomer(PersonalCustomer pc);
}
(4)接下来看看各个访问者的实现,每个访问者对象负责一类的功能处理,先看实现客户提出服务请求的功能的访问者,示例代码如下:
/**
?* 具体的访问者,实现客户提出服务请求的功能
?*/
public class ServiceRequestVisitor implements Visitor {
??? public void visitEnterpriseCustomer(EnterpriseCustomer ec){
?????? //企业客户提出的具体服务请求
?????? System.out.println(ec.getName()+"企业提出服务请求");
??? }
??? public void visitPersonalCustomer(PersonalCustomer pc){
?????? //个人客户提出的具体服务请求
?????? System.out.println("客户"+pc.getName()+"提出服务请求");
??? }
}
接下来看看实现对客户偏好分析功能的访问者,示例代码如下:
/**
?* 具体的访问者,实现对客户的偏好分析
?*/
public class PredilectionAnalyzeVisitor implements Visitor {
??? public void visitEnterpriseCustomer(EnterpriseCustomer ec){
?????? //根据过往购买的历史、潜在购买意向
?????? //以及客户所在行业的发展趋势、客户的发展预期等的分析
?????? System.out.println("现在对企业客户"+ec.getName()
+"进行产品偏好分析");
??? }
??? public void visitPersonalCustomer(PersonalCustomer pc){
?????? System.out.println("现在对个人客户"+pc.getName()
+"进行产品偏好分析");
??? }
}
(5)接下来看看ObjectStructure的实现,示例代码如下:
public class ObjectStructure {
??? /**
??? ?* 要操作的客户集合
??? ?*/
??? private Collection<Customer> col = new ArrayList<Customer>();
??? /**
??? ?* 提供给客户端操作的高层接口,具体的功能由客户端传入的访问者决定
??? ?* @param visitor 客户端需要使用的访问者
??? ?*/
??? public void handleRequest(Visitor visitor){
?????? //循环对象结构中的元素,接受访问
?????? for(Customer cm : col){
?????????? cm.accept(visitor);
?????? }
??? }
??? /**
??? ?* 组建对象结构,向对象结构中添加元素。
??? ?* 不同的对象结构有不同的构建方式
??? ?* @param ele 加入到对象结构的元素
??? ?*/
??? public void addElement(Customer ele){
?????? this.col.add(ele);
??? }
}
(6)该来写个客户端测试一下了,示例代码如下:
public class Client {
??? public static void main(String[] args) {
?????? //创建ObjectStructure
?????? ObjectStructure os = new ObjectStructure();
?????? //准备点测试数据,创建客户对象,并加入ObjectStructure
?????? Customer cm1 = new EnterpriseCustomer();
?????? cm1.setName("ABC集团");
?????? os.addElement(cm1);
??????
?????? Customer cm2 = new EnterpriseCustomer();
?????? cm2.setName("CDE公司");
?????? os.addElement(cm2);
??????
?????? Customer cm3 = new PersonalCustomer();
?????? cm3.setName("张三");
?????? os.addElement(cm3);
??????
?????? //客户提出服务请求,传入服务请求的Visitor
?????? ServiceRequestVisitor srVisitor =
new ServiceRequestVisitor();
??????
ABC集团企业提出服务请求
CDE公司企业提出服务请求
客户张三提出服务请求
现在对企业客户ABC集团进行产品偏好分析
现在对企业客户CDE公司进行产品偏好分析
现在对个人客户张三进行产品偏好分析
(7)使用访问者模式来重新实现了前面示例的功能,把各类相同的功能放到单独的访问者对象里面,使得代码不再杂乱,系统结构也更清晰,能方便的维护了,算是解决了前面示例的一个问题。
还有一个问题,就是看看能不能方便的增加新的功能,前面在示例的时候,故意留下了一个对客户进行价值分析的功能没有实现,那么接下来就看看如何把这个功能增加到已有的系统中。在访问者模式中要给对象结构增加新的功能,只需要把新的功能实现成为访问者,然后在客户端调用的时候使用这个访问者对象来访问对象结构即可。
接下来看看实现对客户价值分析功能的访问者,示例代码如下:
/**
?* 具体的访问者,实现对客户价值分析
?*/
public class WorthAnalyzeVisitor implements Visitor {
??? public void visitEnterpriseCustomer(EnterpriseCustomer ec){
?????? //根据购买的金额大小、购买的产品和服务的多少、购买的频率等进行分析
?????? //企业客户的标准会比个人客户的高
?????? System.out.println("现在对企业客户"+ec.getName()
+"进行价值分析");
??? }
??? public void visitPersonalCustomer(PersonalCustomer pc){
?????? System.out.println("现在对个人客户"+pc.getName()
+"进行价值分析");
??? }
}
使用这个功能,只要在客户端添加如下的代码即可,示例代码如下:
?????? //要对客户进行价值分析,传入价值分析的Visitor
?????? WorthAnalyzeVisitor waVisitor = new WorthAnalyzeVisitor();
?????? os.handleRequest(waVisitor);
去测试看看,是否能正确地把这个功能加入到已有的程序结构中。
转载私塾在线:http://sishuok.com/forum/blogPost/list/5880.html