Java 設計模式系列(九)組合模式
Java 設計模式系列(九)組合模式
將對象組合成樹形結構以表示“部分-整體”的層次結構。組合模式使得用戶對單個對象的使用具有一致性。
一、組合模式結構
Component
: 抽象的組件對象,為組合中的對象聲明接口,讓客戶端可以通過這個接口來訪問和管理整個對象結構,可以在裏面為定義的功能提供缺省的實現。Leaf
: 葉子節點對象,定義和實現葉子對象的行為,不再包含其它的子節點對象。Composite
: 組合對象,通常會存儲子組件,定義包含子組件的那些組件的行為,並實現在組件接口中定義的與子組件有關的操作。Client
: 客戶端,通過組件接口來操作組合結構裏面的組件對象。
組合模式參考實現
(1)先看看組件對象的定義,示例代碼如下:
/**
* 抽象的組件對象,為組合中的對象聲明接口,實現接口的缺省行為
*/
public abstract class Component {
/** 示意方法,子組件對象可能有的功能方法 */
public abstract void someOperation();
/** 向組合對象中加入組件對象 */
public void addChild(Component child) {
// 缺省的實現,拋出例外,因為葉子對象沒有這個功能,或者子組件沒有實現這個功能
throw new UnsupportedOperationException("對象不支持這個功能" );
}
/** 從組合對象中移出某個組件對象 */
public void removeChild(Component child) {
// 缺省的實現,拋出例外,因為葉子對象沒有這個功能,或者子組件沒有實現這個功能
throw new UnsupportedOperationException("對象不支持這個功能");
}
/** 返回某個索引對應的組件對象 */
public Component getChildren(int index) {
// 缺省的實現,拋出例外,因為葉子對象沒有這個功能,或者子組件沒有實現這個功能
throw new UnsupportedOperationException("對象不支持這個功能");
}
}
(2)接下來看看Composite對象的定義,示例代碼如下:
/**
* 組合對象,通常需要存儲子對象,定義有子部件的部件行為,
* 並實現在Component裏面定義的與子部件有關的操作
*/
public class Composite extends Component {
/**
* 用來存儲組合對象中包含的子組件對象
*/
private List<Component> childComponents = null;
/** 示意方法,通常在裏面需要實現遞歸的調用 */
public void someOperation() {
if (childComponents != null){
for(Component c : childComponents){
//遞歸的進行子組件相應方法的調用
c.someOperation();
}
}
}
public void addChild(Component child) {
//延遲初始化
if (childComponents == null) {
childComponents = new ArrayList<Component>();
}
childComponents.add(child);
}
public void removeChild(Component child) {
if (childComponents != null) {
childComponents.remove(child);
}
}
public Component getChildren(int index) {
if (childComponents != null){
if(index>=0 && index<childComponents.size()){
return childComponents.get(index);
}
}
return null;
}
}
(3)該來看葉子對象的定義了,相對而言比較簡單,示例代碼如下:
/**
* 葉子對象,葉子對象不再包含其它子對象
*/
public class Leaf extends Component {
/** 示意方法,葉子對象可能有自己的功能方法 */
public void someOperation() {
// do something
}
}
(4)對於Client,就是使用Component接口來操作組合對象結構,由於使用方式千差萬別,這裏僅僅提供一個示範性質的使用,順便當作測試代碼使用,示例代碼如下:
public class Client {
public static void main(String[] args) {
//定義多個Composite對象
Component root = new Composite();
Component c1 = new Composite();
Component c2 = new Composite();
//定義多個葉子對象
Component leaf1 = new Leaf();
Component leaf2 = new Leaf();
Component leaf3 = new Leaf();
//組和成為樹形的對象結構
root.addChild(c1);
root.addChild(c2);
root.addChild(leaf1);
c1.addChild(leaf2);
c2.addChild(leaf3);
//操作Component對象
Component o = root.getChildren(1);
System.out.println(o);
}
}
二、父組件引用
(1) Component
public abstract class Component {
/**
* 記錄父組件對象
*/
private Component parent = null;
/**
* 獲取一個組件的父組件對象
* @return 一個組件的父組件對象
*/
public Component getParent() {
return parent;
}
/**
* 設置一個組件的父組件對象
* @param parent 一個組件的父組件對象
*/
public void setParent(Component parent) {
this.parent = parent;
}
/**
* 返回某個組件的子組件對象
* @return 某個組件的子組件對象
*/
public List<Component> getChildren() {
throw new UnsupportedOperationException("對象不支持這個功能");
}
/*-------------------以下是原有的定義----------------------*/
/**
* 輸出組件自身的名稱
*/
public abstract void printStruct(String preStr);
/**
* 向組合對象中加入組件對象
* @param child 被加入組合對象中的組件對象
*/
public void addChild(Component child) {
// 缺省的實現,拋出例外,因為葉子對象沒有這個功能,或者子組件沒有實現這個功能
throw new UnsupportedOperationException("對象不支持這個功能");
}
/**
* 從組合對象中移出某個組件對象
* @param child 被移出的組件對象
*/
public void removeChild(Component child) {
// 缺省的實現,拋出例外,因為葉子對象沒有這個功能,或者子組件沒有實現這個功能
throw new UnsupportedOperationException("對象不支持這個功能");
}
/**
* 返回某個索引對應的組件對象
* @param index 需要獲取的組件對象的索引,索引從0開始
* @return 索引對應的組件對象
*/
public Component getChildren(int index) {
throw new UnsupportedOperationException("對象不支持這個功能");
}
}
(2) Leaf
public class Leaf extends Component {
/**
* 葉子對象的名字
*/
private String name = "";
/**
* 構造方法,傳入葉子對象的名字
* @param name 葉子對象的名字
*/
public Leaf(String name){
this.name = name;
}
/**
* 輸出葉子對象的結構,葉子對象沒有子對象,也就是輸出葉子對象的名字
* @param preStr 前綴,主要是按照層級拼接的空格,實現向後縮進
*/
public void printStruct(String preStr){
System.out.println(preStr+"-"+name);
}
}
(3) Composite
public class Composite extends Component{
public void addChild(Component child) {
//延遲初始化
if (childComponents == null) {
childComponents = new ArrayList<Component>();
}
childComponents.add(child);
//添加對父組件的引用
child.setParent(this);
}
public void removeChild(Component child) {
if (childComponents != null) {
//查找到要刪除的組件在集合中的索引位置
int idx = childComponents.indexOf(child);
if (idx != -1) {
//先把被刪除的商品類別對象的父商品類別,設置成為被刪除的商品類別的子類別的父商品類別
for(Component c : child.getChildren()){
//刪除的組件對象是本實例的一個子組件對象
c.setParent(this);
//把被刪除的商品類別對象的子組件對象添加到當前實例中
childComponents.add(c);
}
//真的刪除
childComponents.remove(idx);
}
}
}
public List<Component> getChildren() {
return childComponents;
}
/*-------------------以下是原有的實現,沒有變化----------------------*/
/**
* 用來存儲組合對象中包含的子組件對象
*/
private List<Component> childComponents = null;
/**
* 組合對象的名字
*/
private String name = "";
/**
* 構造方法,傳入組合對象的名字
* @param name 組合對象的名字
*/
public Composite(String name){
this.name = name;
}
/**
* 輸出組合對象自身的結構
* @param preStr 前綴,主要是按照層級拼接的空格,實現向後縮進
*/
public void printStruct(String preStr){
//先把自己輸出去
System.out.println(preStr+"+"+this.name);
//如果還包含有子組件,那麽就輸出這些子組件對象
if(this.childComponents!=null){
//然後添加一個空格,表示向後縮進一個空格
preStr+=" ";
//輸出當前對象的子對象了
for(Component c : childComponents){
//遞歸輸出每個子對象
c.printStruct(preStr);
}
}
}
}
三、總結
(1) 觀察者模式的本質
組合模式的本質: 統一葉子對象和組合對象。
組合模式通過把葉子對象當成特殊的組合對象看待,從而對葉子對象和組合對象一視同仁,統統當成了Component對象,有機的統一了葉子對象和組合對象。
正是因為統一了葉子對象和組合對象,在將對象構建成樹形結構的時候,才不需要做區分,反正是組件對象裏面包含其它的組件對象,如此遞歸下去;也才使得對於樹形結構的操作變得簡單,不管對象類型,統一操作。
(2) 何時選用觀察者模式
如果你想表示對象的部分-整體層次結構,可以選用組合模式,把整體和部分的操作統一起來,使得層次結構實現更簡單,從外部來使用這個層次結構也簡單。
如果你希望統一的使用組合結構中的所有對象,可以選用組合模式,這正是組合模式提供的主要功能。
(3) 觀察者模式的優缺點
定義了包含基本對象和組合對象的類層次結構
在組合模式中,基本對象可以被組合成更復雜的組合對象,而組合對象又可以組合成更復雜的組合對象,可以不斷地遞歸組合下去,從而構成一個統一的組合對象的類層次結構
統一了組合對象和葉子對象
在組合模式中,可以把葉子對象當作特殊的組合對象看待,為它們定義統一的父類,從而把組合對象和葉子對象的行為統一起來
簡化了客戶端調用
組合模式通過統一組合對象和葉子對象,使得客戶端在使用它們的時候,就不需要再去區分它們,客戶不關心使用的到底是什麽類型的對象,這就大大簡化了客戶端的使用
更容易擴展
由於客戶端是統一的面對Component來操作,因此,新定義的Composite或Leaf子類能夠很容易的與已有的結構一起工作,而客戶端不需要為增添了新的組件類而改變
很難限制組合中的組件類型
容易增加新的組件也會帶來一些問題,比如很難限制組合中的組件類型。這在需要檢測組件類型的時候,使得我們不能依靠編譯期的類型約束來完成,必須在運行期間動態檢測。
每天用心記錄一點點。內容也許不重要,但習慣很重要!
Java 設計模式系列(九)組合模式