設計模式-訪問者模式(Visitor)以及增加反射機制

一、定義

封裝某些作用於某種數據結構中各元素的操作,它可以在不改變數據結構的前提下定義作用於這些元素的新的操作。

二、類圖

在這裏插入圖片描述

三、代碼例子

先建一個元素接口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<IPersonService> 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());
}

}

到此,訪問者設計模式 梳理差不多了,如果有哪裏對不,請指出,謝謝。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章