在筆者之前一篇介紹DBRider中策略模式案例的文章中有提到為了支持某些操作的組合,在這個策略模式中還混合使用了組合模式。
首先還是通過策略模式來看一下類圖。
在右下角有一個名為CompsiteOperation的類,從命名上看疑似使用了組合模式。
首先來簡單了解一下組合模式Composite Pattern
組合模式(Composite Pattern),又叫部分整體模式,是用于把一組相似的對象當作一個單一的對象。組合模式依據樹形結構來組合對象,用來表示部分以及整體層次。這種類型的設計模式屬于結構型模式,它創建了對象組的樹形結構。
這種模式創建了一個包含自己對象組的類。該類提供了修改相同對象組的方式。
說到組合模式,一般都會用樹來作為案例,樹由樹枝和樹葉組合而成,而樹枝又包含了更小的枝杈或者是樹葉。由于這是一個關于結構型的設計模式,是一個比較靜態的呈現,會讓人感覺有些抽象,以下是筆者從知乎上面一篇文章中摳來的一張圖,通過UML序列圖的方式來表達組合模式,就更為直觀了。
從上圖我們可以看出,當客戶端Client調用整個樹的類CompositeA類的方法doAction()時,由于采用了組合模式,在CompositeA類中存儲了以下的節點的組合
- CompositeB
- LeafC
因此,CompositeA在執行doAction方法的過程會依次調用它們各自的doAction方法。
類似的,CompositeB中也持有2個節點 - LeafA
- LeafB
它們各自的doAction方法會被調用。這樣,通過組合模式,只要通過CompositeA,就可以把一連串的doAction動作組合起來供客戶端調用。在這個基礎上,我們還可以根據業務需要派生出CompositeC等不同的組合。
為什么說CompsiteOperation是采用了組合模式。
首先來看看調用者。前面有提到@DataSet注解有一個strategy屬性,指定了若干的數據集插入數據庫的策略,這主要是通過SeedStrategy 這個枚舉類來實現的。
package com.github.database.rider.core.api.dataset;
/**
* Created by pestano on 23/07/15.
*/
import org.dbunit.operation.*;
public enum SeedStrategy {
CLEAN_INSERT(DatabaseOperation.CLEAN_INSERT),
TRUNCATE_INSERT(new CompositeOperation(DatabaseOperation.TRUNCATE_TABLE, DatabaseOperation.INSERT)),
INSERT(DatabaseOperation.INSERT),
REFRESH(DatabaseOperation.REFRESH),
UPDATE(DatabaseOperation.UPDATE);
private final DatabaseOperation operation;
SeedStrategy(DatabaseOperation operation) {
this.operation = operation;
}
public DatabaseOperation getOperation() {
return operation;
}
}
這其中有兩種策略就屬于組合策略,
- CLEAN_INSERT(DatabaseOperation.CLEAN_INSERT),
- TRUNCATE_INSERT(new CompositeOperation(DatabaseOperation.TRUNCATE_TABLE, DatabaseOperation.INSERT))
例如CLEAN_INSERT就是先將數據庫中目標表清空,然后在執行INSERT操作。
組合類中的自身對象組和遍歷方法
作為組合類的標志,CompositeOperation中應該包含了一個容納DatabaseOperation類及其子類的組合,以及遍歷并執行execute方法的execute方法,我們來看一下
package org.dbunit.operation;
import java.sql.SQLException;
import java.util.Arrays;
import org.dbunit.DatabaseUnitException;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.dataset.IDataSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CompositeOperation extends DatabaseOperation {
private static final Logger logger = LoggerFactory.getLogger(CompositeOperation.class);
private final DatabaseOperation[] _actions;
public CompositeOperation(DatabaseOperation action1, DatabaseOperation action2) {
this._actions = new DatabaseOperation[]{action1, action2};
}
public CompositeOperation(DatabaseOperation[] actions) {
this._actions = actions;
}
public void execute(IDatabaseConnection connection, IDataSet dataSet) throws DatabaseUnitException, SQLException {
logger.debug("execute(connection={}, , dataSet={}) - start", connection, dataSet);
for(int i = 0; i < this._actions.length; ++i) {
DatabaseOperation action = this._actions[i];
action.execute(connection, dataSet);
}
}
//...
}
可以看到,在CompositeOperation類中的確有如下的一個數組
private final DatabaseOperation[] _actions;
以及一個重載的execute方法
public void execute(IDatabaseConnection connection, IDataSet dataSet)
這樣,就能將_actions數組中存放的各類型DataBaseOperation按照順序執行了。
CompositeOperation的UML序列圖
參考之前的類圖,結合DataBaseRider中的源碼,筆者畫了下面的一個簡化示意圖。如前所述,目前有兩種策略是使用了組合模式,也就是是CompositeOperation類的兩個實例,分別是CLEAN_INSERT和TRUNCATE_INSERT。整個組合的調用過程還是比較清晰的。
如果有看到開源項目中使用的其它設計模式,歡迎留言給筆者提供線索。