系統(tǒng)設(shè)計時經(jīng)常會遇到類似的需求:設(shè)計一個系統(tǒng),為公司內(nèi)(外)的其它系統(tǒng)提供XX服務(wù)(登錄、支付等,為了表述方便,下面以支付為例),這個系統(tǒng)需要對接其它若干支付系統(tǒng),如:對接支付寶,對接微信支付,對接網(wǎng)銀等。此處,我們把要設(shè)計的系統(tǒng)叫做XX網(wǎng)關(guān)系統(tǒng)。
一般設(shè)計方案
最直接的方法是,每對接一個新的支付,創(chuàng)建一套對應(yīng)代碼,從service、manager到dao完全寫一套,然后把新支付需要的參數(shù)直接暴露給調(diào)用方。
或者是對系統(tǒng)調(diào)用方提供一個請求參數(shù)的聚合體,同時增加一個支付類型參數(shù),每新增加一種支付,就把請求參數(shù)放到這個聚合體中。系統(tǒng)根據(jù)請求類型不同來篩選請求參數(shù)與處理方法。
存在的問題
上面的方案其實只是一種簡單的封裝,它存在以下問題:
1、每當(dāng)新增加一種支付,系統(tǒng)調(diào)用方都需要增加對應(yīng)的額外的參數(shù)與對應(yīng)的代碼,而且每個系統(tǒng)調(diào)用方都需要去實現(xiàn)一遍
2、在系統(tǒng)中存在大量冗余代碼,涉及到變更,很可能需求修改多處
總的來說就是難以維護(hù),維護(hù)工作量大
我們希望的是
一種擴(kuò)展靈活,易于維護(hù)的系統(tǒng),對于新的支付的接入,不需要開發(fā)大量的重復(fù)代碼,系統(tǒng)調(diào)用方也不需要做大量的改動,只需要少量改動即可(或者不改動)。
另一種設(shè)計
下面我來說一說另一種方案以及其實現(xiàn)步驟,大家仔細(xì)體會一下區(qū)別。
對外只提供一個調(diào)用接口,通過參數(shù)類型來區(qū)分不同的支付處理類,提取出支付必須的通用參數(shù)創(chuàng)建請求參數(shù),并增加支付類型參數(shù),提取公共實現(xiàn)類,設(shè)計模板方案,實現(xiàn)具體的支付類。
步驟
下面的代碼,只是為了對步驟進(jìn)行說明,沒有參數(shù)校驗與異常檢測,不能直接用于線上系統(tǒng)。
一、設(shè)計接口層
接口的目的之一就是定義外部通信的協(xié)議。
1、提供模塊調(diào)用的服務(wù),如,pay
2、提取支付的必須(公共)參數(shù)作為請求參數(shù)
3、提供支付類型參數(shù),用于區(qū)分交易類型(此類型可以作為查詢參數(shù)返回到客戶端側(cè),這樣的話,調(diào)用方就完全不用關(guān)心新增類型的事情了,讀者請自行思考)
二、提取抽象基類,完成公共代碼
1、完成公共代碼開發(fā),生成模板方法
類似如下:
abstract doSomethingA(); 抽象方法,需要子類實現(xiàn)
common method(); 通用的代碼邏輯,比如更新DB
abstract doSomethingB(); 抽象方法,需要子類實現(xiàn)
公共接口定義,如保存支付記錄,發(fā)起支付請求,為service層提供調(diào)用,其中g(shù)etPayTpe是為工廠提供調(diào)用的。
提取抽象方法,編寫公共代碼。注意此處泛型的使用。
支付工廠類實現(xiàn),通過getBeansOfType來加載所有實現(xiàn)了BasePayManager的類,放到內(nèi)存中。
三、編寫具體子類實現(xiàn)
定義具體支付的接口,用于編寫支付類特有方法。支付接口特有的參數(shù)在具體子類中隱藏掉,不對外暴露。
實現(xiàn)具體的支付功能
四、編寫PayService實現(xiàn)類
從工廠類獲取具體的支付處理類。調(diào)用BasePayManager的doPay方法。
類圖:
總結(jié)
此處我們使用了開閉原則,面向接口編程,模板方法等技巧,后續(xù)再增加新的支付,只需要編寫具體的實現(xiàn)類即可(XXManager),不會因為新的加入而影響原有功能。
最后說一點感悟,作為一個開發(fā)人員,我們要主動的去提高自己的競爭力,如果已經(jīng)寫了3年以上代碼了,還只停留在service,manager,dao層面上,這是很危險的,慢慢的就會被淘汰。因為這些即使是一個1年工作經(jīng)驗的人,只要做的足夠多,那么他也可以熟練掌握。而如果我們還只停留在這個層面上,沒有基類與進(jìn)步,那用不了多久就會失去競爭力。