设计模式-访问者模式(Visitor)以及增加反射机制

设计模式-访问者模式(Visitor)以及增加反射机制

    • 一、定义
    • 二、类图
    • 三、代码例子
    • 4、改进,加入反射
        • 4.1接口加入反射
        • 4.2 再改进

一、定义

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

二、类图

设计模式-访问者模式(Visitor)以及增加反射机制_第1张图片

三、代码例子

先建一个元素接口IPerson,定义一个方法accept ,为了让访问者能访问到自己

public interface IPersonService {
    void accept(IVisitorService action);
    String getName();
}
  1. 实现具体的元素(这里的元素一般是固定的,这个也是访问者模式的适应场景,如果元素经常变动,不适合访问者模式,这是重点,敲黑板),我这边选中用男人和女人做为元素,一般系统没有第三种
public class Man implements IPersonService {

    @Override
    public void accept(IActionService action) {
        action.visitor(this);
    }

    @Override
    public String getName() {
        return "Man";
    }
}

public class Women implements IPersonService {

    @Override
    public void accept(IActionService action) {
        action.visitor(this);

    }
    @Override
    public String getName() {
        return "Women";

    }
}
  1. 创建访问者(这里要注意,就是有几个元素,都会有一个对应的处理方法,这里也就是 有一点和开闭原则 不符合的地方吧,这里是重点,敲黑板
public interface IVisitorService {

    void visitorMan(Man man);

    void visitorWomen(Women man);
}
  1. 具体的访问者,我这边弄了两个访问者,visitor1 和visitor2
public class Visitor1 implements IVisitorService {

    @Override
    public void visitorMan(Man man) {
        System.out.println("Visitor1 " + man.getName());
    }

    @Override
    public void visitorWomen(Women woman) {
        System.out.println("Visitor1 " + woman.getName());
    }
}
public class Visitor2 implements IVisitorService {
    @Override
    public void visitorMan(Man man) {
        System.out.println("Visitor2  " + man.getName());
    }

    @Override
    public void visitorWomen(Women man) {
        System.out.println("Visitor2 " + man.getName());
    }
}
  1. 创建一个 ObjectStructure,用于枚举元素:
public class ObjectStructure {
    List list = Lists.newArrayList();
     /**
     * 增加
     *
     * @param action
     */
    void add(IPersonService action) {
        this.list.add(action);
    }
    /**
     * 移除
     *
     * @param action
     */
    void remove(IPersonService action) {
        this.list.remove(action);
    }
    /**
     * @param action
     */
    public void display(IVisitorService action) {
        list.stream().forEach(e -> {
            e.accept(action);
        });
    }
}

6 测试一下,建个client:

public class client {

    public static void main(String[] args) {

        ObjectStructure obj = new ObjectStructure();
        obj.add(new Man());
        obj.add(new Women());

        Visitor1 v1 = new Visitor1();
        obj.display(v1);
        
        Visitor2 v2 = new Visitor2();
        obj.display(v2);
    }
}

4、改进,加入反射

4.1接口加入反射

上面也说了,增加元素时比较麻烦嘛,设计模式的书里也提到过有一定的现在,这里可以用反射的方式,根据具体的元素执行具体的方法嘛(
参照文章

参考的这边文章 https://www.javaworld.com/article/2077602/java-tip-98--reflect-on-the-visitor-design-pattern.html)

思路:接口里面增加一个visit 方法,根据具体的类型,调用具体的执行方法,代码如下:

public interface IVisitorService {
   
    void visitorMan(Man man);
    void visitorWomen(Women man);

    /**
     * 这里是通过反射,选择具体的执行方法
     * @param obj
     */
    void visitor(Object obj);
}

具体的visitor 1(visitor2 类的代码就不贴了,和 visitor1 一样的) (敲黑板,划重点,这是匹配规则时,用了一个visitor 作为前缀,这里需要统一一个命名规则)如下:

public class Visitor1 implements IVisitorService {

    public void visitorMan(Man man) {
        System.out.println("Visitor1 " + man.getName());

    }
    public void visitorWomen(Women woman) {
        System.out.println("Visitor1 " + woman.getName());
    }

    @Override
    public void visitor(Object obj) {
        String className = obj.getClass().getName();
        String methodName = "visitor" + className.substring(className.lastIndexOf(".") + 1);
        try {
            Method method = getClass().getMethod(methodName, new Class[] {obj.getClass()});
            method.invoke(this, new Object[]{obj});
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

接下来所有的 元素里面的accept ,调用的是 visit() 方法:

public class Man implements IPersonService {
    @Override
    public void accept(IVisitorService action) {
        action.visitor(this);
    }
    @Override
    public String getName() {
        return "Man";
    }
}

小结一下:这样就可以解决,增加元素时IVisitor 里面类型定死的问题了,直接在实现类里面定义即可。

4.2 再改进

我感觉 在接口里面定义一个 visitor 方法,每一个实现类都需要实现,只要遵循规则,感觉每次都要实现太麻烦了,所以我提出再抽出一个 基础类,把visitor(Object obj) 方法放在里面,如果需要改动,也可以在实现类里面重写,如果不需要,直接默认使用

public abstract class BaseAction implements IVisitorService{

    public void visitor(Object obj) {
        String className = obj.getClass().getName();
        String methodName = "visitor" + className.substring(className.lastIndexOf(".") + 1);
        try {
            Method method = getClass().getMethod(methodName, new Class[] {obj.getClass()});
            method.invoke(this, new Object[]{obj});
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

具体的访问者只要继承 BaseAction 即可,其他不用动。如下为 Visitor1 的代码(Visitor2 代码几乎一样),这样一看,都简洁

public class Visitor1 extends BaseAction{

public void visitorMan(Man man) {
    System.out.println("Visitor1 " + man.getName());

}
public void visitorWomen(Women woman) {
    System.out.println("Visitor1 " + woman.getName());
}

}

到此,访问者设计模式 梳理差不多了,如果有哪里对不,请指出,谢谢。

你可能感兴趣的:(设计模式)