本文首發於cdream的我的博客,點擊得到更好的閱讀體驗!html
歡迎轉載,轉載請註明出處。java
本文簡單介紹組合模式,以系統文件和文件夾爲例。git
<!--more-->程序員
定義:組合模式(Composite pattern)將對象整合到樹狀結構中來表示總體/部分的層次關係,在樹狀結構中包括對象和對象組合。組合模式能讓客戶用一致的方式處理個別對象的對象組合。github
<div class="note info"><strong>組合模式主要解決兩個問題</strong>:<br>1.部分-總體的層級結構以樹狀結構來表現<br>2.部分總體的層級結構中,對象和對象組合要以一致方式處理</div>設計模式
UML:安全
主要角色:ide
抽象構件類(Compnent):爲全部對象定義了一個接口,不管是葉節點仍是組合。測試
葉構件類(Leaf):繼承了抽象構件類,可是沒有子構件了,雖然繼承了add,remove,getChildren方法,可是對於葉節點來講沒什麼意義,不過爲了保持透明性,咱們堅持這麼作。this
樹枝構件類(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='小豬程序'}]}]}
優勢:
缺點:
組合的適用場合:
組合模式是對象的結構模式,重點掌握樹狀結構和將葉節點和組合節點同等看待。
當組合模式與迭代器模式結合時,調用者甚至能夠忽略掉組合模式的結構,這裏我必須推薦<font color="purple"><i>Head First 設計模式</i></font>中迭代器模式與組合模式這一章,你會發現組合模式與迭代器結合的巨大威力!
此外也有人會爲了安全性,將非共性方法不在抽象類中聲明,這樣雖然安全了,可是調用者就不能將組合模式的葉節點與組合節點同等看待,這並不符合咱們的目的,因此咱們仍是選擇本文這種實現方式。