「補課」進行時:設計模式系列java
訪問者模式也能夠說是全部設計模式中最難的一種設計模式了,固然咱們日常也不多會用到它。設計模式的做者是這麼評價訪問者模式的:大多狀況下,你並不須要使用訪問者模式,可是一旦須要使用它時,那就真的須要使用了。設計模式
又快到年末, CEO 和 CTO 開始評定員工一年的工做績效,員工分爲工程師和經理, CTO 關注工程師的代碼量、經理的新產品數量; CEO 關注的是工程師的KPI和經理的KPI以及新產品數量。數據結構
因爲 CEO 和 CTO 對於不一樣員工的關注點是不同的,這就須要對不一樣員工類型進行不一樣的處理。訪問者模式此時能夠派上用場了。dom
首先定義一個員工基類 Staff :ide
public abstract class Staff { public String name; // 員工KPI public int kpi; public Staff(String name) { this.name = name; kpi = new Random().nextInt(10); } // 核心方法,接受Visitor的訪問 abstract void accept(Visitor visitor); }
Staff
類定義了員工基本信息及一個 accept()
方法, accept()
方法表示接受訪問者的訪問,由子類具體實現。函數
而 Visitor
是個接口,傳入不一樣的實現類,可訪問不一樣的數據。this
下面是工程師和經理的具體實現類:設計
public class Engineer extends Staff { public Engineer(String name) { super(name); } @Override void accept(Visitor visitor) { visitor.visit(this); } // 工程師一年的代碼數量 public int getCodeLines() { return new Random().nextInt(10 * 10000); } } public class Manager extends Staff { public Manager(String name) { super(name); } @Override void accept(Visitor visitor) { visitor.visit(this); } // 一年作的產品數量 public int getProducts() { return new Random().nextInt(10); } }
工程師是代碼數量,經理是產品數量,他們的職責不同,也就是由於差別性,才使得訪問模式可以發揮它的做用。code
下面是 Visitor 接口的定義:orm
public interface Visitor { // 訪問工程師類型 void visit(Engineer engineer); // 訪問經理類型 void visit(Manager manager); }
Visitor 聲明瞭兩個 visit 方法,分別是對工程師和經理對訪問函數。
接下來定義兩個具體的訪問者: CEO 和 CTO 。
public class CEOVisitor implements Visitor { @Override public void visit(Engineer engineer) { System.out.println("工程師: " + engineer.name + ", KPI: " + engineer.kpi); } @Override public void visit(Manager manager) { System.out.println("經理: " + manager.name + ", KPI: " + manager.kpi + ", 新產品數量: " + manager.getProducts()); } } public class CTOVisitor implements Visitor { @Override public void visit(Engineer engineer) { System.out.println("工程師: " + engineer.name + ", 代碼行數: " + engineer.getCodeLines()); } @Override public void visit(Manager manager) { System.out.println("經理: " + manager.name + ", 產品數量: " + manager.getProducts()); } }
接着是一個報表類,公司的 CEO 和 CTO 經過這個報表查看全部員工的業績:
public class BusinessReport { private List<Staff> mStaffs = new LinkedList<>(); public BusinessReport() { mStaffs.add(new Manager("經理-A")); mStaffs.add(new Engineer("工程師-A")); mStaffs.add(new Engineer("工程師-B")); mStaffs.add(new Manager("經理-B")); mStaffs.add(new Engineer("工程師-C")); } /** * 爲訪問者展現報表 * @param visitor 公司高層,如 CEO、CTO */ public void showReport(Visitor visitor) { for (Staff staff : mStaffs) { staff.accept(visitor); } } }
最後是一個場景類:
public class Client { public static void main(String[] args) { // 構建報表 BusinessReport report = new BusinessReport(); System.out.println("=========== CEO看報表 ==========="); report.showReport(new CEOVisitor()); System.out.println("=========== CTO看報表 ==========="); report.showReport(new CTOVisitor()); } }
執行結果以下:
=========== CEO看報表 =========== 經理: 經理-A, KPI: 7, 新產品數量: 8 工程師: 工程師-A, KPI: 6 工程師: 工程師-B, KPI: 3 經理: 經理-B, KPI: 4, 新產品數量: 4 工程師: 工程師-C, KPI: 2 =========== CTO看報表 =========== 經理: 經理-A, 產品數量: 6 工程師: 工程師-A, 代碼行數: 61280 工程師: 工程師-B, 代碼行數: 10353 經理: 經理-B, 產品數量: 5 工程師: 工程師-C, 代碼行數: 65827
訪問者模式(Visitor Pattern) 是一個相對簡單的模式, 其定義以下:
Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates. (封裝一些做用於某種數據結構中的各元素的操做, 它能夠在不改變數據結構的前提下定義做用於這些元素的新的操做。 )
抽象元素:
public abstract class Element { // 定義業務邏輯 abstract void doSomething(); // 定義容許訪問角色 abstract void accept(IVisitor visitor); }
具體元素:
public class ConcreteElement1 extends Element{ @Override void doSomething() { } @Override void accept(IVisitor visitor) { visitor.visit(this); } } public class ConcreteElement2 extends Element{ @Override void doSomething() { } @Override void accept(IVisitor visitor) { visitor.visit(this); } }
抽象訪問者:
public interface IVisitor { void visit(ConcreteElement1 ele1); void visit(ConcreteElement2 ele2); }
具體訪問者:
public class Visitor implements IVisitor{ @Override public void visit(ConcreteElement1 ele1) { ele1.doSomething(); } @Override public void visit(ConcreteElement2 ele2) { ele2.doSomething(); } }
結構對象:
public class ObjectStruture { public static Element createElement() { Random random = new Random(); if (random.nextInt(100) > 50) { return new ConcreteElement1(); } else { return new ConcreteElement2(); } } }
場景類:
public class Client { public static void main(String[] args) { for (int i = 0; i < 10; i++) { Element e1 = ObjectStruture.createElement(); e1.accept(new Visitor()); } } }
各角色職責分離,符合單一職責原則。
經過UML類圖和上面的示例能夠看出來,Visitor、ConcreteVisitor、Element 、ObjectStructure,職責單一,各司其責。
具備優秀的擴展性。
若是須要增長新的訪問者,增長實現類 ConcreteVisitor 就能夠快速擴展。
使得數據結構和做用於結構上的操做解耦,使得操做集合能夠獨立變化。
員工屬性(數據結構)和CEO、CTO訪問者(數據操做)的解耦。
靈活性。
具體元素對訪問者公佈細節,違反了迪米特原則。
CEO、CTO須要調用具體員工的方法。
具體元素變動時致使修改爲本大。
變動員工屬性時,多個訪問者都要修改。
違反了依賴倒置原則,爲了達到「區別對待」而依賴了具體類,沒有用來抽象。
訪問者 visit 方法中,依賴了具體員工的具體方法。