什么是組合模式?
組合模式允許你將對象組合成樹形結構來表示"部分-整體"的層次結構。它讓客戶端能夠以統一的方式處理單個對象和對象組合。
簡單來說,就像公司的組織結構:
- 公司有部門
- 部門有小組
- 小組有員工
- 但無論是對公司、部門還是員工,都可以統一執行"工作"操作
主要解決什么問題?
組合模式主要解決處理樹形結構數據時的問題:
- 客戶端需要區分簡單元素(葉子節點)和復雜元素(容器節點)
- 處理容器節點時需要遞歸處理其子節點
- 希望用統一接口處理所有節點,無論它是簡單還是復雜
何時使用組合模式?
當你發現以下場景時,考慮使用組合模式:
- 需要表示對象的部分-整體層次結構
- 希望用戶忽略組合對象與單個對象的不同
- 結構可以形成任意深度的樹形嵌套
- 需要對整個樹形結構執行統一操作(如渲染、計算等)
組合模式的優點
- 簡化客戶端代碼:客戶端可以一致地處理單個對象和組合對象
- 開閉原則:容易新增組件類型,無需修改現有代碼
- 靈活的結構:可以構建任意復雜的樹形結構
- 統一操作:對整個結構執行操作變得簡單
組合模式的缺點
- 過度一般化:有時很難為所有組件定義通用接口
- 類型檢查問題:運行時可能需要檢查對象類型
- 設計復雜:需要仔細設計組件接口,可能變得過于抽象
代碼示例:文件系統
讓我們用文件系統的例子來演示組合模式:
import java.util.ArrayList;
import java.util.List;// 組件抽象類(可以是接口)
abstract class FileSystemComponent {protected String name;public FileSystemComponent(String name) {this.name = name;}public abstract void display(int depth);public abstract long getSize();// 默認實現(葉子節點不需要實現)public void add(FileSystemComponent component) {throw new UnsupportedOperationException();}public void remove(FileSystemComponent component) {throw new UnsupportedOperationException();}
}// 葉子節點:文件
class File extends FileSystemComponent {private long size;public File(String name, long size) {super(name);this.size = size;}@Overridepublic void display(int depth) {System.out.println("-".repeat(depth) + name + " (" + size + " bytes)");}@Overridepublic long getSize() {return size;}
}// 容器節點:目錄
class Directory extends FileSystemComponent {private List<FileSystemComponent> children = new ArrayList<>();public Directory(String name) {super(name);}@Overridepublic void display(int depth) {System.out.println("-".repeat(depth) + "[D] " + name);for (FileSystemComponent component : children) {component.display(depth + 2);}}@Overridepublic long getSize() {long totalSize = 0;for (FileSystemComponent component : children) {totalSize += component.getSize();}return totalSize;}@Overridepublic void add(FileSystemComponent component) {children.add(component);}@Overridepublic void remove(FileSystemComponent component) {children.remove(component);}
}// 客戶端代碼
public class CompositePatternDemo {public static void main(String[] args) {// 創建文件File file1 = new File("document.txt", 1024);File file2 = new File("image.jpg", 2048);File file3 = new File("notes.txt", 512);// 創建子目錄Directory subDir = new Directory("SubFolder");subDir.add(file2);subDir.add(file3);// 創建根目錄Directory rootDir = new Directory("Root");rootDir.add(file1);rootDir.add(subDir);// 顯示整個文件系統結構System.out.println("File System Structure:");rootDir.display(1);// 計算總大小System.out.println("\nTotal Size: " + rootDir.getSize() + " bytes");}
}
輸出結果:
File System Structure:
- [D] Root
---document.txt (1024 bytes)
--- [D] SubFolder
-----image.jpg (2048 bytes)
-----notes.txt (512 bytes)Total Size: 3584 bytes
實際應用場景
組合模式在Java中有許多實際應用:
- GUI組件:Swing/AWT中的Container和Component
- XML/HTML解析:DOM樹結構
- 組織架構:公司部門人員管理
- 文件系統:如上面的示例
- 菜單系統:菜單和菜單項
總結
組合模式通過將對象組織成樹形結構,讓我們能夠以統一的方式處理單個對象和組合對象。它特別適合表示部分-整體層次結構,使得添加新類型的組件變得容易,同時保持代碼的簡潔性。
關鍵點在于:
- 定義一個既能代表葉子又能代表容器的抽象
- 容器需要存儲子組件并實現管理方法
- 葉子節點實現基礎行為
- 客戶端代碼可以統一處理所有組件