一、模式介紹
訪問者模式通常包含如下幾個角色:
- 抽象元素,定義元素接受訪問者訪問的方法;
- 具體元素,提供接受訪問者訪問的具體實現(xiàn),以及自身的一些操作;
- 抽象訪問者,定義多個visit方法用來訪問每一個具體元素,理論上visit方法個數(shù)等于具體元素個數(shù);因此要求該模式使用場景是元素穩(wěn)定不變的。
- 具體訪問者,實現(xiàn)對具體元素操作的訪問;
- 結構對象,維護具體元素的集合,提供方法接受具體訪問者對集合中的所有元素進行訪問;
通用的實現(xiàn)代碼如下:
/**
* 抽象元素
* 定義接受訪問者訪問的方法
* 所有具體元素都支持被訪問者訪問
*/
public interface IElement {
void accept(IVisitor visitor);
}
/**
* 具體元素A
*/
@Slf4j
public class ConcreteElementA implements IElement{
@Override
public void accept(IVisitor visitor) {
// 訪問者進行訪問
visitor.visit(this);
}
public void operation(){
log.info("ConcreteElementA operation");
}
}
/**
* 具體元素B
*/
@Slf4j
public class ConcreteElementB implements IElement{
@Override
public void accept(IVisitor visitor) {
// 訪問者進行訪問
visitor.visit(this);
}
public void operation(){
log.info("ConcreteElementB operation");
}
}
/**
* 抽象訪問者
*/
public interface IVisitor {
/**
* 定義visit方法訪問每一個具體的元素
* 理論上visit方法個數(shù)和具體元素的個數(shù)是相等的
* @param element
*/
void visit(ConcreteElementA element);
void visit(ConcreteElementB element);
}
/**
* 具體訪問者,實現(xiàn)對具體元素的訪問操作
*/
public class ConcreteVisitor implements IVisitor{
@Override
public void visit(ConcreteElementA element) {
element.operation();
}
@Override
public void visit(ConcreteElementB element) {
element.operation();
}
}
public class ObjectStructure {
private List<IElement> list = new ArrayList<>();
/**
* 需要被訪問的具體元素初始化時都裝入集合中
*/
{
this.list.add(new ConcreteElementA());
this.list.add(new ConcreteElementB());
}
/**
* 接待某個具體的訪問者
* @param visitor
*/
public void accept(IVisitor visitor){
// 帶訪問者依次參觀具體元素
for (IElement element : this.list) {
element.accept(visitor);
}
}
}
@Slf4j
public class Main {
public static void main(String[] args) {
ObjectStructure collection = new ObjectStructure();
IVisitor jack = new ConcreteVisitor();
// 接待jack這個訪問者
collection.accept(jack);
}
}
我們將如上的案例來做一個實際場景的類比:
-
ObjectStructure相當于一個家族的管家,具體元素類相當于每個房間里面的主人,家族里面主人的個數(shù)肯定是固定的,然后訪問者來家族進行訪問的時候,由管家進行接待accept(visitor); - 然后管家?guī)ьI訪問者依次去每個房間拜訪主人
element.accept(visitor); - 每個主人需要同意
accept(visitor)訪問者訪問后,訪問者才可以進行對當前主人的訪問visitor.visit(this); - 然后這個主人才和訪問者進行交流,展示自己
operation;
在這樣的設計模式中,任何訪問者想來拜訪,只要讓管家去接待一下即可,每個房間的主人們則完全不用操心,只需要固定地接受拜訪并展示自己即可,對訪問者的擴展十分方便。與此同時,倘若每個房間的主人有多項技能的話(唱歌、跳舞、吟詩、作畫......),訪問者可以自己定義想拜訪主人都需要為自己展示什么才能,只要過了管家這一關即可,因為必須要管家給安排。
二、使用場景
- JDK的NIO中的FileVisitor接口;
- Spring中的BeanDefinitionVisitor類;
三、模式總結
3.1 優(yōu)點
- 解耦了數(shù)據結構和數(shù)據操作;
- 可以很方便地擴展訪問者角色,實現(xiàn)對不同數(shù)據集的不同操作,擴展性良好;
- 元素具體類型可以是多樣的,訪問者均可操作;
- 各個角色職責分離,符合單一職責原則;
3.2 缺點
- 無法靈活增加元素類型,否則就得頻繁改動訪問者,違反了開閉原則;
- 元素行為增刪困難,需要改動訪問者的調用行為,違反了開閉原則;
- 訪問者直接耦合了具體元素類,而沒有以來抽象,違背了依賴倒置原則;