說明:本文介紹結構型設計模式之一的組合模式
定義
組合模式(Composite Pattern)又叫作整體-部分(Part-Whole)模式,它的宗旨是通過將單個對象(葉子節點)和組合對象(樹枝節點)用相同的接口進行表示,使得客戶對單個對象和組合對象的使用具有一致性,屬于結構型設計模式。(引自《設計模式就該這樣學》P263)
文件系統
以文件系統為例,如下,是服務器上某個文件夾的文件結構,該文件夾下既有文件夾,也有文件
如果我要構建這樣一個文件夾-文件系統,代碼應該是這樣寫的,如下:
(圖片文件,ImageFile)
/*** 圖片文件*/
public class ImageFile {/*** 圖片名稱*/private String name;public ImageFile(String name) {this.name = name;}/*** 展示*/public void show(int space) {for (int i = 0; i < space; i++) {System.out.print(" ");}System.out.println(name);}
}
(電影文件,MovieFile)
/*** 電影文件*/
public class MovieFile {/*** 電影名稱*/private String name;public MovieFile(String name) {this.name = name;}/*** 展示*/public void show(int space) {for (int i = 0; i < space; i++) {System.out.print(" ");}System.out.println(name);}
}
(文件夾,Folder,定義多個文件集合,并開放對應的增加方法)
import java.util.ArrayList;/*** 文件夾*/
public class Folder {/*** 文件夾名稱*/private String name;/*** 文件夾下的文件夾*/private ArrayList<Folder> folders = new ArrayList<>();/*** 文件夾下的圖片文件*/private ArrayList<ImageFile> imageFiles = new ArrayList<>();/*** 文件夾下的視頻文件*/private ArrayList<MovieFile> movieFiles = new ArrayList<>();public Folder(String name) {this.name = name;}/*** 添加文件夾*/public void addFolder(Folder folder) {folders.add(folder);}/*** 添加圖片文件*/public void addImageFile(ImageFile imageFile) {imageFiles.add(imageFile);}/*** 添加電影文件*/public void addMoveFile(MovieFile movieFile) {movieFiles.add(movieFile);}/*** 展示*/public void show(int space) {// 打印文件夾space++;for (int i = 0; i < space; i++) {System.out.print(" ");}System.out.println(name);for (Folder folder : folders) {folder.show(space);}// 打印圖片文件space++;for (ImageFile imageFile : imageFiles) {imageFile.show(space);}// 打印電影文件space++;for (MovieFile movieFile : movieFiles) {movieFile.show(space);}}
}
(客戶端使用,Client)
public class Client {public static void main(String[] args) {// 頂級文件夾Folder folder = new Folder("folder");// 二級文件夾Folder images = new Folder("images");Folder movies = new Folder("movies");// 三級目錄下的文件ImageFile boy = new ImageFile("boy.png");ImageFile girl = new ImageFile("girl.png");images.addImageFile(boy);images.addImageFile(girl);// 三級文件夾Folder director1 = new Folder("heizeming");Folder director2 = new Folder("xiaolinzhengshu");// 四級目錄MovieFile movieFile1 = new MovieFile("luoshengmen.mp4");MovieFile movieFile2 = new MovieFile("qiwushi.mp4");director1.addMoveFile(movieFile1);director1.addMoveFile(movieFile2);MovieFile movieFile3 = new MovieFile("duomingjian.mp4");MovieFile movieFile4 = new MovieFile("qiefu.mp4");director2.addMoveFile(movieFile3);director2.addMoveFile(movieFile4);movies.addFolder(director1);movies.addFolder(director2);// 文件夾添加到頂級文件夾folder.addFolder(images);folder.addFolder(movies);folder.show(0);}
}
運行,hora!(看!),能實現目的
但這里存在問題,文件系統,簡單來說只有文件夾和文件兩個實體對象,代碼中的圖片文件、電影文件可以抽象為文件(File),這是整體與個體的場景。
組合模式
使用組合模式改進上述代碼,如下:
(抽象節點類,Node)
/*** 抽象節點*/
public abstract class Node {/*** 節點名稱*/protected String name;public Node(String name) {this.name = name;}/*** 添加節點*/protected abstract void add(Node node);/*** 展示*/protected void show(int space) {for (int i = 0; i < space; i++) {System.out.print(" ");}System.out.println(name);}/*** 重載方法,使用的時候就不用給參數了*/protected void show() {show(0);}
}
(文件夾,Folder)
import java.util.ArrayList;/*** 文件夾*/
public class Folder extends Node {/*** 文件夾下的子節點*/private ArrayList<Node> childrenNodes = new ArrayList<>();/*** 調用父類的構造方法*/public Folder(String name) {super(name);}@Overrideprotected void add(Node node) {childrenNodes.add(node);}@Overrideprotected void show(int space) {super.show(space);space++;for (Node node : childrenNodes) {node.show(space);}}
}
(文件,File)
/*** 文件*/
public class File extends Node {/*** 調用父類的構造方法*/public File(String name) {super(name);}@Overrideprotected void add(Node node) {System.out.println("不能添加子節點");}@Overrideprotected void show(int space) {super.show(space);}
}
(客戶端使用,Client)
public class Client {public static void main(String[] args) {// 頂級文件夾Folder folder = new Folder("folder");// 二級文件夾Folder images = new Folder("images");Folder movies = new Folder("movies");// 三級目錄下的文件File boy = new File("boy.png");File girl = new File("girl.png");images.add(boy);images.add(girl);// 三級文件夾Folder director1 = new Folder("heizeming");Folder director2 = new Folder("xiaolinzhengshu");// 四級目錄File movieFile1 = new File("luoshengmen.mp4");File movieFile2 = new File("qiwushi.mp4");director1.add(movieFile1);director1.add(movieFile2);File movieFile3 = new File("duomingjian.mp4");File movieFile4 = new File("qiefu.mp4");director2.add(movieFile3);director2.add(movieFile4);movies.add(director1);movies.add(director2);// 文件夾添加到頂級文件夾folder.add(images);folder.add(movies);folder.show();}
}
執行如下,也實現了目的
這么看下來,文件系統場景使用組合模式實現是很不錯的,代碼少了很多,也削減了文件夾類中的職責(可以對比下Folder類前后的代碼)
使用場景
在《設計模式就該這樣學》(P229)這本書中,提到狀態模式適用于以下場景:
(1)希望客戶端可以忽略組合對象與單個對象的差異;
(2)對象層次具備整體和部分,呈樹形結構;
除了文件系統、企業組織架構場景,我還沒想到其他使用場景;
總結
本文介紹了結構型設計模式中的組合模式,參考《設計模式就該這樣學》、《秒懂設計模式》、《設計模式的藝術》(第一版)這三本書,其中的例子來自《秒懂設計模式》。