Java实现在访问者模式中使用反射

澳门新葡亰平台游戏网站 1

集合类型在面向对象编程中很常用,这也带来一些代码相关的问题。比如,“怎么操作集合中不同类型的对象?”

访问者模式C++实现

訪问者模式(Visitor
Pattern)是GoF提出的23种设计模式中的一种,属于行为模式。

一种做法就是遍历集合中的每个元素,然后根据它的类型而做具体的操作。这会很复杂,尤其当你不知道集合中元素的类型时。如果y要打印集合中的元素,可以写一个这样的方法:

1定义Visitor Pattern

据《大话设计模式》中说算是最复杂也是最难以理解的一种模式了。

public void messyPrintCollection(Collection collection) {
    Iterator iterator = collection.iterator()
    while (iterator.hasNext())
        System.out.println(iterator.next().toString())
}

封装一些作用于某种数据结构中各元素的操作,他可以在不改变数据结构的前提下定义作用于这些元素新的操作

   定义(源于GoF《Design
Pattern》):表示一个作用于某对象结构中的各元素的操作。它使你能够在

看起来很简单。仅仅调用了Object.toString()方法并打印出了对象,对吧?但如果你的集合是一个包含hashtable的vector呢?那会变得更复杂。你必须检查集合返回对象的类型:

2类图

不改变各元素类的前提下定义作用于这些元素的新操作。从定义能够看出结构对象是使用訪问者模式必备

public void messyPrintCollection(Collection collection) {
    Iterator iterator = collection.iterator()
    while (iterator.hasNext()) {
        Object o = iterator.next();
        if (o instanceof Collection)
            messyPrintCollection((Collection)o);
        else
            System.out.println(o.toString());
        }
}

澳门新葡亰平台游戏网站 1

条件,并且这个结构对象必须存在遍历自身各个对象的方法。这便类似于Java语言其中的collection概念了。

好了,现在可以处理内嵌的集合对象,但其他对象返回的字符串不是你想要的呢?假如你想在字符串对象加上引号,想在Float对象后加一个f,你该怎么做?代码会变得更加复杂:

角色分析

  涉及角色 :

public void messyPrintCollection(Collection collection) {
    Iterator iterator = collection.iterator()
    while (iterator.hasNext()) {
        Object o = iterator.next();
        if (o instanceof Collection)
            messyPrintCollection((Collection)o);
        else if (o instanceof String)
            System.out.println("'"+o.toString()+"'");
        else if (o instanceof Float)
            System.out.println(o.toString()+"f");
        else
            System.out.println(o.toString());
    }
}

Visitor抽象访问者,声明可以访问哪些元素,具体到程序中就是Visit方法参数定义哪些对象可以被访问

  1.IVisitor
抽象訪问者角色,为该对象结构中详细元素角色声明一个訪问操作接口。该操作接口的名字和

代码很快就变杂乱了。你不想让代码中包含一大堆的if-else语句!怎么避免呢?访问者模式可以帮助你。

ConcreteVisitor,他影响访问者访问到一个类之后该怎么做

參数标识了发送訪问请求给具休訪问者的具休元素角色,这样訪问者就能够通过该元素角色的特定接口直接訪问它。

为实现访问者模式,你需要创建一个Visitor接口,为被访问的集合对象创建一个Visitable接口。接下来需要创建具体的类来实现Visitor和Visitable接口。这两个接口大致如下:

Element抽象元素,声明哪一个类访问者访问,程序上通过accept方法中的参数定义

  2.ConcreteVisitor.详细訪问者角色,实现Visitor声明的接口。

public interface Visitor
{
    public void visitCollection(Collection collection);
    public void visitString(String string);
    public void visitFloat(Float float);
}
public interface Visitable
{
    public void accept(Visitor visitor);
}

ConcreteElement实现accept方法,visitor.visit(this)基本形成一种模式

  3.Element
定义一个接受訪问操作(accept()),它以一个訪问者(Visitor)作为參数。

对于一个具体的String类,可以这么实现:

ObjectStrture结构对象,元素产生者,一般容纳多个不同类,不同接口的容器,如list,set,map,在项目中一般很少抽象这个角色

  4.ConcreteElement
详细元素,实现了抽象元素(Element)所定义的接受操作接口。

public class VisitableString implements Visitable
{
    private String value;
    public VisitableString(String string) {
        value = string;
    }
    public void accept(Visitor visitor) {
        visitor.visitString(this);
    }
}

3实现

  5.ObjectStructure 结构对象角色,这是使用訪问者模式必备的角色。

在accept方法中,根据不同的类型,调用visitor中对应的方法:

class Element

它具备下面特性:

visitor.visitString(this)

{

能枚举它的元素;能够提供一个高层接口以同意訪问者訪问它的元素;如有须要,能够设计成一个

具体Visitor的实现方式如下:

protected:

复合对象或者一个聚集(如一个列表或无序集合)。

public class PrintVisitor implements Visitor
{
    public void visitCollection(Collection collection) {
        Iterator iterator = collection.iterator();
        while (iterator.hasNext()) {
            Object o = iterator.next();
            if (o instanceof Visitable)
                ((Visitable)o).accept(this);
    }
    public void visitString(String string) {
        System.out.println("'"+string+"'");
    }
    public void visitFloat(Float float) {
        System.out.println(float.toString()+"f");
    }
}

  Element();

  訪问者模式的几个特点:

到时候,只要实现了VisitableFloat类和VisitableCollection类并调用合适的visitor方法,你就可以去掉包含一堆if-else结构的messyPrintCollection方法,采用一种十分清爽的方式实现了同样的功能。visitCollection()方法调用了Visitable.accept(this),而accept()方法又反过来调用了visitor中正确的方法。这就是双分派:Visitor调用了一个Visitable类中的方法,这个方法又反过来调用了Visitor类中的方法。

public:

  訪问者模式把数据结构和作用于结构上的操作解耦合,使得操作集合可相对自由地演化。

尽管实现visitor后,if-else语句不见了,但还是引入了很多附加的代码。你不得不将原始的对象——String和Float,打包到一个实现Visitable接口的类中。虽然很烦人,但这一般来说不是个问题。因为你可以限制被访问集合只能包含Visitable对象。

  void doSomething();

  訪问者模式适用于数据结构相对稳定算法又易变化的系统。

然而,这还有很多附加的工作要做。更坏的是,当你想增加一个新的Visitable类型时怎么办,比如VisitableInteger?这是访问者模式的一个主要缺点。如果你想增加一个新的Visitable类型,你不得不改变Visitor接口以及每个实现Visitor接口方法的类。你可以不把Visitor设计为接口,取而代之,可以把Visitor设计为一个带有空操作的抽象基类。这与Java
GUI中的Adapter类很相似。这么做的问题是你会用尽单次继承,而常见的情形是你还想用继承实现其他功能,比如继承StringWriter类。这同样只能成功访问实现Visitable接口的对象。

  void accept(Ivisitor visitor) = 0;

由于訪问者模式使得算法操作添加变得easy。

幸运的是,Java可以让你的访问者模式更灵活,你可以按你的意愿增加Visitable对象。怎么实现呢?答案是使用反射。使用反射的ReflectiveVisitor接口只需要一个方法:

  virtual ~Element() = 0;

若系统数据结构对象易于变化。常常有新的数据对象添加进来,则不适合使用訪问者模式。

public interface ReflectiveVisitor {
    public void visit(Object o);
}

};

  訪问者模式的长处是添加操作非常easy,由于添加操作意味着添加新的訪问者。

好了,上面很简单。Visitable接口先不动,待会我会说。现在,我使用反射实现PrintVisitor类。

class ConcreteElement

訪问者模式将有关行为集中

public class PrintVisitor implements ReflectiveVisitor {
    public void visitCollection(Collection collection)
    { ... same as above ... }
    public void visitString(String string)
    { ... same as above ... }
    public void visitFloat(Float float)
    { ... same as above ... }
    public void default(Object o)
    {
        System.out.println(o.toString());
    }
    public void visit(Object o) {
        // Class.getName() returns package information as well.
        // This strips off the package information giving us
        // just the class name
        String methodName = o.getClass().getName();
        methodName = "visit"+
                    methodName.substring(methodName.lastIndexOf('.')+1);
        // Now we try to invoke the method visit<methodName>
        try {
            // Get the method visitFoo(Foo foo)
            Method m = getClass().getMethod(methodName,
                new Class[] { o.getClass() });
            // Try to invoke visitFoo(Foo foo)
            m.invoke(this, new Object[] { o });
        } catch (NoSuchMethodException e) {
            // No method, so do the default implementation
            default(o);
        }
    }
}

{

到一个訪问者对象中,其改变不影响系统数据结构。其缺点就是添加新的数据结构非常困难。

现在你无需使用Visitable包装类(包装了原始类型String、Float)。你可以直接访问visit(),它会调用正确的方法。visit()的一个优点是它会分派它认为合适的方法。这不一定使用反射,可以使用完全不同的一种机制。

public:

  适用情况 :

在新的PrintVisitor类中,有对应于Collections、String和Float的操作方法;对于不能处理的类型,可以通过catch语句捕捉。对于不能处理的类型,可以通过扩展visit()方法来尝试处理它们的所有超类。首先,增加一个新的方法getMethod(Class
c),返回值是一个可被触发的方法。它会搜索Class
c的所有父类和接口,以找到一个匹配方法。

  void doSomething();

  1)
一个对象结构包括非常多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其详细类的操作。

protected Method getMethod(Class c) {
    Class newc = c;
    Method m = null;
    // Try the superclasses
    while (m == null && newc != Object.class) {
        String method = newc.getName();
        method = "visit" + method.substring(method.lastIndexOf('.') + 1);
        try {
            m = getClass().getMethod(method, new Class[] {newc});
        } catch (NoSuchMethodException e) {
            newc = newc.getSuperclass();
        }
    }
    // Try the interfaces.  If necessary, you
    // can sort them first to define 'visitable' interface wins
    // in case an object implements more than one.
    if (newc == Object.class) {
        Class[] interfaces = c.getInterfaces();
        for (int i = 0; i < interfaces.length; i++) {
            String method = interfaces[i].getName();
            method = "visit" + method.substring(method.lastIndexOf('.') + 1);
            try {
                m = getClass().getMethod(method, new Class[] {interfaces[i]});
            } catch (NoSuchMethodException e) {}
        }
    }
    if (m == null) {
        try {
            m = thisclass.getMethod("visitObject", new Class[] {Object.class});
        } catch (Exception e) {
            // Can't happen
        }
    }
    return m;
}

  void accept(IVisitor visitor)

  2)
须要对一个对象结构中的对象进行非常多不同的而且不相关的操作。而你想避免让这些操作“污染”这些对象的类。

这看上去很复杂,实际上并不。大致来说,首先根据传入的class名称搜索可用方法;如果没找到,就尝试从父类搜索;如果还没找到,就从接口中尝试。最后,(仍没找到)可以使用visitObject()作为默认方法。

  {

Visitor模式使得你能够将相关的操作集中起来 定义在一个类中。

由于大家对传统的访问者模式比较熟悉,这里沿用了之前方法命名的惯例。但是,有些人可能注意到,把所有的方法都命名为“visit”并通过参数类型不同来区分,这样更高效。然而,如果你这么做,你必须把visit(Object
o)方法的名称改为其他,比如dispatch(Object
o)。否则,(当没有对应处理方法时),你无法退回到默认的处理方法,并且当你调用visit(Object
o)方法时,为了确保正确的方法调用,你必须将参数强制转化为Object。

    visitor.visit(*this);

  3)
当该对象结构被非常多应用共享时。用Visitor模式让每一个应用仅包括须要用到的操作。

为了利用getMethod()方法,现在需要修改一下visit()方法。

  }

  4) 定义对象结构的类非常少改变,但常常须要在此结构上定义新的操作。

public void visit(Object object) {
    try {
        Method method = getMethod(getClass(), object.getClass());
        method.invoke(this, new Object[] {object});
    } catch (Exception e) { }
}

};

改变对象结构类须要重定义对全部訪

现在,visitor类更加强大了——可以传入任意的对象并且有对应的处理方法。另外,有一个默认处理方法,visitObject(Object
o),的好处就是就可以捕捉到任何没有明确说明的类型。再稍微修改下,你甚至可以添加一个visitNull()方法。

class IVisitor

澳门新葡亰平台游戏网站 ,问者的接口,这可能须要非常大的代价。

我仍保留Visitable接口是有原因的。传统访问者模式的另一个好处是它可以通过Visitable对象控制对象结构的遍历顺序。举例来说,假如有一个实现了Visitable接口的类TreeNode,它在accept()方法中遍历自己的左右节点。

{

假设对象结构类常常改变,那么可能还是在这些类中定义这些操作较好。

public void accept(Visitor visitor) {
    visitor.visitTreeNode(this);
    visitor.visitTreeNode(leftsubtree);
    visitor.visitTreeNode(rightsubtree);
}

public:

public class Body {  

这样,只要修改下Visitor类,就可以通过Visitable类控制遍历:

  void Visitor(ConcreteElent el);

    public void accept(IVisitor visitor) {  

public void visit(Object object) throws Exception
{
    Method method = getMethod(getClass(), object.getClass());
    method.invoke(this, new Object[] {object});
    if (object instanceof Visitable)
    {
        callAccept((Visitable) object);
    }
}
public void callAccept(Visitable visitable) {
    visitable.accept(this);
}

};

        visitor.visit(this);  

如果你实现了Visitable对象的结构,你可以保持callAccept()不变,就可以使用Visitable控制的对象遍历。如果你想在visitor中遍历对象结构,你只需重写allAccept()方法,让它什么都不做。

class Visitor:public IVisitor

    }  

当使用几个不同的visitor去操作同一个对象集合时,访问者模式的力量就会展现出来。比如,当前有一个解释器、中序遍历器、后续遍历器、XML编写器以及SQL编写器,它们可以处理同一个对象集合。我可以轻松地为这个集合再写一个先序遍历器或者一个SOAP编写器。另外,它们可以很好地兼容它们不识别的类型,或者我愿意的话可以让它们抛出异常。

{

}  

总结

使用Java反射,可以使访问者模式提供一种更加强大的方式操作对象结构,可以按照需求灵活地增加新的Visitable类型。我希望在你的编程之旅中可以使用访问者模式。

Jeremy
Blosser有5年的Java编程经验,他在很多软件公司工作过。他现在在一家创业型公司Software
Instruments供职。你可以访问Jeremy的网站

public:

public class Engine {  

了解更多

  • Patterns homepage
  • [Design PatternsElements of Reusable Object-Oriented Software,
    Erich Gamma, et al. (Addison-Wesley,
    1995)](
  • Patterns in Java, Volume 1, Mark Grand (John Wiley &
    Sons, 1998)
  • Patterns in Java, Volume 2, Mark Grand (John Wiley &
    Sons, 1999)
  • View all previous Java Tips and submit your
    own

  void visit(Concrete el)

    public  void accept(IVisitor visitor) {  

  {

             visitor.visit(this);  

    el.doSomething();

     }  

  }

}  

};

public class Wheel {  

//结构体对象

    private String name;  

class ObjectStuture

    public Wheel(String name) {  

{

        this.name = name;  

public:

    }  

  static Element createElement()

    String getName() {  

  {

        return this.name;  

    Random rand = new Random();

    }  

    return new ConcreteElement();

    public  void accept(IVisitor visitor) {  

  }

        visitor.visit(this);  

};

    }  

4应用

}  

①优点

public class Car {  

单一职责,Elementz子类加载数据,Visitor负责报表展示

    private Engine  engine = new Engine();  

优秀的扩展性,增加数据操作是很方便的

    private Body    body   = new Body();  

灵活性高,可以实现Element提供数据,Visitor对数据进行修饰并呈现

    private Wheel[] wheels   

②缺点

        = { new Wheel(“front left”), new Wheel(“front right”),  

具体元素对访问者公布细节

            new Wheel(“back left”) , new Wheel(“back right”)  };  

具体元素变更困难,因为要同时变更Visitor

    public void accept(IVisitor visitor) {  

违背依赖倒置原则,访问者依赖具体元素而不是抽象元素

        visitor.visit(this);  

③使用场景

        engine.accept(visitor);  

一个对象结构包含很多类对象,他们接口不同,而你需要对其是实施的具体操作依赖具体类.迭代器模式不能胜任

        body.accept(visitor) ;   

需要对一个结构体中的对象进行很多不同且不相关的操作,且不想操作
污染对象的类。

        for (int i = 0; i < wheels.length; ++ i)  

::业务规则要求遍历多个不同的对象,与迭代器模式对比。

            wheels[i].accept(visitor);  

同时访问者模式也可以充当拦截器角色,在混编模式中

    }  

5扩展

}    

①统计功能

public interface IVisitor {  

将数据统计和批处理通过访问者模式进行处理

    void visit(Wheel wheel);  

②多个访问者

    void visit(Engine engine);  

 

    void visit(Body body);  

    void visit(Car car);  

}    

    

public class PrintVisitor implements IVisitor {   

    @Override  

    public void visit(Wheel wheel) {  

        System.out.println(“Visiting ” + wheel.getName() + ” wheel”);   

    }    

    @Override  

    public void visit(Engine engine) {  

        System.out.println(“Visiting engine”);  

    }    

    @Override  

    public void visit(Body body) {  

        System.out.println(“Visiting body”);  

    }    

    @Override  

    public void visit(Car car) {  

        System.out.println(“Visiting car”);  

    }   

————————样例2———————–

Visitor模式,在不改动已有程序结构的前提下,通过加入额外的“訪问者”来完毕对已有代码功能的提升。Visitor模式的组成结构:

  1)
訪问者角色(Visitor):声明一个訪问接口。

接口的名称和方法的參数标识了向訪问者发送请求的元素角色。这样訪问者就能够通过该元素角色的特定接口直接訪问它。

  2) 详细訪问者角色(Concrete
Visitor):实现訪问者角色(Visitor)接口

  3)
元素角色(Element):定义一个Accept操作,它以一个訪问者为參数。

  4) 详细元素角色(Concrete
Element):实现元素角色(Element)接口。

  5) 对象结构角色(Object
Structure):这是使用Visitor模式必须的角色。它要具备下面特征:能枚举它的元素;能够提供一个高层的接口同意訪问者角色訪问它的元素;能够是一个组合(组合模式)或是一个集合,如一个列表或一个无序集合

public abstract class Customer {

private String customerId;

private String name;

public String getCustomerId() {

    return customerId;

}

public void setCustomerId(String customerId) {

   this.customerId = customerId;

}

public String getName() {

   return name;

}

public void setName(String name) {

   this.name = name;

}

//接受訪问者的訪问

public abstract void accept(Visitor visitor);

//企业客户

public class EnterpriseCustomer extends Customer {

 private String linkman;

 private String linkTelephone;

 private String registerAddress;

 public String getLinkman() {

    return linkman;

 }

 public void setLinkman(String linkman) {

  this.linkman = linkman;

 }

 public String getLinkTelephone() {

 return linkTelephone;

 }

 public void setLinkTelephone(String linkTelephone) {

 this.linkTelephone = linkTelephone;

 }

 public String getRegisterAddress() {

 return registerAddress;

 }

 public void setRegisterAddress(String registerAddress) {

this.registerAddress = registerAddress;

 }

 @Override

 public void accept(Visitor visitor) {

 //回调訪问者对象的方法

         visitor.visitEnterpriseCustomer(this);

  }

}

//个人客户

public class PersonalCustomer extends Customer {

 private String telephone;

 private int age;

 public String getTelephone() {

      return telephone;

 }

 public void setTelephone(String telephone) {

         this.telephone = telephone;

 }

 public int getAge() {

       return age;

 }

 public void setAge(int age) {

          this.age = age;

 }

 @Override

 public void accept(Visitor visitor) {

     //回调訪问者对象的方法

     visitor.visitPersonalCustomer(this);

     }

}

/**

 * 訪问者接口

 */

public interface Visitor {

    // 訪问企业客户。相当于给企业客户加入訪问者功能

    public void visitEnterpriseCustomer(EnterpriseCustomer ec);

    //訪问个人客户,相当于给个人客户加入訪问者的功能

    public void visitPersonalCustomer(PersonalCustomer pc);

}

/**

 * 详细的訪问者。实现对客户的偏好分析

 */

public class PredilectionAnalyzeVisitor implements Visitor {

  @Override

  public void visitEnterpriseCustomer(EnterpriseCustomer ec) {

  // TODO
依据以往的购买历史、潜在购买意向,以及客户所在行业的发展趋势、客户的发展趋势等的分析

                System.out.println(“如今对企业客户” + ec.getName() +
“进行产品偏好分析”);

  }

  @Override

  public void visitPersonalCustomer(PersonalCustomer pc) {

             System.out.println(“如今对个人客户” + pc.getName() +
“进行产品偏好分析”);

  }

}

/**

 * 详细的訪问者,实现客户提出服务请求的功能

 */

public class ServiceRequestVisitor implements Visitor {

  @Override

public void visitEnterpriseCustomer(EnterpriseCustomer ec) {

        // TODO 企业客户提出的详细服务请求

         System.out.println(ec.getName() + “企业提出服务请求”);

}

@Override

public void visitPersonalCustomer(PersonalCustomer pc) {

       // TODO 个人客户提出的详细服务请求

      System.out.println(“客户” + pc.getName() + “提出服务请求”);

     }

}

public class ObjectStructure {

 /**

  * 要操作的客户集合

  */

 private Collection<Customer> col = new
ArrayList<Customer>();

 /**

  * 提供client操作的高层接口,详细的功能由client传入的訪问者决定

  * @param visitor client须要的訪问者

  */

 public void handleRequest(Visitor visitor) {

         for(Customer cm : col) {

              cm.accept(visitor);

        }

 }

 /**

  * 组建对象结构,想对象中加入元素

  * 不同的对象结构有不同的构建方式

  * @param ele 增加到对象的结构元素

  */

 public void addElement(Customer ele) {

          this.col.add(ele);

     }

}

public class Client {

 public static void main(String[] args) {

ObjectStructure os = new ObjectStructure();

Customer cml = new EnterpriseCustomer();

cml.setName(“钢铁侠”);

os.addElement(cml);

Customer cm2 = new EnterpriseCustomer();

cm2.setName(“CDE公司”);

os.addElement(cm2);

Customer cm3 = new PersonalCustomer();

cm3.setName(“美国佬”);

os.addElement(cm3);

ServiceRequestVisitor srVisitor = new ServiceRequestVisitor();

os.handleRequest(srVisitor);

PredilectionAnalyzeVisitor paVisitor = new PredilectionAnalyzeVisitor();

os.handleRequest(paVisitor);

  WorthAnalyzeVisitor waVisitor = new WorthAnalyzeVisitor();

  os.handleRequest(waVisitor);

  }

}

You can leave a response, or trackback from your own site.

Leave a Reply

网站地图xml地图