訪問者模式

前言

訪問者模式是行爲型設計模式。行爲型設計模式主要的關注點是對象內部算法及對象之間的職責和分配,好比,具體實現算法、選擇策略、狀態變化等抽象概念。linux

行爲型模式還能夠分爲如下兩種。算法

  • 類行爲型模式:使用繼承的方式來關聯不一樣類之間的行爲。windows

  • 對象行爲型模式:使用組合或聚合方式來分配不一樣類之間的行爲。設計模式

目錄

1、定義

封裝一些做用於某種數據結構中的各元素操做,它能夠在不改變數據結構的前提下定義做用於這些元素的新的操做。markdown

官方定義,剛開始的時候仍是須要看代碼才能理解,其實就是新建一個類來封裝對其餘類的操做,例如獲取其餘類的數據等等,但不改變其餘類的數據結構。數據結構

2、模式原理分析

先上模板代碼,再舉例ide

//1.抽象元素
public abstract class Element{
    //定義業務邏輯
    public abstract void doSomething();
    //容許誰來訪問
    public abstract void accept(IVisitor visitor);
}
//2.具體元素
public abstract ConcreteElement1 extend Element{
    //完善業務邏輯
    public void accept(IVisitor visitor){
        visitor.visit(this);//將本身傳遞給visitor類
    }
}
public abstract ConcreteElement2 extend Element{
    //完善業務邏輯
    public void accept(IVisitor visitor){
        visitor.visit(this);//將本身傳遞給visitor類
    }
}
//3.抽象訪問者
public interface IVisitor{
    //能夠訪問哪些對象
    public void visit(ConcreteElement1 el1);
    public void visit(ConcreteElement2 el2);
}
//4.具體的訪問者
public class Visitor implements IVisitor{
    //訪問el1元素
    public void visit(ConcreteElement1 el1){
        el1.doSomething();
    }
    //訪問el2元素
    public void visit(ConcreteElement2 el2){
        el2.doSomething();
    }
}咱們再來看下簡單例子,經過不一樣路由訪問不一樣操做系統
複製代碼

假設你是一家路由器軟件的生產商,接了不少家不一樣硬件品牌的路由器的軟件需求(好比,D-LinkTP-Link),這時你須要針對不一樣的操做系統(好比,LinuxWindows)作路由器發送數據的兼容功能this

//1.訪問角色類
public interface Router {
    void sendData(char[] data);
    void accept(RouterVisitor v);
}
//2.根據不一樣型號路由器具體實現對應功能
public class DLinkRouter implements Router{

    @Override
    public void sendData(char[] data) {
    }

    @Override
    public void accept(RouterVisitor v) {
        v.visit(this);
    }
}
public class TPLinkRouter implements Router {
    @Override
    public void sendData(char[] data) {
    }
    @Override
    public void accept(RouterVisitor v) {
        v.visit(this);
    }

}
//3.訪問者類  用於給不一樣路由器提供訪問的入口點
public interface RouterVisitor {
    void visit(DLinkRouter router);
    void visit(TPLinkRouter router);
}

public class LinuxRouterVisitor implements RouterVisitor{

    @Override
    public void visit(DLinkRouter router) {
        System.out.println("=== DLinkRouter Linux visit success!");
    }

    @Override
    public void visit(TPLinkRouter router) {
        System.out.println("=== TPLinkRouter Linux visit success!");
    }

}

public class WindowsRouterVisitor implements RouterVisitor{

    @Override
    public void visit(DLinkRouter router) {
        System.out.println("=== DLinkRouter Windows visit success!");
    }

    @Override
    public void visit(TPLinkRouter router) {
        System.out.println("=== DLinkRouter Windows visit success!");
    }

}
//4.場景類
public class Client {

    public static void main(String[] args) {
        LinuxRouterVisitor linuxRouterVisitor = new LinuxRouterVisitor();
        WindowsRouterVisitor windowsRouterVisitor = new WindowsRouterVisitor();

        DLinkRouter dLinkRouter = new DLinkRouter();
        dLinkRouter.accept(linuxRouterVisitor);
        dLinkRouter.accept(windowsRouterVisitor);

        TPLinkRouter tpLinkRouter = new TPLinkRouter();
        tpLinkRouter.accept(linuxRouterVisitor);
        tpLinkRouter.accept(windowsRouterVisitor);
    }
}

//輸出結果
=== DLinkRouter Linux visit success!
=== DLinkRouter Windows  visit success!
=== TPLinkRouter Linux visit success!
=== DLinkRouter Windows  visit success!
複製代碼

不一樣型號的路由器能夠在運行時動態添加(第一次分派)accept,對於不一樣的操做系統來講,路由器能夠動態地選擇適配(第二次分派)visit,整個過程完成了兩次動態綁定。spa

單分派操作系統

  • 單分派語言處理一個操做是根據請求者的名稱和接受到的參數決定的,在Java中有靜態綁定和動態綁定之說,它的實現是依據重載和覆寫來實現的。

  • //一個演員和可扮演不少角色
      public interface Role{}
      public class GongFuRole implements Role{}
      public class IdiotRole implements Role{}
      //青年演員和老年演員
      public abstract class AbsActor{
      public void act(Role role){
         //演員能夠扮演全部角色
         System.out.println("演員能夠扮演全部角色");
      }
      public void act(GongFuRole role){
          System.out.println("年輕人最喜歡演功夫角色了");
      }}
      public class YoungActor extend AbsActor{
      
      public void act(GongFuRole role){        System.out.println("年輕人最喜歡演功夫角色了");
      }}
      public class OldActor extend AbsActor{
      public void act(GongFuRole role){
        System.out.println("老年人不能演功夫角色!!!!");
      }}
      //場景類
      AbsActor actor = new OldActor();
      Role role = new GongFuRole();
      actor.act(role);
      actor.act(new GongFuRole());
      //輸出
      演員能夠扮演全部角色
      老年人不能演功夫角色
      
    複製代碼
  •  重載就是在編譯器就決定了調用哪一個方法,它是根據 role 的表面類型決定調用act(Role role)方法,屬於靜態綁定,而Actor的執行方法 act 則是由其實際類型決定的,這是動態綁定。

雙分派

  • 獲得的執行操做,決定於請求的種類和兩個接收者的類型。對應於Router的類型和Visitor的類型。

3、使用場景

  • 一個對象結構包含不少類,它們有不一樣的接口,而你想對這些對象實施一些依賴於其具體類的操做。

  • 或者想對一個對象結構中的對象進行不少不一樣而且不相關的操做,而你想避免讓這些操做"污染"這些對象的類。

  • 或者你想針對對象不一樣,操做不一樣,攔截一些邏輯。

  • 須要將數據結構與不經常使用的操做進行分離的時候

4、優勢

  • 知足開閉原則,因爲訪問者模式沒有對原對象進行修改,只是新增了外部統一操做

  • 符合單一職責原則,Router 的兩個子類負責 數據的加載、傳輸, RouterVisitor 兩個子類負責具體的數據配置

  • 擴展性,若是路由器有改動,或者新增路由器的一些配置改動,徹底能夠就在Visitor的兩個子類的修改就能夠了

5、缺點

  • 具體元素對訪問者公佈細節,即TPLinkRouter 類須要對WindowsRouterVisitor 的具體實現瞭解,須要瞭解它的方法和數據,違反了迪米特法則。

  • 具體元素變動比較困難,即路由器新增一個還好辦,若是新增不少個呢?或者RouterVisitor類新增了屬性呢?改動很大的。

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息