目錄
- 第一章 訪問者模式介紹
- 第二章 訪問者模式實作
- 2.1、抽象訪問者類
- 2.2、具體訪問者類
- 2.3、抽象元素類
- 2.4、具體元素類
- 2.5、物件結構類
- 2.6、最終測驗類
- 第三章 訪問者模式應用
專案地址:https://gitee.com/caochenlei/design-pattern
第一章 訪問者模式介紹
訪問者模式的介紹:
在現實生活中,有些集合物件存在多種不同的元素,且每種元素也存在多種不同的訪問者和處理方式,例如,公園中存在多個景點,也存在多個游客,不同的游客對同一個景點的評價可能不同;醫院醫生開的處方單中包含多種藥元素,査看它的劃價員和藥房作業人員對它的處理方式也不同,劃價員根據處方單上面的藥品名和數量進行劃價,藥房作業人員根據處方單的內容進行抓藥,
訪問者模式(Visitor Pattern)主要將資料結構與資料操作分離,我們使用了一個訪問者類,它改變了元素類的執行演算法,通過這種方式,元素的執行演算法可以隨著訪問者改變而改變,這種型別的設計模式屬于行為型模式,根據模式,元素物件已接受訪問者物件,這樣訪問者物件就可以處理元素物件上的操作,
訪問者模式的優點:
- 擴展性好,能夠在不修改物件結構中的元素的情況下,為物件結構中的元素添加新的功能,
- 復用性好,可以通過訪問者來定義整個物件結構通用的功能,從而提高系統的復用程度,
- 靈活性好,訪問者模式將資料結構與作用于結構上的操作解耦,使得操作集合可相對自由地演化而不影響系統的資料結構,
- 符合單一職責原則,訪問者模式把相關的行為封裝在一起,構成一個訪問者,使每一個訪問者的功能都比較單一,
訪問者模式的缺點:
- 增加新的元素類很困難,每增加一個新的元素類,都要在每一個具體訪問者類中增加相應的具體操作,這違背了“開閉原則”,
- 破壞封裝,訪問者模式中具體元素對訪問者公布細節,這破壞了物件的封裝性,
- 違反了依賴倒置原則,訪問者模式依賴了具體類,而沒有依賴抽象類,
訪問者模式的場景:
- 物件結構相對穩定,但其操作演算法經常變化的程式,
- 物件結構中的物件需要提供多種不同且不相關的操作,而且要避免讓這些操作的變化影響物件的結構,
- 物件結構包含很多型別的物件,希望對這些物件實施一些依賴于其具體型別的操作,
訪問者模式的角色:
- 抽象訪問者(Visitor)角色:定義一個訪問具體元素的介面,為每個具體元素類對應一個訪問操作 visit() ,該操作中的引數型別標識了被訪問的具體元素,
- 具體訪問者(ConcreteVisitor)角色:實作抽象訪問者角色中宣告的各個訪問操作,確定訪問者訪問一個元素時該做什么,
- 抽象元素(Element)角色:宣告一個包含接受操作 accept() 的介面,被接受的訪問者物件作為 accept() 方法的引數,
- 具體元素(ConcreteElement)角色:實作抽象元素角色提供的 accept() 操作,其方法體通常都是 visitor.visit(this) ,另外具體元素中可能還包含本身業務邏輯的相關操作,
- 物件結構(Object Structure)角色:是一個包含元素角色的容器,提供讓訪問者物件遍歷容器中的所有元素的方法,通常由 List、Set、Map 等聚合類實作,

第二章 訪問者模式實作
2.1、抽象訪問者類
ComputerPartVisitor
public interface ComputerPartVisitor {
void visit(Mouse mouse);
void visit(Keyboard keyboard);
}
2.2、具體訪問者類
MIComputerPartVisitor
public class MIComputerPartVisitor implements ComputerPartVisitor {
@Override
public void visit(Mouse mouse) {
System.out.println("Displaying XiaoMi Mouse ...");
}
@Override
public void visit(Keyboard keyboard) {
System.out.println("Displaying XiaoMi Keyboard ...");
}
}
HPComputerPartVisitor
public class HPComputerPartVisitor implements ComputerPartVisitor {
@Override
public void visit(Mouse mouse) {
System.out.println("Displaying HuiPu Mouse ...");
}
@Override
public void visit(Keyboard keyboard) {
System.out.println("Displaying HuiPu Keyboard ...");
}
}
2.3、抽象元素類
ComputerPart
public interface ComputerPart {
void accept(ComputerPartVisitor computerPartVisitor);
}
2.4、具體元素類
Mouse
public class Mouse implements ComputerPart {
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
computerPartVisitor.visit(this);
}
}
Keyboard
public class Keyboard implements ComputerPart {
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
computerPartVisitor.visit(this);
}
}
2.5、物件結構類
ObjectStructure
public class ObjectStructure {
private List<ComputerPart> computerParts = new ArrayList<>();
public void attach(ComputerPart computerPart) {
computerParts.add(computerPart);
}
public void detach(ComputerPart computerPart) {
computerParts.remove(computerPart);
}
public void display(ComputerPartVisitor computerPartVisitor) {
for (ComputerPart computerPart : computerParts) {
computerPart.accept(computerPartVisitor);
}
}
}
2.6、最終測驗類
Client
public class Client {
public static void main(String[] args) {
ObjectStructure os = new ObjectStructure();
os.attach(new Mouse());
os.attach(new Keyboard());
os.display(new MIComputerPartVisitor());
os.display(new HPComputerPartVisitor());
}
}
Displaying XiaoMi Mouse ...
Displaying XiaoMi Keyboard ...
Displaying HuiPu Mouse ...
Displaying HuiPu Keyboard ...
第三章 訪問者模式應用
在早期的 Java 版本中,如果要對指定目錄下的檔案進行遍歷,必須用遞回的方式來實作,這種方法復雜且靈活性不高,
Java 7 版本后,Files 類提供了 walkFileTree() 方法,該方法可以很容易的對目錄下的所有檔案進行遍歷,需要 Path、FileVisitor 兩個引數,其中,Path 是要遍歷檔案的路徑,FileVisitor 則可以看成一個檔案訪問器,原始碼如下:
public final class Files {
//以上代碼部分省略...
public static Path walkFileTree(Path start, FileVisitor<? super Path> visitor)
throws IOException
{
return walkFileTree(start,
EnumSet.noneOf(FileVisitOption.class),
Integer.MAX_VALUE,
visitor);
}
//以下代碼部分省略...
}
FileVisitor 提供了遞回遍歷檔案樹的支持,這個介面的方法表示了遍歷程序中的關鍵程序,允許在檔案被訪問、目錄將被訪問、目錄已被訪問、發生錯誤等程序中進行控制,換句話說,這個介面在檔案被訪問前、訪問中和訪問后,以及產生錯誤的時候都有相應的鉤子程式進行處理,FileVisitor 主要提供了 4 個方法,且回傳結果的都是 FileVisitResult 物件值,用于決定當前操作完成后接下來該如何處理,FileVisitResult 是一個列舉類,代表回傳之后的一些后續操作,原始碼如下:
public interface FileVisitor<T> {
FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs) throws IOException;
FileVisitResult visitFile(T file, BasicFileAttributes attrs) throws IOException;
FileVisitResult visitFileFailed(T file, IOException exc) throws IOException;
FileVisitResult postVisitDirectory(T dir, IOException exc) throws IOException;
}
FileVisitResult 主要包含 4 個常見的操作:
- FileVisitResult.CONTINUE:這個訪問結果表示當前的遍歷程序將會繼續,
- FileVisitResult.SKIP_SIBLINGS:這個訪問結果表示當前的遍歷程序將會繼續,但是要忽略當前檔案/目錄的兄弟節點,
- FileVisitResult.SKIP_SUBTREE:這個訪問結果表示當前的遍歷程序將會繼續,但是要忽略當前目錄下的所有節點,
- FileVisitResult.TERMINATE:這個訪問結果表示當前的遍歷程序將會停止,
public enum FileVisitResult {
CONTINUE,
TERMINATE,
SKIP_SUBTREE,
SKIP_SIBLINGS;
}
通過訪問者去遍歷檔案樹會比較方便,比如查找檔案夾內符合某個條件的檔案或者某一天內所創建的檔案,這個類中都提供了相對應的方法,它的實作也非常簡單,代碼如下:
public class SimpleFileVisitor<T> implements FileVisitor<T> {
protected SimpleFileVisitor() {}
@Override
public FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs)
throws IOException
{
Objects.requireNonNull(dir);
Objects.requireNonNull(attrs);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(T file, BasicFileAttributes attrs)
throws IOException
{
Objects.requireNonNull(file);
Objects.requireNonNull(attrs);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(T file, IOException exc)
throws IOException
{
Objects.requireNonNull(file);
throw exc;
}
@Override
public FileVisitResult postVisitDirectory(T dir, IOException exc)
throws IOException
{
Objects.requireNonNull(dir);
if (exc != null)
throw exc;
return FileVisitResult.CONTINUE;
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/275145.html
標籤:java
上一篇:(原始碼級)一篇文章帶你深入了解Java字串(String、StringBuilder、StringBuffer的底層實作原理)
