實例場景:咱們平時操做文件夾的複製,不管是文件中包含文件或者文件中的子文件咱們都會一併將其複製到相應的目錄下面,在這個場景中咱們使用了組合模式。java
組合模式:屬於結構性模式,它描述了對象間的組合關係。對象間經常經過樹結構來組織(包含)起來,以實現總體-部分的層次結構。總體上能夠看作是一個組合對象。拋卻各類複雜的術語。緩存
組合模式的特色是:對象經過實現(繼承)統一的接口(抽象類),調用者對單一對象和組合對象的操做具備一致性。安全
場景分析:文件夾系統就是一個樹形結構,它裏面包含了總體與部分的層次結構,文件和文件夾提供給用戶的操做是同樣的,好比說複製文件或文件夾,因而,能夠經過一個統一的接口將文件和文件夾統一塊兒來,對用戶提供一致的操做,屏蔽不一樣的複製實現過程。咱們在複製文件夾的時候,操做系統實現了對文件夾內的全部文件和文件夾的複製,即實現了組合對象的總體複製,而不是一個空的文件夾;這和咱們複製單個文件的體驗是一致的。這即是組合模式的妙處。ide
組合模式的角色:測試
抽象構件角色(Component):爲組合的對象聲明接口,在某些狀況下實現今後接口派生出的全部類共有的默認行爲;定義一個接口能夠訪問及管理它的多個子部件ui
葉部件(Leaf):在組合中表示葉節點對象,葉節點沒有子節點;定義組合中接口對象的行爲this
組合類(Composite):定義有子節點(子部件)的部件的行爲;存儲子節點;在Component接口中實現與子部件相關的操做。spa
客戶端(Client):經過Component接口控制組合部件的對象。操作系統
場景代碼實現:code
package cn.com.composite; //抽象構件角色,爲全部文件及文件夾定義統一的接口 public interface Component { public void copy();//定義一個文件複製的方法 }
package cn.com.composite; //文件,至關與葉子節點(葉部件) public class MyFile implements Component { private String name; public MyFile(String name){ this.name=name; } @Override public void copy() { System.out.println("複製文件:"+name); } }
package cn.com.composite; import java.util.ArrayList; import java.util.List; //文件夾,至關與組合類(Composite) public class Folder implements Component { private String name; public Folder(String name){ this.name=name; } //裏面裝Component表示能夠操做Component List<Component> list=new ArrayList<Component>(); //增長構件角色,增長文件或者文件夾 public void add(Component component){ list.add(component); } //刪除文件或文件夾 public void remove(Component componet){ list.remove(componet); } //返回全部文件及文件夾 public List<Component> getAll(){ return this.list; } //對文件及文件夾進行復制操做 @Override public void copy() { System.out.println("複製文件夾:"+name); //遍歷文件及文件夾,若是是文件就會複製,若是是文件夾 //就會遍歷文件夾中的文件進行復制,隱藏了一個遞歸操做 for(Component component:list){ component.copy(); } } }
package cn.com.composite; public class Client { public static void main(String[] args) { Component file1=new MyFile("你是個人眼.mp3"); Component file2=new MyFile("傷不起.mp3"); Folder music=new Folder("個人音樂"); music.add(file1); music.add(file2); Component file3=new MyFile("個人資料.doc"); Component file4=new MyFile("個人圖片.jpg"); Folder folder=new Folder("個人文件"); folder.add(file3); folder.add(file4); folder.add(music);//添加一個文件夾 folder.copy(); } }
由以上的代碼和運行結果可知:
經過實現組合模式,用戶對文件夾的操做與對普通文件的操做並沒有差別。用戶徹底不用關心這是文件夾仍是文件,也不用關心文件夾內部的具體結構,就能夠完成相關操做。一樣的道理,咱們能夠表達以下:
經過實現組合模式,調用者對組合對象的操做與對單一對象的操做具備一致性。調用者不用關心這是組合對象仍是文件,也不用關心組合對象內部的具體結構,就能夠調用相關方法,實現功能。
類比JUnit中,當咱們定義TestCase時與suite,運行的時候發現不管運行多個測試案例仍是單個測試案例,他們運行沒有什麼差別,由於在這個裏面就用到了咱們的組合模式,在Suite中遍歷TestCase,執行其方法
仔細分析copy()方法的代碼,咱們會發現,若是從面向過程的角度思考,組合模式經過遞歸原理實現了樹結構(組合對象)的深度優先遍歷。
代碼中還有一種實現方式:就是把那些方法都定義在Component接口中,在Leaf中對其進行空實現,那麼咱們在Client端就均可以換成是Component引用了。
代碼以下:
package cn.com.composite; import java.util.List; //抽象構件角色,爲全部文件及文件夾定義統一的接口 public interface Component { public void copy();//定義一個文件複製的方法 public void add(Component component); public void remove(Component componet); public List<Component> getAll(); }
package cn.com.composite; import java.util.List; //文件,至關與葉子節點(葉部件) public class MyFile implements Component { private String name; public MyFile(String name){ this.name=name; } @Override public void copy() { System.out.println("複製文件:"+name); } @Override public void add(Component component) { } @Override public List<Component> getAll() { return null; } @Override public void remove(Component componet) { } }//Client均可以換成Component引用
Component file1=new MyFile("你是個人眼.mp3"); Component file2=new MyFile("傷不起.mp3"); Component music=new Folder("個人音樂"); music.add(file1); music.add(file2); Component file3=new MyFile("個人資料.doc"); Component file4=new MyFile("個人圖片.jpg"); Component folder=new Folder("個人文件"); folder.add(file3); folder.add(file4); folder.add(music);//添加一個文件夾 folder.copy();使用組合模式時考慮的幾個問題:
- Composite模式採用樹形結構來實現廣泛存在的對象容器,從而將「一對多」的關係轉換爲「一對一」的關係,使得客戶代碼能夠一致地處理對象和對象容器,無需關係處理的是單個對象仍是組合的對象容器。
- 將客戶代碼與複雜的對象容器解耦是合成模式的核心思想,解耦以後,客戶代碼將與純粹的抽象接口—----非對象容器的內部實現結構發生依賴關係。
- 有時候系統須要遍歷一個樹枝結構的子構件不少次,這時候能夠考慮把遍歷子構件的結構暫時存儲在父構件裏面做爲緩存。
- Composite模式中,是將Add和Remove等和對象容器相關的方法定義在「表示抽象的Componont類」中,仍是定義在「表示對象容器的Composite類」中,是一個關乎「透明性」和「安全性」的兩難問題,須要仔細權衡。