`

访问者模式2

 
阅读更多

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);

去测试看看,是否能正确地把这个功能加入到已有的程序结构中。

转载私塾在线:http://sishuok.com/forum/blogPost/list/5880.html

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics