設計模式——組合模式(文件夾與文件)
本文首發於cdream的個人博客,點擊獲得更好的閱讀體驗!
歡迎轉載,轉載請註明出處。
本文簡單介紹組合模式,以系統文件和文件夾為例。
一、概述
定義:組合模式(Composite pattern)將對象整合到樹狀結構中來表示整體/部分的層次關系,在樹狀結構中包括對象和對象組合。組合模式能讓客戶用一致的方式處理個別對象的對象組合。
組合模式主要解決兩個問題:1.部分-整體的層級結構以樹狀結構來表現
2.部分整體的層級結構中,對象和對象組合要以一致方式處理
二、結構
UML:
主要角色:
抽象構件類(Compnent):為所有對象定義了一個接口,無論是葉節點還是組合。
葉構件類(Leaf):繼承了抽象構件類,但是沒有子構件了,雖然繼承了add,remove,getChildren方法,但是對於葉節點來說沒什麽意義,不過為了保持透明性,我們堅持這麽做。
樹枝構件類(Component):繼承了抽象構件類,持有一個子構件類容器口。
三、系統文件目錄
系統文件目錄是一個典型的包括葉構件(文件)和樹枝構件(文件夾)的組合模式。
當然文件夾和文件操作上還是有區別的,不過,我在這裏就讓文件夾和文件都實現相同的接口,完成最理想的組合的組合模式——放棄安全性,追求透明性。
定義一個抽象接口,為了保證可以使用戶不關心究竟是什麽,所有文件/文件夾都要實現這個接口。
public abstract class FileInterface { // 添加文件 void add(FileInterface file) { throw new UnsupportedOperationException(); } // 刪除文件 void remove(String fileName) { throw new UnsupportedOperationException(); } // 獲取文件 Set<FileInterface> getChildren() { throw new UnsupportedOperationException(); } // 獲取文件名字 String getName(){ throw new UnsupportedOperationException(); } // 獲取文件描述 String getDescription(){ throw new UnsupportedOperationException(); }; // 運行程序 void run(){ throw new UnsupportedOperationException(); }; }
寫一個文件夾類
public class Directory implements FileInterface { private String name; private String desc; // 文件夾要持有文件列表 private Set<FileInterface> set = new HashSet<>(); public Directory(String name, String desc) { this.name = name; this.desc = desc; } @Override public void add(FileInterface file) { set.add(file); } @Override public void remove(String fileName) { if (fileName==null) throw new UnsupportedOperationException("請輸入文件名"); Iterator<FileInterface> iterator = this.set.iterator(); while (iterator.hasNext()){ FileInterface next = iterator.next(); if (fileName.equals(next.getName())){ iterator.remove(); } } } @Override public Set<FileInterface> getChildren() { return this.set; } @Override public String getName() { return this.name; } @Override public String getDescription() { return this.desc; } // 重寫了equals和hashcode方法,不允許出現重名文件 @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof FileInterface)) return false; FileInterface directory = (FileInterface) o; return name.equals(directory.getName()); } @Override public int hashCode() { return name.hashCode(); } @Override public String toString() { return "Directory{" + "name=‘" + name + ‘\‘‘ + ", desc=‘" + desc + ‘\‘‘ + ", set=" + set + ‘}‘; } }
寫一個exe文件類
public class ExeFile implements FileInterface {
private String name;
private String desc;
public ExeFile(String name, String desc) {
this.name = name;
this.desc = desc;
}
@Override
public String getName() {
return this.name;
}
@Override
public String getDescription() {
return this.desc;
}
@Override
public void run() {
System.out.println("運行程序");
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof FileInterface)) return false;
FileInterface directory = (FileInterface) o;
return name.equals(directory.getName());
}
@Override
public int hashCode() {
return name.hashCode();
}
@Override
public String toString() {
return "ExeFile{" +
"name=‘" + name + ‘\‘‘ +
", desc=‘" + desc + ‘\‘‘ +
‘}‘;
}
}
這兩個類都繼承了FileIntercepter抽象類並實現了所有方法,可以等同看待,雖然有些方法並不是兩個類都能用的,一定程度上喪失了安全性(程序員調用時需要指到方法的具體實現),不過為了保持透明性(方法同等看待),我們仍選擇了全部實現,對於個別方法使用拋出不支持操作異常處理!
下面看測試類
public class Test {
public static void main(String[] args) {
Directory c = new Directory("C:", "C盤啊");
ExeFile system = new ExeFile("system.exe", "系統程序");
c.add(system);
Directory animals = new Directory("animals", "動物程序文件夾");
ExeFile dog = new ExeFile("dog.exe", "小狗程序");
ExeFile pig = new ExeFile("pig.exe","小豬程序");
animals.add(dog);
animals.add(pig);
c.add(animals);
animals.remove("dog.exe");
System.out.println(c);
}
}
---->結果
Directory{name=‘C:‘, desc=‘C盤啊‘, set=[ExeFile{name=‘system.exe‘, desc=‘系統程序‘}, Directory{name=‘animals‘, desc=‘動物程序文件夾‘, set=[ExeFile{name=‘pig.exe‘, desc=‘小豬程序‘}]}]}
四、優缺點
優點:
- 組合模式可以很容易的增加新的構件。
- 使用組合模式可以使客戶端變的很容易設計,因為客戶端可以對組合和葉節點一視同仁。
缺點:
- 使用組合模式後,控制樹枝構件的類型不太容易。
- 用繼承的方法來增加新的行為很困難。
組合的適用場合:
- 你想表示對象的部分-整體層次結構。
- 你希望用戶忽略組合對象與單個對象的不同,用戶將統一地使用組合結構中的所有對象。
五、總結
組合模式是對象的結構模式,重點掌握樹狀結構和將葉節點和組合節點同等看待。
當組合模式與叠代器模式結合時,調用者甚至可以忽略掉組合模式的結構,這裏我必須推薦Head First 設計模式中叠代器模式與組合模式這一章,你會發現組合模式與叠代器結合的巨大威力!
此外也有人會為了安全性,將非共性方法不在抽象類中聲明,這樣雖然安全了,但是調用者就不能將組合模式的葉節點與組合節點同等看待,這並不符合我們的目的,所以我們還是選擇本文這種實現方式。
- Head First 設計模式,Eric Freeman &Elisabeth Freeman with Kathy Sierra & Bert Bates
- Composite pattern,wiki
- 《JAVA與模式》之合成模式
設計模式——組合模式(文件夾與文件)