從重構的角度學習bridge設計模式

liubing76發表於2003-05-16
從重構的角度學習bridge設計模式

Bridge模式是一個在實際系統中經常應用的模式。它最能體現設計模式的原則
針對介面進行程式設計,和使用聚合不使用繼承這兩個原則。


由於我們過分的使用繼承,使類的結構過於複雜,不易理解,難以維護。特別
是在Java中由於不能同時繼承多個類,這樣就會造成多層繼承,維護更難。
Bridge模式是解決多層繼承的根本原因。如果你在實現應用中一個類,需要繼承
兩個以上的類,並且這兩者之間又持有某種關係,它們兩個都會有多種變化。
Bridge模式是把這兩個類,分解為一個抽象,一個實現,使它們兩個分離,這樣
兩種類可以獨立的變化。
抽象就是,把一個實體的共同概念(相同的步驟),抽取出來(分解出幾個相互獨立的步驟),
作為一個過程。如我們把資料庫的 操作抽象為一個過程,有幾個步驟,建立SQL語句,
傳送到資料庫處理,取得結果。

實現就是怎樣完成這個抽象步驟,如傳送到資料庫,需要結合具體的資料庫,考慮怎樣完成這個步驟等。
並且同一步驟可能存在不同的實現,如對不同的資料庫需要不同的實現。

現在我們假設一個情況,也是WEB中經常遇到的,在一個page有輸入框,
如客戶資訊的姓名,地址等,輸入資訊後,然後按查詢按鈕,把查詢的結果顯示出來。
我們現在假設查詢客戶資訊和帳戶資訊,它們在不同的表中。
但是我們的系統面對兩種人群,總部的它們資訊儲存到oracle資料庫,但是各個分公司
的資料儲存在Sybase中,資料庫的位置等各不相同,這兩種的操作不同。

下面是我們一般首先會使用的方式,使用if else進行,判斷,這樣使用系統
難以維護,難以擴充套件,不妨你增加一種查詢,或者一種資料庫試試????
public class SearchAction(){
public Vector searchData(string ActionType,String DbType){
String SQL="";
if(ActionType.equal("查詢客戶資訊")){
//如果是查詢客戶資訊,拼SQL語句從客戶表中讀取資料
SQL="select * from Customer "

if(dbType.equal("oracle")){
//從總部資料庫讀取,資料庫為Oracle
String connect_string ="jdbc:oracle:thin:hr/hr@localhost:1521:HRDB";
DriverManager.registerDriver (new oracle.jdbc.OracleDriver());
Connection conn = DriverManager.getConnection (connect_string);
// Create a statement
Statement stmt = conn.createStatement ();
ResultSet rset = stmt.executeQuery (SQL);
//以下省略部分動態從資料庫中取出資料,組裝成Vector,返回
..................................
...................................
}else(dbType.equal("sybase")){
//從分公司資料庫讀取,資料庫為Sybase
String connect_string ="jdbc:sybase:Tds:cai/cai@192.168.1.12:1521:FIN";
DriverManager.registerDriver (new com.sybase.jdbc.SybDriver());
Connection conn = DriverManager.getConnection (connect_string);
// Create a statement
Statement stmt = conn.createStatement ();
ResultSet rset = stmt.executeQuery (SQL);
//以下省略部分動態從資料庫中取出資料,組裝成Vector,返回
..................................
...................................

}

}else if(ActionType.equal("查詢帳戶資訊")){
//如果是查詢帳戶資訊,拼接SQL語句從帳戶表中讀取資料
SQL="select * from Account "
if(dbType.equal("oracle")){
..........................
..........................
(作者注:此處省略從oracle讀取,約300字)

}else if(dbType.equal("Sybase")){
..........................
..........................
(作者注:此處省略從Sybase讀取,約300字)
}

}
}
}

如果你認為這寫的比較弱智,應該進行使用函式,但是你也會大量使用if else.???

於是我們進行重構,首先我們學習過DAO模式,就是把資料讀取進行分裡,我們定義一個
共同的介面,它負責資料庫的操作,然後根據不同的資料庫進行實現,在我們的查詢操作中,
使用介面,進行操作,這樣就可以不用考慮具體的實現,我們只管實現過程。

查詢共同介面:
public interface searchDB{
public Vector searchFromDB(String SQL)
}
Oracle資料庫的查詢實現
public class searchDBOracleImpl{
public Vector searchFromDB(String SQL){
//從總部資料庫讀取,資料庫為Oracle
String connect_string ="jdbc:oracle:thin:hr/hr@localhost:1521:HRDB";
DriverManager.registerDriver (new oracle.jdbc.OracleDriver());

ResultSet rset = stmt.executeQuery (SQL);
.............................
............................

}
}
Sybase資料庫的查詢實現
public class searchDBSysbaseImpl{
public Vector searchFromDB(String SQL){
//從分公司資料庫讀取,資料庫為Sysbase
String connect_string ="jdbc:sybase:Tds:cai/cai@192.168.1.12:1521:FIN";
DriverManager.registerDriver (new com.sybase.jdbc.SybDriver());
ResultSet rset = stmt.executeQuery (SQL);
.............................
............................

}
}

這樣在我們的查詢中就可以使用介面searchDB,但是建立有是一個問題,因為我們不能
靜態的確定,查詢的資料庫型別,必須動態確定,於是我們又想到使用簡單工廠方法,
來分別建立這裡的具體實現,根據類別,建立
public class searchFactory{
public static searchDB createSearch(int DBType){
if(DBType.equal("oracle")){
return searchDBOracleImpl();
}else if(DBType.equal("sybase")){
return searchDBSysbaseImpl();
}
}
}
於是我們的查詢程式碼可以改變為這樣了;
public class SearchAction(){

public Vector searchData(string ActionType,String DbType){
String SQL="";
if(ActionType.equal("查詢客戶資訊")){
//如果是查詢客戶資訊,拼SQL語句從客戶表中讀取資料
SQL="select * from Customer "
searchDB obj=searchFactory.createSearch(DbType);
return obj.searchFromDB(SQL);

}else if(ActionType.equal("查詢帳戶資訊")){
//如果是查詢帳戶資訊,拼接SQL語句從帳戶表中讀取資料
SQL="select * from Account "
searchDB obj=searchFactory.createSearch(DbType);
return obj.searchFromDB(SQL);
}
}
}
是不是簡單一些,如果增加一個新的資料庫,對我們只需增加一個新的資料庫實現便可,
老的程式碼,不需改變,這樣便實現開-閉原則(Open-closed原則),在我們的查詢查詢
中使用的是介面,這就是設計模式的原則,針對介面進行程式設計,並且使用聚合,而不是直接的繼承
大家,可以考慮使用繼承來完成該工作怎樣實現?????

上面是把實現進行分離,實現可以動態變化!!!!!

我們把查詢的操作的具體資料庫實現進行了分離,增強了靈活性,但是我們的查詢。
仍然使用了if else這樣仍然不易進行擴充套件,於是我們進行抽象一個查詢操作的過程,
把它分成幾個具體步驟,建立SQL語句,傳送到資料庫,執行查詢,返回結果。
它們雖然是不同的查詢,SQL各不相同,不同資料庫執行不同,返回結果的內容不同。但是
這個過程卻是不變的,於是我們宣告一個抽象類,來完成這個過程。

public abstract class searchAction{
searchDB obj;
//兩個步驟
public searchDB createSearchImple(int DbType){
return searchFactory.createSearch(DbType);
}
public abstract String createSQL();

//查詢過程,最後返回結果
public vector searchResult(int DbType){
obj=createSearchImple(DbType);
return obj.searchFromDB(createSQL())
}
}

//我們客戶查詢,操作
public class searchCustomerAction{
public String createSQL(){
return "select * from Customer"
}
}
//我們的帳戶查詢操作
public class searchAccountAction{
public String createSQL(){
return "select * from account"
}
}

這樣我們的查詢程式設計簡單的建立SQL語句,我們應該再建立一個工廠方法,
來完成建立它們
public class actionFactory{
public static searchAction ceateAction(int actionType){
if(actionType.equal("customer")){
return searchCustomerAction();
}else if(actionType.equal("account")){
return searchAccountAction();
}
}
}
這樣我們把查詢操作的過程進行了抽象,定義了步驟,和具體過程,經過我們的兩次改變
把抽象部分和實現部分進行分離,使他們都可以獨立的變化,增強靈活性。
我們再看當初查詢實現,現在經過這兩次的地修改,變成了什麼模樣?如下:

public class SearchAction(){
public Vector searchData(string ActionType,String DbType){
searchAction action=actionFactory.ceateAction(ActionType);
return action.searchResult(DbType);
}
現在假如增加一個資料庫型別,將會改變那些??,如果增加一種查詢操作需要改變那些???

討論點:
1:在我們的重構過程中,
怎樣使用設計模式原則的???
現在如果增加功能,遵循開閉原則嗎??
2:我們使用了兩個簡單工廠,這是為了簡化,一般最好使用抽象工廠方法,
如果改為抽象工廠,怎樣修改???



我打算寫一系列的文章介紹設計模式,
希望從重構的角度考慮模式的應用,而不是
直接介紹模式,這樣對初學者容易入門,step by step。
如果在自己的程式碼中遇到類似的情景,可以進行重構。


從重構學習proxy,預告
為什麼EJB有Home,remote,bean這三種角色?
為什麼又客戶端與容器進行互動??
介紹在EJB中的應用???

從重構學習decorator??

相關文章