訪問者模式是一種從操做的對象結構中分離算法的方式。 它能夠在不改變數據結構的前提下定義做用與這些元素的新操做。它遵循開閉原則。html
> Represent an operation to be performed on elements of an object
> structure. Visitor lets you define a new operation without changing
> the classes of the elements on which it operatesjava
.visitor: n. 訪問者,參觀者;視察者.算法
1.Visitor 抽象訪問者角色,爲該對象結構中具體元素角色聲明一個訪問操做接口。該操做接口的名字和參數標識了發送訪問請求給具體訪問者的具體元素角色,這樣訪問者就能夠經過該元素角色的特定接口直接訪問它。設計模式
2.ConcreteVisitor.具體訪問者角色,實現Visitor聲明的接口。
3.Element 定義一個接受訪問操做(accept()),它以一個訪問者(Visitor)做爲參數。數據結構
4.ConcreteElement具體元素,實現了抽象元素(Element)所定義的接受操做接口。
5.ObjectStructure結構對象角色,這是使用訪問者模式必備的角色。它具有如下特性:能枚舉它的元素;能夠提供一個高層接口以容許訪問者訪問它的元素;若有須要,能夠設計成一個複合對象或者一個彙集(如一個列表或無序集合)ide
1.我做爲一個訪客(Visitor)到朋友家(Element)拜訪,朋友之間喝喝酒,聊聊天,再互相吹捧。聊天的時候,朋友告訴我他今年的表現(doSomthing),而後我就作(visit-self-method)一些對這件事的評價。學習
2.老闆做爲視察者,查閱(訪問)手下員工的工做業績。老闆是Visitor的抽象實現,員工是Element的抽象實現。對象結構(Object Structure)爲員工的業績等信息this
3.家裏有一臺電腦,電腦出現了一點問題,那麼我做爲訪問者,想去了解電腦的那個部分出了問題。我Visitor,電腦的各個部分(Element),查看有沒有壞(visit method)
應該有不少相似的比喻,在開發的過程當中多去思考,作什麼事情都要思考。spa
1.定義一個表示Element的接口.net
2.實現Element接口。建立Element的實體類ConcreteElement
3.建立一個表示訪問者Visitor的接口實現
4.Visitor的接口,建立Visitor實體類ConcreteVisitor,(有時候會有多個訪問者)
5.使用Visitor實體類來訪問Element。
優勢:1.符合單一職責原則
2.元素類能夠經過接受不一樣的訪問者來實現對不一樣操做的擴展。
缺點:1.具體元素對訪問者公佈細節,違背了迪米特法則。
2.違背了依賴倒置原則,訪問者依賴的是具體元素,而不是抽象元素。
適用場景
1.對象結構中對象對應的類不多改變,但常常須要在此對象結構上定義新的操做。
2.須要對一個對象結構中的對象進行不少不一樣的而且不相關的操做,而須要避免讓這些操做"污染"這些對象的類,也不但願在增長新操做時修改這些類。
注意事項:訪問者能夠對功能進行統一,能夠作報表、UI、攔截器與過濾器。
案例一
類圖
咱們去檢查汽車的各個部分是否能正常打印,使用Visitor根據不一樣的汽車部分來分發動做。 而不是在汽車的各個部分來打印。interface
CarElement {
void accept(CarElementVisitor visitor);
}
interface CarElementVisitor {
void visit(Body body);
void visit(Car car);
void visit(Engine engine);
void visit(Wheel wheel);
}class Car implements CarElement {
CarElement[] elements;public Car() {
this.elements = new CarElement[] {
new Wheel("front left"), new Wheel("front right"),
new Wheel("back left"), new Wheel("back right"),
new Body(), new Engine()
};
}public void accept(final CarElementVisitor visitor) {
for (CarElement elem : elements) {
elem.accept(visitor);
}
visitor.visit(this);
}
}class Body implements CarElement {
public void accept(final CarElementVisitor visitor) {
visitor.visit(this);
}
}
class Engine implements CarElement {
public void accept(final CarElementVisitor visitor) {
visitor.visit(this);
}
}
class Wheel implements CarElement {
private String name;public Wheel(final String name) {
this.name = name;
}public String getName() {
return name;
}public void accept(final CarElementVisitor visitor) {
/*
* accept(CarElementVisitor) in Wheel implements
* accept(CarElementVisitor) in CarElement, so the call
* to accept is bound at run time. This can be considered
* the *first* dispatch. However, the decision to call
* visit(Wheel) (as opposed to visit(Engine) etc.) can be
* made during compile time since 'this' is known at compile
* time to be a Wheel. Moreover, each implementation of
* CarElementVisitor implements the visit(Wheel), which is
* another decision that is made at run time. This can be
* considered the *second* dispatch.
*/
visitor.visit(this);
}
}class CarElementDoVisitor implements CarElementVisitor {
public void visit(final Body body) {
System.out.println("Moving my body");
}
public void visit(final Car car) {
System.out.println("Starting my car");
}public void visit(final Wheel wheel) {
System.out.println("Kicking my " + wheel.getName() + " wheel");
}
public void visit(final Engine engine) {
System.out.println("Starting my engine");
}
}class CarElementPrintVisitor implements CarElementVisitor {
public void visit(final Body body) {
System.out.println("Visiting body");
}public void visit(final Car car) {
System.out.println("Visiting car");
}
public void visit(final Engine engine) {
System.out.println("Visiting engine");
}public void visit(final Wheel wheel) { System.out.println("Visiting " + wheel.getName() + " wheel");
}
}public class VisitorDemo {
public static void main(final String[] args) {
final Car car = new Car();
car.accept(new CarElementPrintVisitor());
car.accept(new CarElementDoVisitor());
}
}/* 輸出內容
Visiting front left wheel
Visiting front right wheel
Visiting back left wheel
Visiting back right wheel
Visiting bodyVisiting engine
Visiting carKicking my front left wheel
Kicking my front right wheel
Kicking my back left whee
lKicking my back right wheel
Moving my body
Starting my engine
Starting my car
*/
案例二
類圖
本質上和案例一沒什麼差異
// ComputerPart.java
public interface ComputerPart {
public void accept(ComputerPartVisitor computerPartVisitor);
}
// Keyboard.java
public class Keyboard implements ComputerPart {
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
computerPartVisitor.visit(this);
}
}
// Monitor.java
public class Monitor implements ComputerPart {
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
computerPartVisitor.visit(this);
}
}
// Mouse.java
public class Mouse implements ComputerPart {
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
computerPartVisitor.visit(this);
}
}
// Computer.java
public class Computer implementsComputerPart {
ComputerPart[] parts;
public Computer(){
parts = new ComputerPart[] {new Mouse(), new Keyboard(), new Monitor()};
}
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
for (int i = 0; i < parts.length; i++) {
parts[i].accept(computerPartVisitor);
}
computerPartVisitor.visit(this);
}
}
// ComputerPartVisitor
public interface ComputerPartVisitor {
public void visit(Computer computer);
public void visit(Mouse mouse);
public void visit(Keyboard keyboard);
public void visit(Monitor monitor);
}
// ComputerPartDisplayVisitor.java
public class ComputerPartDisplayVisitor implements ComputerPartVisitor {
@Override
public void visit(Computer computer) {
System.out.println("Displaying Computer.");
}
@Override
public void visit(Mouse mouse) {
System.out.println("Displaying Mouse.");
}
@Override
public void visit(Keyboard keyboard) {
System.out.println("Displaying Keyboard.");
}
@Override
public void visit(Monitor monitor) {
System.out.println("Displaying Monitor.");
}
}
// demo
public class VisitorPatternDemo {
public static void main(String[] args) {
ComputerPart computer = new Computer();
computer.accept(newComputerPartDisplayVisitor());
}
}/* 輸出
Displaying Mouse.
Displaying Keyboard.
Displaying Monitor.
Displaying Computer.
*/
主要記錄了在學習設計模式時的一些資料,對資料進行了整理。想要深刻理解設計模式,還要多讀優秀的代碼,在開發的時候多去思考相關的應用場景。 附錄 訪問者模式—百度百科:https://0x9.me/HXKTA(短網址) 訪問者模式—維基百科:https://en.wikipedia.org/wiki/Visitor_pattern 訪問者模式—菜鳥教程:http://www.runoob.com/design-pattern/visitor-pattern.html