引入間接隔離變化(三)

作者: 張逸  來源: 博客園  發布時間: 2011-01-21 00:01  閱讀: 567 次  推薦: 0   原文鏈接   [收藏]  

  間接在分層架構中體現尤為明顯,引入層實則就是引入間接性。利用間接對變化進行隔離,使得變化只能影響一層。例如在領域層與數據庫之間引入數據訪問層,就可以隔絕因為數據庫發生的變化對領域帶來的影響。

  在分層架構中,我們應盡量保證在某一層中所有用到的組件都工作在同一個抽象層中,這意味著需要謹慎處理對象之間的協作,盡量避免跨層之間的調用。如果不同的層部署在不同的服務器,就會帶來不必要的消息處理,增加了網絡跳數與帶寬占用。拋開性能不談,跨層調用產生的依賴,可能破壞層的內聚性。倘若必須依賴于底層,我們也應該減少對底層的依賴點。要減少這種依賴,可以在同一層中提供一個間接接口,用于處理對底層的依賴關系,從而將變化集中于一處。

  對于模塊和組件中的對象協作,同樣遵循這一原則。假設系統定義了報表引擎組件,它需要調用數據引擎組件提供的服務,驅動引擎執行數據庫查詢,以獲得報表所必須的數據。一般采用的設計如下圖所示:image   這樣的設計充分體現了抽象的原則,保證了ReportEngine與DataEngine組件之間的松散耦合。在ReportEngine組件中,與報表相關的許多領域對象都需要調用DataEngineService,以獲得報表所需要的數據。然而,我在設計時并未滿足于DataEngineService接口引入的間接,而是在ReportEngine組件中再度引入了一層間接,我將其定義為DataEngineRepository,它屬于報表引擎的領域范圍:

 
public interface DataEngineRepository {
public List<Map<String, Object>> find(
CommandInfoPreparing commandInfoPreparing);
}

  在DataEngineRepository的實現類中,注入了DataEngineService對象:

 
public class DataEngineRepositoryImpl
implements DataEngineRepository{
DataReadCommandInfoFactory commandInfoFactory
= null;
DataEngineService dataEngineService ;


public List<Map<String, Object>> find(
CommandInfoPreparing commandInfoPreparing){
DataReadCommandInfo commandInfo
= commandInfoFactory.
create(commandInfoPreparing);

return dataEngineService.queryData(commandInfo);
}


public DataEngineService getDataEngineService() {
return dataEngineService;
}

public void setDataEngineService(
DataEngineService dataEngineService) {

this.dataEngineService = dataEngineService;
}
}

  既然DataEngineService接口已經提供了合理的抽象,引入DataEngineRepository接口會否是多余的呢?現在的他,看起來像是一名呆瓜接力選手,剛剛接到接力棒,就驚慌失措地趕緊塞給下一個人手中了。然而,經過仔細分析,我們還是能夠看到二者的細微區別。在沒有引入DataEngineRepository接口之前,報表引擎中的領域對象都依賴于跨組件的DataEngineService;現在,這些領域對象只需依賴同一個組件中的DataEngineRepository即可。這意味著什么呢?我們可以比較下面的兩個組件圖:

image

image  顯然,通過引入DataEngineRepository接口,報表引擎組件中領域對象的依賴關系發生了轉移。依賴存在于組件之中,而沒有擴散到組件之外,表明這個組件是高內聚的。

  間接引入的好處只有在變化時,才能凸顯出來。我們的項目確實遭遇了變化。我們不希望看到ReportEngine組件直接依賴于DataEngine,因為報表對數據的訪問不應該是直接的,它需要受到權限、安全的限制,同時可能還需要控制相關的業務。為此,我們在ReportEngine與DataEngine之間,引入了FunctionEngine,用以實現功能的控制與分發。現在,ReportEngine應該依賴于FunctionEngine,而不是DataEngine。image   由于DataEngineRepository接口隔離了報表引擎中領域對象與數據引擎之間的依賴關系,使得我們可以從容應對功能引擎帶來的變化。例如,修改接口的實現類,將原來對DataEngineService的引用,修改為對功能引擎中FunctionExecutor的引用:

 
public class DataEngineRepositoryImpl
implements DataEngineRepository{
DataReadCommandInfoFactory commandInfoFactory
= null;
FunctionExecutor functionExecutor ;


public List<Map<String, Object>> find(
CommandInfoPreparing commandInfoPreparing){
DataReadCommandInfo commandInfo
= commandInfoFactory.
create(commandInfoPreparing);
RequestParameter parameter
= RequestParameter.newInstance();
parameter.add(commandInfo);

return (List<Map<String,Object>>)functionExecutor.
execute(parameter);
}


public FunctionExecutor getFunctionExecutor() {
return functionExecutor;
}

public void setFunctionExecutor(
FunctionExecutor functionExecutor) {

this.functionExecutor = functionExecutor;
}
}

  修改DataEngineRepositoryImpl類的實現,并不會影響報表引擎中的領域對象,數據引擎也維持了自身的穩定。或許有人認為,即使不引入DataEngineRepository接口,功能引擎帶來的變化也不會影響原有的設計,因為DataEngineService是抽象的接口,我們只需要修改它的實現即可。可是,切勿忘記這里的變化牽涉到功能引擎,修改DataEngineService的實現類,就意味著需要在它的實現中執行對功能引擎的調用;而功能引擎又必須調用數據引擎,從而帶來FunctionEngine與DataEngine之間的循環依賴。沒有比循環依賴更糟糕的依賴關系了!

0
0
 
標簽:OO
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()