25.2 解决方案
25.2.1 访问者模式来解决
用来解决上述问题的一个合理的解决方案,就是使用访问者模式。那么什么是访问者模式呢?
(1)访问者模式定义
(2)应用访问者模式来解决的思路
仔细分析上面的示例,对于客户这个对象结构,不想改变类,又要添加新的功能,很明显就需要一种动态的方式,在运行期间把功能动态地添加到对象结构中去。
有些朋友可能会想起装饰模式,装饰模式可以实现为一个对象透明的添加功能,但装饰模式基本上是在现有的功能的基础之上进行功能添加,实际上是对现有功能的加强或者改造。并不是在现有功能不改动的情况下,为对象添加新的功能。
看来需要另外寻找新的解决方式了,可以应用访问者模式来解决这个问题,访问者模式实现的基本思路如下:
- 首先定义一个接口来代表要新加入的功能,为了通用,也就是定义一个通用的功能方法来代表新加入的功能
- 然后在对象结构上添加一个方法,作为通用的功能方法,也就是可以代表被添加的功能,在这个方法中传入具体的实现新功能的对象
- 然后在对象结构的具体实现对象里面实现这个方法,回调传入具体的实现新功能的对象,就相当于调用到新功能上了
- 接下来的步骤就是提供实现新功能的对象
- 最后再提供一个能够循环访问整个对象结构的类,让这个类来提供符合客户端业务需求的方法,来满足客户端调用的需要
这样一来,只要提供实现新功能的对象给对象结构,就可以为这些对象添加新的功能,由于在对象结构中定义的方法是通用的功能方法,所以什么新功能都可以加入。
25.2.2 模式结构和说明
访问者模式的结构如图25.3所示:
图25.3 访问者模式结构示意图
Visitor
访问者接口,为所有的访问者对象声明一个visit方法,用来代表为对象结构添加的功能,理论上可以代表任意的功能。
ConcreteVisitor
具体的访问者实现对象,实现要真正被添加到对象结构中的功能。
Element
抽象的元素对象,对象结构的顶层接口,定义接受访问的操作。
ConcreteElement
具体元素对象,对象结构中具体的对象,也是被访问的对象,通常会回调访问者的真实功能,同时开放自身的数据供访问者使用。
ObjectStructure
对象结构,通常包含多个被访问的对象,它可以遍历这多个被访问的对象,也可以让访问者访问它的元素。可以是一个复合或是一个集合,如一个列表或无序集合。
但是请注意:这个ObjectStructure并不是我们在前面讲到的对象结构,前面一直讲的对象结构是指的一系列对象的定义结构,是概念上的东西;而ObjectStructure可以看成是对象结构中的一系列对象的一个集合,是用来辅助客户端访问这一系列对象的,所以为了不造成大家的困惑,后面提到ObjectStructure的时候,就用英文名称来代替,不把它翻译成中文。
25.2.3 访问者模式示例代码
(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); } } |
25.2.4 使用访问者模式重写示例
要使用访问者模式来重写示例,首先就要按照访问者模式的结构,分离出两个类层次来,一个是对应于元素的类层次,一个是对应于访问者的类层次。
对于对应于元素的类层次,现在已经有了,就是客户的对象层次。而对应于访问者的类层次,现在还没有,不过,按照访问者模式的结构,应该是先定义一个访问者接口,然后把每种业务实现成为一个单独的访问者对象,也就是说应该使用一个访问者对象来实现对客户的偏好分析,而用另外一个访问者对象来实现对客户的价值分析。
在分离好两个类层次过后,为了方便客户端的访问,定义一个ObjectStructure,其实就类似于前面示例中的客户管理的业务对象。新的示例的结构如图25.4所示:
图25.4 使用访问者模式的示例程序结构示意图
仔细查看图25.4所示的程序结构示意图,细心的朋友会发现,在图上没有出现对客户进行价值分析的功能了。这是为了示范“使用访问者模式来实现示例功能过后,可以很容易的给对象结构增加新的功能”,所以先不做这个功能,等都实现好了,再来扩展这个功能。接下来还是看看代码实现,以更好的体会访问者模式。
(1)先来看看Customer的代码,Customer相当于访问者模式中的Element,它的实现跟以前相比有如下的改变:
- 新增一个接受访问者访问的方法
- 把能够分离出去放到访问者中实现的方法,从Customer中删除掉,包括:客户提出服务请求的方法、对客户进行偏好分析的方法、对客户进行价值分析的方法等
示例代码如下:
public abstract class Customer { private String customerId; private String name; /** * 接受访问者的访问 * @param visitor 访问者对象 */ public abstract void accept(Visitor visitor);
} |
(2)看看两种客户的实现,先看企业客户的实现,示例代码如下:
public class EnterpriseCustomer extends Customer{ private String linkman; private String linkTelephone; private String registerAddress;
public void accept(Visitor visitor) { //回调访问者对象的相应方法 visitor.visitEnterpriseCustomer(this); } } |
再看看个人客户的实现,示例代码如下:
public class PersonalCustomer extends Customer{ private String telephone; private int age;
public void accept(Visitor visitor) { //回调访问者对象的相应方法 visitor.visitPersonalCustomer(this); } } |
(3)看看访问者的接口定义,示例代码如下:
/** * 访问者接口 */ 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(); os.handleRequest(srVisitor);
//要对客户进行偏好分析,传入偏好分析的Visitor PredilectionAnalyzeVisitor paVisitor = new PredilectionAnalyzeVisitor(); os.handleRequest(paVisitor); } } |
运行结果如下:
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); |
去测试看看,是否能正确地把这个功能加入到已有的程序结构中。
相关推荐
在“访问者模式例子2”中,我们将深入探讨如何利用该模式来扩展系统的行为。 访问者模式的核心思想是将数据结构与数据操作分离。它提供了一种方式,让新的操作可以动态地添加到已存在的类层次结构中,而无需修改...
《访问者模式在实际开发中的应用深度解析》 访问者模式是设计模式中的一种行为模式,它在实际的软件开发中具有重要的应用价值。通过理解并掌握访问者模式,开发者可以更好地实现对象结构中元素的操作,同时保持元素...
**访问者模式**是一种行为设计模式,它允许在不修改对象结构的情况下添加新的操作。这种模式主要用于处理具有复杂对象结构的情况,使得我们可以对结构中的每个元素执行特定操作,而无需暴露其内部实现。 访问者模式...
访问者模式(Visitor)是一种行为设计模式,它允许在不修改对象结构的前提下向对象结构中的元素添加新的操作。这种模式将算法与数据结构分离,使得算法可以独立于数据结构进行变化,增强了系统的可扩展性。 在C++中...
**访问者模式**是一种设计模式,它在软件工程中用于在不修改对象结构的前提下,为对象添加新的操作或行为。这种模式将数据结构与数据操作分离,使得可以在不改变对象本身的情况下扩展其功能。访问者模式是行为设计...
访问者模式是一种设计模式,属于行为模式类别,其主要目的是在不修改对象结构的前提下,为对象结构中的元素提供新的操作。这种模式的核心思想是将数据结构与数据操作分离,使得增加新的操作变得容易,同时也避免了对...
访问者模式是一种软件设计模式,它在对象结构中定义了一个访问者的接口,使得该访问者可以访问该结构中的每一个元素,同时不影响对象结构本身。这种模式的主要目的是将数据操作与数据结构分离,使得数据结构的变化...
访问者模式是一种软件设计模式,它在对象结构中定义了一个访问者的接口,使得该访问者可以访问该结构中的每一个元素,同时不影响这些元素自身的行为。这种模式的主要目的是将数据操作和业务逻辑分离,使得数据结构...
**访问者模式** 访问者模式(Visitor Pattern)是一种行为设计模式,它使你能在不修改对象结构的前提下向其添加新的操作。这种模式常用于处理具有相同接口或抽象类的对象结构,使得可以在不改变原有结构的基础上...
**访问者模式(Visitor)详解** 访问者模式是一种行为设计模式,它使你可以在不修改对象结构的情况下,为对象添加新的操作。这种模式的核心在于将数据结构与对这些数据的操作解耦,使得增加新的操作变得容易,同时...
访问者模式是一种设计模式,属于行为模式类别,它在软件工程中被用来分离对象结构与对这个结构进行操作的行为。这种模式的主要目的是使代码结构更加灵活,同时增加新的操作而无需修改现有对象结构。在“访问者模式”...
访问者模式是一种软件设计模式,它允许在不改变对象结构的情况下,在对象的现有结构上增加新的操作。在Java中,这种模式尤其有用,因为它能够帮助我们保持类的封装性,同时提供了一种灵活的方式来扩展对象的功能。...
【访问者模式】是一种行为设计模式,其主要目的是在不修改已有对象结构的前提下,为对象增加新的操作。这种模式在23种经典设计模式中属于较为复杂的一种,因为它的理解和应用相对困难,可能导致代码可读性和可维护性...
**访问者模式(Visitor Pattern)**是一种行为设计模式,它提供了一种在不修改对象结构的情况下增加新操作的方法。这种模式的主要思想是将数据结构与算法分离,使得算法可以在不改变对象结构的情况下独立变化。 在...
访问者模式是一种设计模式,主要用于在不修改原有类层次结构的基础上,增加新的操作或功能。在UML(统一建模语言)文档中,访问者模式通常用于表示对象结构中的类如何接受并处理来自访问者的访问请求。这个模式的...
**访问者模式**是软件设计模式中的一种结构型模式,它允许在不修改对象结构的情况下,在对象上增加新的操作。这种模式将数据结构与数据操作分离,使得代码更易于维护和扩展。 在Java中实现访问者模式,通常包括以下...
**访问者模式详解** 访问者模式是一种行为设计模式,它允许在不修改对象结构的情况下向对象添加新的操作。这种模式的核心思想是将数据结构与算法分离,使得算法可以在不改变对象结构的前提下增加对对象的操作。 在...
访问者模式是一种软件设计模式,属于行为模式范畴,它的核心思想是将数据结构与数据操作分离,使得在不修改原有对象结构的情况下,可以为对象添加新的操作。这种模式常用于处理具有复杂对象结构且需要对这些对象进行...
### Java设计模式之访问者模式详解 #### 模式动机 访问者模式主要应用于处理一组对象,这组对象通常被存储在一个聚合结构(如列表、树等)中,并且这组对象具有不同的类型。访问者模式的核心思想在于,可以通过向...