设计模式之组合模式(Composite)
1.引言
在學習JUnit的時候,看到一段話“JUnit框架是一個典型的Composite模式:TestSuite可以容納任何派生自Test的對象;當調用TestSuite對象的run()方法是,會遍歷自己容納的對象,逐個調用它們的run()方法”。就來學習什么是組合模式。
2.應用實例
在實現跟商品有關的應用系統的時候,一個很常見的功能就是商品類別樹的管理,比如有以下的商品類別樹:
——————————————————————————————————
-服裝
-男裝
-襯衣
-夾克
-女裝
-裙子
-套裝
——————————————————————————————————
通過上面的商品類別樹我們可以發現商品類別樹有兩種類型的節點,分別是葉子節點(襯衣,夾克)和組合節點(服裝,男裝,女裝)。組合節點中可以包含其他的組合節點或者葉子節點,而葉子節點不能。
給出一個簡單的管理商品類別樹的實例代碼。
2.1不使用組合模式的解決方案
組合節點Composite
View Code package edu.sjtu.erplab.designpattern.composite.exp1;import java.util.ArrayList;
import java.util.Collection;
public class Composite {
private Collection<Composite> childComposite=new ArrayList<Composite>();
private Collection<Leaf> childLeaf=new ArrayList<Leaf>();
private String name="";
public Composite(String name)
{
this.name=name;
}
//向組合對象中加入其他組合對象
public void addComposite(Composite c)
{
this.childComposite.add(c);
}
//向組合對象加入其他葉子對象
public void addLeaf(Leaf l)
{
this.childLeaf.add(l);
}
//輸出組合對象結構
public void printStruct(String preStr)
{
System.out.println(preStr+"-"+name);
preStr+=" ";
for(Leaf leaf:childLeaf)
{
leaf.printStruct(preStr);
}
for(Composite composite:childComposite)
{
composite.printStruct(preStr);
}
}
}
葉子節點
View Code package edu.sjtu.erplab.designpattern.composite.exp1;public class Leaf {
private String name="";
public Leaf(String name){
this.name=name;
}
public void printStruct(String preStr)
{
System.out.println(preStr+"-"+name);
}
}
客戶端Client
View Code package edu.sjtu.erplab.designpattern.composite.exp1;public class Client {
public static void main(String args[])
{
//定義組合對象
Composite root=new Composite("服裝");
Composite c1=new Composite("男裝");
Composite c2=new Composite("女裝");
//定義葉子對象
Leaf l1=new Leaf("襯衣");
Leaf l2=new Leaf("夾克");
Leaf l3=new Leaf("裙子");
Leaf l4=new Leaf("套裝");
//按照樹的結構來組合對象
root.addComposite(c1);
root.addComposite(c2);
c1.addLeaf(l1);
c1.addLeaf(l2);
c2.addLeaf(l3);
c2.addLeaf(l4);
root.printStruct("");
}
}
運行結果就是應用實例中顯示的商品樹形結構。
上述解決方案存在的問題:
雖然實現了要求的功能,但是有一個明顯的問題:那就是必須區分組合對象和葉子對象,并進行區別對待,比如在Composite(addComposite方法和addLeaf方法)和Client(定義Composite對象和定義Leaf對象)里面,都需要去區別對待這兩種對象。區別對待組合對象與葉子對象不但讓程序更加復雜,還對功能的擴展帶了了不便。用戶不希望區別對待這兩類對象。
2.2使用組合模式的解決方案
組合模式通過引入一個抽象的組件對象,作為組合對象和葉子對象的父對象,這樣就把組合對象和葉子對象統一起來了,用戶使用的時候,始終是在操作組件對象,而不再去區分是在操作組合對象還是葉子對象。
組合模式的關鍵就在于這個抽象類,這個抽象類既可以代表葉子對象,也可以代表組合對象,這樣用戶在操作的時候,對葉子對象和組合對象的使用就具有了一致性。
組件對象Component
View Code package edu.sjtu.erplab.designpattern.composite.exp2;public abstract class Component {
public abstract void printStruct(String preStr);
//透明性
public void addChild(Component child){
throw new UnsupportedOperationException("對象不支持這個功能");
}
public void removeChild(Component child){
throw new UnsupportedOperationException("對象不支持這個功能");
}
public Component getChild(Component child){
throw new UnsupportedOperationException("對象不支持這個功能");
}
}
組合對象Composite
View Code package edu.sjtu.erplab.designpattern.composite.exp2;import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class Composite extends Component {
private List<Component> childComponents = null;
private String name = "";
public Composite(String name)
{
this.name=name;
}
@Override
public void addChild(Component child) {
// TODO Auto-generated method stub
if (childComponents == null) {
childComponents = new ArrayList<Component>();
}
childComponents.add(child);
}
@Override
public void removeChild(Component child) {
// TODO Auto-generated method stub
super.removeChild(child);
}
@Override
public Component getChild(Component child) {
// TODO Auto-generated method stub
return super.getChild(child);
}
public void printStruct(String preStr) {
System.out.println(preStr + "-" + name);
if (this.childComponents != null) {
preStr += " ";
for (Component c : childComponents) {
c.printStruct(preStr);
}
}
}
}
葉子對象Leaf
View Code package edu.sjtu.erplab.designpattern.composite.exp2;public class Leaf extends Component {
private String name="";
public Leaf(String name){
this.name=name;
}
//必須實現父類的抽象方法
public void printStruct(String preStr)
{
System.out.println(preStr+"-"+name);
}
}
客戶端
View Code package edu.sjtu.erplab.designpattern.composite.exp2;public class Client {
public static void main(String args[])
{
//定義組合對象
Component root=new Composite("服裝");
Component c1=new Composite("男裝");
Component c2=new Composite("女裝");
//定義葉子對象
Component l1=new Leaf("襯衣");
Component l2=new Leaf("夾克");
Component l3=new Leaf("裙子");
Component l4=new Leaf("套裝");
//按照樹的結構來組合對象
root.addChild(c1);
root.addChild(c2);
c1.addChild(l1);
c1.addChild(l2);
c2.addChild(l3);
c2.addChild(l4);
root.printStruct("");
}
}
程序架構如下圖所示。這樣的架構實現了用戶的透明訪問。
3.透明性與安全性的權衡考慮
如上圖所示,在Component組件中定義了操作組合節點的方法addChild,removeChild等,這些方法被Leaf繼承,因此Leaf也能夠調用,但是葉子節點是不能進行增加子節點和刪除子節點的。這樣就存在安全性的問題。
基于安全性考慮的組合模式的解決方案
組件節點Component
View Code package edu.sjtu.erplab.designpattern.composite.exp3;public abstract class Component {
public abstract void printStruct(String preStr);
}
組合節點Composite和葉子節點Leaf沒有改變
客戶端發生改變,需要區分組合節點和葉子節點,這個跟第一個代碼實例中類似。
View Code package edu.sjtu.erplab.designpattern.composite.exp3;public class Client {
public static void main(String args[])
{
//定義組合對象
Composite root=new Composite("服裝");
Composite c1=new Composite("男裝");
Composite c2=new Composite("女裝");
//定義葉子對象
Leaf l1=new Leaf("襯衣");
Leaf l2=new Leaf("夾克");
Leaf l3=new Leaf("裙子");
Leaf l4=new Leaf("套裝");
//按照樹的結構來組合對象
root.addChild(c1);
root.addChild(c2);
c1.addChild(l1);
c1.addChild(l2);
c2.addChild(l3);
c2.addChild(l4);
root.printStruct("");
}
}
上述實例的結構如下圖所示:
透明性組合模式與安全性組合模式的選擇:
對于組合模式,在安全性和透明性上,會更加看重透明性,畢竟組合模式的功能就是讓客戶端對葉子對象和組合對象的使用具有一致性。
4.父組件引用
在上面的示例中,都是在父組件對象中,保存有子組件的引用,也就是說都是從父到子的引用,本節討論一下子組件對象到父組件對象的引用,在實際開發過程中也非常有用,比如可以實現如下功能:
要實現上述功能, 一個較為簡單的方案就是在保持從父組件到子組件引用的基礎上,再增加保持從子組件到父組件的引用,這樣在刪除一個組件對象或者是調整一個組件對象的時候,可以通過調整父組件的引用來實現,可以大大簡化實現。
實例說明如下:
組件對象Component
View Code package edu.sjtu.erplab.designpattern.composite.exp4;import java.util.List;
public abstract class Component {
private Component parent=null;
//獲取某個組件的所有子對象
public List<Component> getChildren()
{
throw new UnsupportedOperationException("對象不支持這個功能");
}
//獲取父對象
public Component getParent() {
return parent;
}
public void setParent(Component parent) {
this.parent = parent;
}
public abstract void printStruct(String preStr);
public void addChild(Component child){
throw new UnsupportedOperationException("對象不支持這個功能");
}
public void removeChild(Component child){
throw new UnsupportedOperationException("對象不支持這個功能");
}
public Component getChild(Component child){
throw new UnsupportedOperationException("對象不支持這個功能");
}
}
組合對象Composite
View Code package edu.sjtu.erplab.designpattern.composite.exp4;import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class Composite extends Component {
private List<Component> childComponents = null;
private String name = "";
public Composite(String name)
{
this.name=name;
}
public void printStruct(String preStr) {
System.out.println(preStr + "-" + name);
if (this.childComponents != null) {
preStr += " ";
for (Component c : childComponents) {
c.printStruct(preStr);
}
}
}
/**
* 添加子組件
* child為具體的子組件
* 為child設置父組件。setParent
*/
@Override
public void addChild(Component child) {
if (childComponents == null) {
childComponents = new ArrayList<Component>();
}
childComponents.add(child);
//添加對父組件的引用
child.setParent(this);
}
/**
* 刪除子組件
*/
@Override
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);
}
}
}
@Override
public List<Component> getChildren()
{
return childComponents;
}
}
葉子節點沒有變化,客戶端有所改變
View Code package edu.sjtu.erplab.designpattern.composite.exp4;public class Client {
public static void main(String args[])
{
//定義組合對象
Component root=new Composite("服裝");
Component c1=new Composite("男裝");
Component c2=new Composite("女裝");
//定義葉子對象
Component l1=new Leaf("襯衣");
Component l2=new Leaf("夾克");
Component l3=new Leaf("裙子");
Component l4=new Leaf("套裝");
//按照樹的結構來組合對象
root.addChild(c1);
root.addChild(c2);
c1.addChild(l1);
c1.addChild(l2);
c2.addChild(l3);
c2.addChild(l4);
root.printStruct("");
System.out.println("---------------------------------");
root.removeChild(c1);
root.printStruct("");
}
}
程序運行結果:
——————————————————————————
-服裝
-男裝
-襯衣
-夾克
-女裝
-裙子
-套裝
---------------------------------
-服裝
-女裝
-裙子
-套裝
-襯衣
-夾克
——————————————————————
程序架構結構圖如下圖所示:
5組合模式的優缺點及適用環境
優點:
缺點:
適用環境
轉載于:https://www.cnblogs.com/xwdreamer/archive/2012/03/29/2424034.html
總結
以上是生活随笔為你收集整理的设计模式之组合模式(Composite)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 配置cacti 监控squid
- 下一篇: Asp.Net防止刷新重复提交数据的办法