原文首發於我的部落格:kaolengmian7.com/posts/adapter.htm...
示例程式碼已經上傳到我的 Github:github.com/kaolengmian7/golang-dem...,可以把程式碼拉下來跑一跑~
核心思想
介面卡是兩個不相容的介面之間的橋樑,使介面不相容的物件能夠相互合作。
使用場景
有動機地修改一個正常執行的系統的介面,這時應該考慮使用介面卡模式。
1. 封裝有缺陷的介面
2. 封裝多個介面
3. 替換依賴
4. 相容老版本介面
5. 適配不同格式資料
一定要注意:介面卡為的是解決正在服役的專案的問題,而不是在設計階段新增的。如果在設計階段就考慮使用介面卡模式,那這個設計一定不合格。
實戰
假如你正在開發一款股票市場監測程式, 它會從不同來源下載 XML 格式的股票資料, 然後向使用者呈現出美觀的圖表。
type XmlHandler interface {
ProcessXml(xml string)}
type xmlHandler struct{}
func (x *xmlHandler) ProcessXml(xml string) {
log.Printf("xml:%s processing", xml)}
在開發過程中, 你決定在程式中整合一個第三方智慧分析函式庫。 但是遇到了一個問題, 那就是分析函式庫只相容 JSON 格式的資料。
// 分析庫介面
type JsonHandler interface {
ProcessJson(json []byte)}
type jsonHandler struct {
}
func (j *jsonHandler) ProcessJson(json []byte) {
log.Printf("json processing")}
你可以修改函式庫來支援 XML。 但是, 這可能需要大量程式碼。 或者,我們可以將 XML 轉換成 JSON?
xml 與 json 代表了現實中不相容的兩個介面。
為了演示方便,我用string型別
表示xml檔案
,用[]byte
表示json檔案
介面卡就是為兩個相容兩個介面而生,在此例中:xmlAdapter
繼承了XmlHandler介面
,其中封裝了對介面的處理。結果就是,客戶端的 JSON 處理被“適配”成 XML 處理。
type xmlAdapter struct {
JsonHandler}
func (x *xmlAdapter) ProcessXml(xml string) {
// 模擬物件轉換:xml -> json
json := []byte(xml) // 處理 json 檔案
x.ProcessJson(json)}
func TestAdapter(t *testing.T) {
inputJson := []byte("json_file") // 原客戶端
handler := &jsonHandler{} handler.ProcessJson(inputJson)
// 客戶端接入介面卡,利用 原有的 JsonHandler 處理 xml 檔案。
inputXml := "xml_file" adapter := &xmlAdapter{&jsonHandler{}} adapter.ProcessXml(inputXml)}
優點
靈活性強,上線新功能不需要改動大量原始碼,僅需要在介面層做一層型別轉換。
缺點
過多地使用介面卡,會讓系統非常零亂。比如,表明上看到呼叫的是 A 介面,其實內部被適配成了 B 介面的實現。
一個系統如果太多出現這種情況,無異於一場災難。因此如果不是很有必要,儘量不使用介面卡,而是直接對系統進行重構。
更進一步:加強版介面卡
對於 Golang 這種可以實現多繼承的語言來講,介面卡有更優雅的解決方案:讓介面卡實現所有介面。
type adapter struct {
jsonHandler JsonHandler xmlHandler XmlHandler}
這樣的介面卡強大的多,想用誰的實現就用誰的實現,與此同時完全規避了上面提到的缺點,不容易產生誤解。
func TestAdapterInGolang(t *testing.T) {
inputJson := []byte("json_file") inputXml := "xml_file" // 原客戶端
handler := &jsonHandler{} handler.ProcessJson(inputJson)
// 客戶端接入介面卡
adapter := &adapter{&jsonHandler{}, &xmlHandler{}} adapter.jsonHandler.ProcessJson(inputJson) // 想用 json 用 json adapter.xmlHandler.ProcessXml(inputXml) // 想用 xml 用 xml}
也可以重寫函式:
type adapter struct {}
func (x *adapter) ProcessJson(json []byte) {
// 自定義處理邏輯
}
func (x *adapter) ProcessXml(xml string) {
// 自定義處理邏輯
}
func TestAdapterInGolang(t *testing.T) {
inputJson := []byte("json_file") inputXml := "xml_file" // 原客戶端
handler := &jsonHandler{} handler.ProcessJson(inputJson)
// 客戶端接入介面卡
adapter := &adapter{&jsonHandler{}, &xmlHandler{}} adapter.ProcessJson(inputJson) // 想用 json 用 json adapter.ProcessXml(inputXml) // 想用 xml 用 xml}
參考:
本作品採用《CC 協議》,轉載必須註明作者和本文連結