本文從一個給定的實現了組合(Composite)模式的例子開始
讀者定位於具有Java程序開發和設計模式經驗的開發人員
讀者通過本文可以學到如何在組合(Composite)模式中實現各種不同的業務方法及其優缺點
組合(Composite)模式
組合模式是結構型模式中的一種
組合模式應用廣泛
如圖所示
(
(
(
(
(
用不同的方法實現業務邏輯
數據結構建立好之後
非面向對象的編程方式
這種方式下
/**
* 取得某個SoftwareComponent對象下面所有Product的價格
* @param brand
* @return
*/
public double getTotalPrice(SoftwareComponent softwareComponent) {
SoftwareComponent temp = softwareComponent;
double totalPrice =
//如果傳入的實例是SoftwareSet的類型
if (temp instanceof SoftwareSet) {
Iterator it = ((SoftwareSet) softwareComponent)
&erator();
while (it
temp = (SoftwareComponent) it
//如果子對象是Product類型的
if (temp instanceof Product) {
Product product = (Product) temp;
totalPrice += product
} else if (temp instanceof Brand) {
//如果子對象是Brand類型的
Brand brand = (Brand) temp;
totalPrice += getBrandPrice(brand);
}
}
} else if (temp instanceof Brand) {
//如果傳入的實例是SoftwareSet的類型
totalPrice += getBrandPrice((Brand) temp);
} else if (temp instanceof Product) {
//如果子對象是Product類型的
return ((Product) temp)
}
return totalPrice;
}
/**
* 取得某個Brand對象下面所有Product的價格
* @param brand
* @return
*/
private double getBrandPrice(Brand brand) {
Iterator brandIt = brand
double totalPrice =
while (brandIt
Product product = (Product) brandIt
totalPrice += product
}
return totalPrice;
}
這段代碼的好處是實現業務邏輯的時候無需對前面已經定好的數據結構做改動
面向對象的編程方式(將計算價格的方法加入數據結構中)
下面我們采用面向對象的方式
/**
* 返回該節點中所有子節點對象的價格之和
* @return
*/
public double getTotalPrice();
public double getTotalPrice() {
Iterator it = erator();
double price =
while (it
SoftwareComponent softwareComponent = (SoftwareComponent) it
//自動遞歸調用各個對象的getTotalPrice方法並累加
price += softwareComponent
}
return price;
}
public double getTotalPrice(){
return price;
}
// getMockData()方法返回數據
SoftwareComponent data = getMockData();
//只需直接調用data對象的getTotalPrice 方法就可以返回該對象下所有product對象的價格
double price = data
//找到某個對象後直接調用其getTotalPrice方法也可以返回總價格
price = data
現在把業務邏輯的實現都放在了數據結構中(組合模式的結構中)
使用訪問者模式
使用訪問者模式就能解決上面提到的問題
public interface Visitor {
public void visitBrand(Brand brand);
public void visitSoftwareSet(SoftwareSet softwareSet);
public void visitProduct(Product product);
}
public void accept(Visitor visitor);
public void accept(Visitor visitor) {
visitor
Iterator it = erator();
while (it
SoftwareComponent component = (SoftwareComponent)it
component
}
}
public void accept(Visitor visitor) {
visitor
Iterator it = erator();
while (it
SoftwareComponent component = (SoftwareComponent)it
component
}
}
在Product中實現接口中的accept方法
public void accept(Visitor visitor) {
visitor
}
public class CaculateTotalPriceVisitor implements Visitor {
private double totalPrice;
public void visitBrand(Brand brand) {
}
public void visitSoftwareSet(SoftwareSet softwareSet) {
}
public void visitProduct(Product product) {
//每次在組合的結構中碰到Product對象節點的時候
totalPrice += product
}
public double getTotalPrice() {
return totalPrice;
}
}
//建立一個新的Visitor對象
CaculateTotalPriceVisitor visitor = new CaculateTotalPriceVisitor();
//將該visitor對象傳到結構中
data
//調用visitor對象的getTotalPrice()方法就返回了總價格
double price = visitor
下面是它的時序圖
點擊小圖看大圖
我們可以看到通過訪問者模式很好地解決了如何加入新的業務代碼而無需重新改動
改進訪問者模式
前面我們說到了如何使用Visitor模式及使用該模式後的優缺點
使用Java的Method Reflection機制實現訪問者模式
首先我們需要改變一下Visitor接口
public interface ReflectionVisitor {
/**
* 定義了一個訪問節點的方法
* @param softwareComposite
*/
public void visitSoftwareComposite(Object softwareComposite);
}
在現在的接口的方法裡
下面實現接口ReflectionVisitor
public class ReflectionVisitorImpl implements ReflectionVisitor {
public void visitSoftwareComposite(Object softwareComposite) {
//判斷是否是null
if (softwareComposite == null) {
throw new NullPointerException(
}
//組裝class數組
Class[] classes = new Class[] { softwareComposite
//組裝與class數組相對應的值
Object[] objects = new Object[] { softwareComposite };
try {
//查找visit方法
Method m = getClass()
//調用該方法
m
} catch (NoSuchMethodException e) {
//沒有找到相應的方法
System
+ softwareComposite
} catch (Exception e) {
//發生了別的異常
System
e
}
}
}
這段代碼首先判斷傳入的對象是否是空指針
下面再來寫新版本Visitor類
public class CaculateTotalPriceReflectionVisitor extends ReflectionVisitorImpl {
private double totalPrice;
public void visit(Product product) {
totalPrice += product
}
public void visit(SoftwareSet softwareSet) {
System
}
public double getTotalPrice() {
return totalPrice;
}
}
public void visit(ProductSet productSet) {
//實現的代碼
}
在組合結構的接口SoftwareComponent中改一下accept方法
public void accept(ReflectionVisitor visitor);
由於在類SoftwareSet
public void accept(ReflectionVisitor visitor) {
visitor
Iterator it = erator();
while (it
SoftwareComponent component = (SoftwareComponent) it
//遞歸調用子對象的accept方法
component
}
}
//建立一個新的Visitor對象
CaculateTotalPriceReflectionVisitor reflectionVisitor
= new CaculateTotalPriceReflectionVisitor();
//將該visitor對象傳到結構中
data
//調用visitor對象的getTotalPrice()方法就返回了總價格
double price = reflectionVisitor
另外由於沒有實現Brand類的visit方法
You did not implement the visit method for class:class com
如果運行程序時發生了別的異常
在現在的改進後的訪問者模式中
改進訪問者模式實現與既有代碼對接
到現在為止
public class NewReflectionVisitorImpl implements ReflectionVisitor {
// 實現visit方法的類
private Object targetObject;
//構造方法
public NewReflectionVisitorImpl(Object targetObject) {
if (targetObject == null)
throw new NullPointerException(
this
}
public void visitSoftwareComposite(Object softwareComposite) {
//……與上個例子相同
try {
// 從目標的對象中查找visit方法
Method m = targetObject
// 調用該方法
m
} catch (NoSuchMethodException e) {
//……與上個例子相同
} catch (Exception e) {
//……與上個例子相同
}
}
}
// 從目標的對象中查找visit方法
Method m = targetObject
// 調用該方法
m
本來的代碼中從本身的類及其子類中查找visit方法
現在需要寫Tag類
public class MyTag extends TagSupport {
SoftwareComponent softwareComponent = null;
private double totalPrice =
public int doEngTag() {
//創建一個visitor對象
ReflectionVisitor visitor = new NewReflectionVisitorImpl(this);
//遍歷結構
softwareComponent
//打印出價格
out
return
}
//實現了針對Product的visit方法
public void visit(Product product) {
totalPrice += product
}
public void visit(Brand brand) {
out
}
//別的代碼請參見隨本文帶的源程序
……
}
//getMockData()方法返回數據
SoftwareComponent data = getMockData();
MyTag myTag = new MyTag();
myTag
//計算總價格
myTag
可以看到通過Java的反射機制很好地解決了多重繼承的問題
如果擔心引入類反射機制後帶來的效率問題
結論
在給定的組合模式的數據結構中
From:http://tw.wingwit.com/Article/program/Java/gj/201311/11152.html