訪問者模式最常用的使用場景是統計功能(報表)、數據過濾功能(攔截器)、或者結合別的模式來使用(狀態模式、代理模式等)
我們以統計功能舉例來說明訪問者模式的使用
核心是,分兩個模塊
一個模塊負責數據的產生
一個模塊負責遍歷數據,展示數據。
我們先看下在不使用訪問者模式的情況下的數據展示處理
//員工
public abstract class Employee
{
private int sex;
private String name;
private int age;
private int money;
public int getSex() {
return sex;
}
public int getAge() {
return age;
}
public int getMoney() {
return money;
}
public void setSex(int sex) {
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setMoney(int money) {
this.money = money;
}
public abstract void getOtherInfo(); //輸出別的一些內容
public abstract void accept(IVisitor visitore);
}
//碼農
public class CodeMan extends Employee
{
private String code; //用的是什麼類型的編程語言
public void setCode(String code) {
this.code = code;
}
@Override
public void getOtherInfo() {
//輸出 code
}
//碼農運行訪問者訪問
@Override
public void accept(IVisitor visitore) {
visitore.visit(this);
}
}
//美工妹紙
public class UIGirl extends Employee
{
private String design; //設計UI圖是用什麼工具
public void setDesign(String design) {
this.design = design;
}
@Override
public void getOtherInfo() {
//輸出 design
}
//美工妹紙運行訪問者訪問
@Override
public void accept(IVisitor visitore) {
visitore.visit(this);
}
}
//項目經理
public class Manager extends Employee
{
private String manage; //用的是什麼管理軟件
public void setManage(String manage) {
this.manage = manage;
}
@Override
public void getOtherInfo() {
//輸出 manage
}
//項目經理運行訪問者訪問
@Override
public void accept(IVisitor visitore) {
visitore.visit(this);
}
}
public interface IVisitor
{
public void visit(Employee employee);
}
public class EmployeeVisitor implements IVisitor
{
@Override
public void visit(Employee employee) {
report(employee);
}
public void report(Employee employee)
{
//獲取了 員工的所有屬性
employee.getAge();
employee.getMoney();
employee.getName();
employee.getOtherInfo();
employee.getSex();
//按照某種表格方式輸出
}
}
//客戶端類
public class Client
{
public void main()
{
ArrayList<Employee> employeeList = getEmployeeList();
for (Employee item :employeeList) {
item.accept(new EmployeeVisitor());
}
}
public ArrayList<Employee> getEmployeeList()
{
ArrayList<Employee> employees = new ArrayList<Employee>();
CodeMan zhangSan = new CodeMan();
zhangSan.setAge(25);
zhangSan.setSex(1);
zhangSan.setName("張三");
zhangSan.setMoney(5000);
zhangSan.setCode("java");
UIGirl lisi = new UIGirl();
lisi.setAge(23);
lisi.setSex(0);
lisi.setName("李四");
lisi.setMoney(5000);
lisi.setDesign("PS");
Manager wangWu = new Manager();
wangWu.setAge(30);
wangWu.setSex(1);
wangWu.setName("王五");
wangWu.setMoney(8000);
wangWu.setManage("youdao");
return employees;
}
}
這樣處理的時候是將數據模型和數據展示(report)都放在同一個模塊實現。
我們來看下訪問者模式是如何處理的。
//員工
public abstract class Employee
{
private int sex;
private String name;
private int age;
private int money;
public int getSex() {
return sex;
}
public int getAge() {
return age;
}
public int getMoney() {
return money;
}
public void setSex(int sex) {
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setMoney(int money) {
this.money = money;
}
public abstract void getOtherInfo(); //輸出別的一些內容
public abstract void accept(IVisitor visitore);
}
//碼農
public class CodeMan extends Employee
{
private String code; //用的是什麼類型的編程語言
public void setCode(String code) {
this.code = code;
}
@Override
public void getOtherInfo() {
//輸出 code
}
//碼農運行訪問者訪問
@Override
public void accept(IVisitor visitore) {
visitore.visit(this);
}
}
//美工妹紙
public class UIGirl extends Employee
{
private String design; //設計UI圖是用什麼工具
public void setDesign(String design) {
this.design = design;
}
@Override
public void getOtherInfo() {
//輸出 design
}
//美工妹紙運行訪問者訪問
@Override
public void accept(IVisitor visitore) {
visitore.visit(this);
}
}
//項目經理
public class Manager extends Employee
{
private String manage; //用的是什麼管理軟件
public void setManage(String manage) {
this.manage = manage;
}
@Override
public void getOtherInfo() {
//輸出 manage
}
//項目經理運行訪問者訪問
@Override
public void accept(IVisitor visitore) {
visitore.visit(this);
}
}
public interface IVisitor
{
public void visit(Employee employee);
}
public class EmployeeVisitor implements IVisitor
{
@Override
public void visit(Employee employee) {
report(employee);
}
public void report(Employee employee)
{
//獲取了 員工的所有屬性
employee.getAge();
employee.getMoney();
employee.getName();
employee.getOtherInfo();
employee.getSex();
//按照某種表格方式輸出
}
}
//客戶端類
public class Client
{
public void main()
{
ArrayList<Employee> employeeList = getEmployeeList();
for (Employee item :employeeList) {
item.accept(new EmployeeVisitor());
}
}
public ArrayList<Employee> getEmployeeList()
{
ArrayList<Employee> employees = new ArrayList<Employee>();
CodeMan zhangSan = new CodeMan();
zhangSan.setAge(25);
zhangSan.setSex(1);
zhangSan.setName("張三");
zhangSan.setMoney(5000);
zhangSan.setCode("java");
UIGirl lisi = new UIGirl();
lisi.setAge(23);
lisi.setSex(0);
lisi.setName("李四");
lisi.setMoney(5000);
lisi.setDesign("PS");
Manager wangWu = new Manager();
wangWu.setAge(30);
wangWu.setSex(1);
wangWu.setName("王五");
wangWu.setMoney(8000);
wangWu.setManage("youdao");
return employees;
}
}
執行流程:
首先通過循環遍歷所有元素;
其次,每個員工對象都定義了一個訪問者;
再其次,員工對象把自己做爲一個參數調用訪問者 visit 方法;
然後,訪問者調用自己內部的計算邏輯,計算出相應的數據和表格元素;
最後,訪問者打印出報表和數據;
訪問者模式將數據展示獨立出來,這樣做使得責任單一
這後半段的文字描述轉載自:http://blog.csdn.net/zhengzhb/article/details/7489639
訪問者模式的優點
符合單一職責原則:
凡是適用訪問者模式的場景中,元素類中需要封裝在訪問者中的操作必定是與元素類本身關係不大且是易變的操作,使用訪問者模式一方面符合單一職責原則,另一方面,因爲被封裝的操作通常來說都是易變的,所以當發生變化時,就可以在不改變元素類本身的前提下,實現對變化部分的擴展。
擴展性良好:
元素類可以通過接受不同的訪問者來實現對不同操作的擴展。
訪問者模式的適用場景假如一個對象中存在着一些與本對象不相干(或者關係較弱)的操作,爲了避免這些操作污染這個對象,則可以使用訪問者模式來把這些操作封裝到訪問者中去。假如一組對象中,存在着相似的操作,爲了避免出現大量重複的代碼,也可以將這些重複的操作封裝到訪問者中去。
但是,訪問者模式並不是那麼完美,它也有着致命的缺陷:增加新的元素類比較困難。通過訪問者模式的代碼可以看到,在訪問者類中,每一個元素類都有它對應的處理方法,也就是說,每增加一個元素類都需要修改訪問者類(也包括訪問者類的子類或者實現類),修改起來相當麻煩。也就是說,在元素類數目不確定的情況下,應該慎用訪問者模式。所以,訪問者模式比較適用於對已有功能的重構,比如說,一個項目的基本功能已經確定下來,元素類的數據已經基本確定下來不會變了,會變的只是這些元素內的相關操作,這時候,我們可以使用訪問者模式對原有的代碼進行重構一遍,這樣一來,就可以在不修改各個元素類的情況下,對原有功能進行修改。
總結
正如《設計模式》的作者GoF對訪問者模式的描述:大多數情況下,你並需要使用訪問者模式,但是當你一旦需要使用它時,那你就是真的需要它了。當然這只是針對真正的大牛而言。在現實情況下(至少是我所處的環境當中),很多人往往沉迷於設計模式,他們使用一種設計模式時,從來不去認真考慮所使用的模式是否適合這種場景,而往往只是想展示一下自己對面向對象設計的駕馭能力。編程時有這種心理,往往會發生濫用設計模式的情況。所以,在學習設計模式時,一定要理解模式的適用性。必須做到使用一種模式是因爲了解它的優點,不使用一種模式是因爲了解它的弊端;而不是使用一種模式是因爲不瞭解它的弊端,不使用一種模式是因爲不瞭解它的優點。