設計模式之行為型模式-訪問者模式
訪問者模式(Visitor Pattern)
一、 介紹
模式定義:封裝一些作用於某種資料結構中的各元素的操作,它可以在不改變資料結構的前提下定義作用於這些元素的新的操作。
意圖:主要將資料結構與資料操作分離。
主要解決:穩定的資料結構和易變的操作耦合問題。(被處理的資料元素相對穩定而訪問方式多種多樣)
何時使用:需要對一個物件結構中的物件進行很多不同的並且不相關的操作,而需要避免讓這些操作"汙染"這些物件的類,使用訪問者模式將這些封裝到類中。
如何解決:在被訪問的類裡面加一個對外提供接待訪問者的介面。
關鍵程式碼:在資料基礎類裡面有一個方法接受訪問者,將自身引用傳入訪問者。
應用例項:JDK 對於檔案樹的遍歷(不變:檔案樹的遍歷,變:檔案的具體操作 如 列印檔名、計算檔案數),ASM 修改位元組碼
優點: 1、符合單一職責原則。(訪問者模式把相關的行為封裝在一起,構成一個訪問者,使每一個訪問者的功能都比較單一。)
2、更強的擴充套件性。(能夠在不修改物件結構中的元素的情況下,為物件結構中的元素新增新的功能。)
3、可維護性(複用性好)。(可以通過訪問者來定義整個物件結構通用的功能,從而提高系統的複用程度。)
4、靈活性。(訪問者模式將資料結構與用作於結構上的操作解耦,使得操作集合可相對自由地演化而不影響系統的資料結構。)
缺點: 1、具體元素對訪問者公佈細節,違反了迪米特原則。 2、具體元素(被訪問者的)變更比較困難。 3、違反了依賴倒置原則,依賴了具體類,沒有依賴抽象。4、類結構變得複雜。(不是簡單的呼叫關係,而是多個類之間的繼承和組合關係)
使用場景: 1、物件結構中物件對應的類很少改變,但經常需要在此物件結構上定義新的操作。 2、需要對一個物件結構中的物件進行很多不同的並且不相關的操作,而需要避免讓這些操作"汙染"這些物件的類,也不希望在增加新操作時修改這些類。(不想直接被改變但又想擴充套件功能)
注意事項:訪問者可以對功能進行統一,可以做報表、UI、攔截器與過濾器。
二、詳細
舉例:您在朋友家做客,您是訪問者,朋友接受您的訪問,您通過朋友的描述,然後對朋友的描述做出一個判斷,這就是訪問者模式。
設計模式的本質是找出不變的東西,再找出變化的東西,然後找到合適的資料結構(設計模式)去承載這種變化。
訪問者模式,重點在於訪問二字,從字面上的意思理解:其實就相當於被訪問者(如某個公眾人物)把訪問者(如 記者)當成了外人,不想你隨便動。你想要什麼,我弄好之後給你(呼叫你的方法)。訪問者模式其實就是要把不變的東西固定起來,變化的開放出去
三、結構
訪問者(Visitor) 模式實現的關鍵是如何將作用於元素的操作分離出來封裝成獨立的類,其主要角色如下:
- 抽象訪問者(Visitor)角色:定義一個訪問具體元素的介面,為每個具體元素類對應一個訪問操作visit(),該操作中的引數型別表示了被訪問的具體元素。
- 具體訪問者(Concrete Visitor)角色:實現抽象訪問者角色中宣告的各個訪問操作,確定訪問者訪問一個元素時該做什麼。
- 抽象元素(Element)角色:宣告一個包含接受操作accept()操作,其方法體通常都是visitor.visit(this),另外具體元素中可能還包含業務本身邏輯的相關操作。
- 具體元素(Concrete Element)角色:實現抽象元素角色提供的accept()操作,其方法體通常都是visitor.visit(this),另外具體元素中可能還包含本身業務邏輯的相關操作。
-
物件結構(Object Structure)角色:是一個包含元素角色的容器,提供讓訪問者物件遍歷容器中的所有元素的方法,通常由List、Set、Map等聚合類實現。
結構圖如下:
簡單樣例1(訪問者類只有一個方法)
/**
* 抽象訪問者
*/
interface Visitor{
void visit(ConcreteElementA element);
void visit(ConcreteElementB element);
}
/**
* 具體訪問者A類
*/
class ConcreteVisitorA implements Visitor{
@Override
public void visit(ConcreteElementA element) {
System.out.println("具體訪問者A訪問->" + element.operationA());
}
@Override
public void visit(ConcreteElementB element) {
System.out.println("具體訪問者A訪問->" + element.operationB());
}
}
/**
* 具體訪問者B類
*/
class ConcreteVisitorB implements Visitor{
@Override
public void visit(ConcreteElementA element) {
System.out.println("具體訪問者B訪問->" + element.operationA());
}
@Override
public void visit(ConcreteElementB element) {
System.out.println("具體訪問者B訪問->" + element.operationB());
}
}
/**
* 抽象元素類
*/
interface Element{
void accept(Visitor visitor);
}
/**
* 具體元素A類
*/
class ConcreteElementA implements Element{
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public String operationA(){
return "具體元素A的操作";
}
}
/**
* 具體元素B類
*/
class ConcreteElementB implements Element{
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public String operationB(){
return "具體元素B的操作";
}
}
/**
* 物件結構角色
*/
class ObjectStructure{
private List<Element> list = new ArrayList<Element>();//泛型
public void accept(Visitor visitor){
Iterator<Element> i = list.iterator();
while(i.hasNext()){
i.next().accept(visitor);
}
}
public void add(Element element){//增加
list.add(element);
}
public void remove(Element element){//刪除
list.remove(element);
}
}
public class VisitorPatternSimpleTest {
public static void main(String[] args){
ObjectStructure objectStructure = new ObjectStructure();
objectStructure.add(new ConcreteElementA());
objectStructure.add(new ConcreteElementB());
Visitor visitorA = new ConcreteVisitorA();
objectStructure.accept(visitorA);
System.out.println("------------------------------");
Visitor visitorB = new ConcreteVisitorB();
objectStructure.accept(visitorB);
}
}
執行:
三、案例
案例1:列印集合
PrintAggregate
public class PrintAggregate {
public static void main(String[] args) {
int[] x={2,3,4,5,10,9,8};
Collection collection = new ArrayList();
for(int i=0;i < x.length;i++){
collection.add(x[i]);
}
print(collection);
}
public static void print(Collection collection) {
Iterator it= collectlon.iterator ();
while(it.hasNext()){
System.out.println(it.next().toString());
}
}
}
執行:
PrintAggregate2
public class PrintAggregate2 {
public static void main(String[] args) {
String[] a="Tianjin is one of the most beautiful city in China".aplit(" ");
/* Arrays.asList方法生成的1ist是List是使用的是類內部的構建ArrayList類,而不是我們常用的Java.util.ArrayList
它的內部類根本就沒有寫List的add方法,而是呼叫的java.util.AbstractList.add的add方法
而AbstractList的add方法會直接報出UnsupportedOperationException異常 */
Collection co = new ArrayList();
System.out.println(co.getClass());
Collection co2 = new ArrayList();
co2.add("1");
co2.add("2");
co.addAll(co2);
print(co);
}
public static void print(Collection collection) {
Iterator it= collectlon.iterator ();
while(it.hasNext()){
Object o=it.next();
if(o instanceof Collection){
print((Collection)o);
}else{
System.out.println(o.toString());
}
}
}
}
PrintAggregate3(強耦合,不推薦)
public class PrintAggregate2 {
public static void main(String[] args) {
String[] a="Tianjin is one of the most beautiful city in China".aplit(" ");
Collection co = new ArrayList();
Collection co2 = new ArrayList();
co2.add("1");
co2.add("2");
co2.add(3);
co2.add(4.0);
co2.add(5.6f);
co.addAll(co2);
print(co);
}
public static void print(Collection collection) {
Iterator it= collectlon.iterator ();
while(it.hasNext()){
Object o=it.next();
if(o instanceof Collection){
print((Collection)o);
}else if(o instanceof String){
System.out.println(" "+o.toString()+" ");
}else if(o instanceof Double){
System.out.println(o.toString()+"D");
}else if(o instanceof Float){
System.out.println(o.toString()+"f");
}else{
System.out.println(o.toString());
}