引入間接隔離變化(二)

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

  抽象建立的這層間接性,解除了調用者與實現類之間的具體依賴,使得實現類可以單獨變化,而不會影響到調用者。例如,當我們需要為元數據的讀取操作定義對象時,好的編碼習慣是為其定義一個接口:

 
public interface MetadataReaderService {
public MetadataObject getMetadataObject(String metadataName);
public MetadataField getMetadataField(
String tableName,String fieldName);

public MetadataRelation getMetadatarelation(
long objectId,long relateObjectId);
}

  MetadataReaderServiceImpl類實現了MetadataReaderService接口,在實現中通過注入數據訪問對象,完成對元數據信息的讀取:

 
public class MetadataReaderServiceImpl implements
MetadataReaderService {


private MetadataObjectDAO metadataObjectDAO;
private MetadataFieldDAO metadataFieldDAO;
private MetadataRelationDAO metadataRelationDAO;

public MetadataObjectDAO getMetadataObjectDAO() {
return metadataObjectDAO;
}

public void setMetadataObjectDAO(
MetadataObjectDAO metadataObjectDAO) {

this.metadataObjectDAO = metadataObjectDAO;
}

public MetadataFieldDAO getMetadataFieldDAO() {
return metadataFieldDAO;
}


public MetadataObject getMetadataObject(String metadataName) {
return this.metadataObjectDAO.getMetadataObject(metadataName);
}
}

  為何一定要定義MetadataReaderService接口?因為接口可以使得它與調用者的依賴降到最弱。例如調用者QuerySqlStatementImpl類:

 
public class QuerySqlStatementImpl {
public MetadataReaderService getMetadataReader() {
return this.metadataReaderService;
}


public void setMetadataReader(MetadataReaderService reader) {
this.metadataReaderService = reader;
}


private String generateJoinStatement(TableNode rootTableNode){
String metadataName
= rootTableNode.getMetadataName();
MetadataObject rootMetadataObject
=
this.getMetadataReader().getMetadataObject(metadataName);
}
}

image  QuerySqlStatementImpl與MetadataReaderServiceImpl之間不存在任何依賴關系,它只依賴于MetadataReaderService接口,而該接口是抽象的,因而是相對穩定的。例如,我們考慮到系統中的元數據信息較為穩定,但卻可能被頻繁地讀取。為了提高系統的性能,有必要引入元數據信息的緩存。

  顯然,訪問元數據信息的實現發生了變化。它不再是直接通過數據訪問對象讀取存儲在數據庫中的元數據信息,而是先讀取內存中的緩存。如果緩存中的元數據已經存在,則直接返回,否則,訪問數據庫。現在,我們可以修改MetadataReaderServiceImpl的實現,卻不會影響到調用者QuerySqlStatement,因為MetadataReaderService接口沒有發生任何變化。

  不過,根據封裝的原理,即使沒有定義MetadataReaderService接口,在修改MetadataReaderServiceImpl類時,只要我們不改變它所公開的方法,即使調用者依賴于它,似乎也不會受到影響才對。這樣的推斷并沒有錯誤,錯在我們假設的前提。假若我們直接修改MetadataReaderServiceImpl類的實現,如果面對如下兩個問題,應該如何應對?
  1、 MetadataReaderServiceImpl類的源代碼不允許修改;
  2、 讀取元數據信息的其他調用者不需要緩存功能。

  第一個問題鎖上了修改的大門,第二個問題又添上了另一把鎖。它明確地告訴那些希望走捷徑的設計者:此路不通!或許,禁止修改源代碼的需求過于苛刻,除非使用第三方代碼;然而,第二個問題在實際的項目開發中確實存在。我們在實現元數據信息的編輯器時,同樣需要調用MetadataReaderServiceImpl類。由于編輯器主要針對元數據進行CRUD操作,因此,元數據信息的變化是很頻繁的,此時使用緩存,無異于畫蛇添足。

  根據OCP原則,好的設計應該對修改是封閉的,對擴展是開放的。接口的引入使得擴展成為可能。我們可以定義一個緩存對象去實現MetadataReaderService接口,它提供了緩存的功能。緩存對象除了擁有管理和訪問緩存的職責之外,它還需要讀取數據庫,以應付緩存未包含指定信息的情況。我們需要在緩存對象中再提供讀取數據庫的實現邏輯嗎?設計與開發切忌浪費,信奉“拿來主義”。在實現某個功能之前,首先應考察是否已有現成的實現,如果有,直接拿來重用即可。目前,MetadataReaderServiceImpl類已經實現了讀取數據庫的功能,因此,最簡單的辦法就是將其傳遞給緩存對象。考慮到MetadataReaderServiceImpl才是真正實現讀取的對象,而緩存對象不過是對其包裹了一層外衣而已,為了更好地表達對象的意圖,最好將MetadataReaderServiceImpl更名為RealMetadataReaderService,緩存對象則命名為CachedMetadataReaderService:image

 
public class CachedMetadataReaderService implements
MetadataReaderService{

private MetadataReaderService readerService;
private MetadataCacheManager cacheManager;

@Override

public MetadataObject getMetadataObject(String metadataName) {
Serializable obj
= cacheManager.getObjectCached(metadataName);
if(obj!=null){
return (MetadataObject) obj;
}
else{
MetadataObject metadataObject
= readerService.
getMetadataObject(metadataName);
cacheManager.putObjectCached(metadataName,
(Serializable) metadataObject);

return metadataObject;
}
}
}

  CachedMetadataReaderService類相當于RealMetadataReaderService的代理,由它決定是從緩存獲取,還是從數據庫中獲取。現在,我們可以讓調用者訪問CachedMetadataReaderService類。因為它與RealMetadataReaderService同屬于一個共同的接口,所以調用者察覺不到變化。為了解除創建具體對象的依賴,我們還可以使用IoC容器,利用依賴注入的方式注入調用者需要的對象。正是因為MetadataReaderService接口的存在,當面對需求發生變化時,我們只做了兩件事:擴展增加了CachedMetadataReaderService類;修改Spring的配置文件。對于原有的代碼,無論是服務的提供者還是調用者,都沒有受到任何影響。接口引入的間接性,無疑是能夠擁抱變化的。

0
0
 
標簽:OO
 
 

文章列表

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

    IT工程師數位筆記本

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