學(xué)會(huì)這10個(gè)設(shè)計(jì)原則,離架構(gòu)師又進(jìn)了一步!!!
知識(shí)分享,以技會(huì)友。大家好,我是Tom哥。閱讀本文大約需要 15 分鐘。
閑言碎語(yǔ):
一個(gè)懂設(shè)計(jì)原則的程序猿,寫出來(lái)的代碼可擴(kuò)展性就是強(qiáng),后續(xù)的人看代碼如沐春風(fēng)。相反,如果代碼寫的跟流水賬似的,完全一根筋平鋪下來(lái),后續(xù)無(wú)論換誰(shuí)接手維護(hù)都要罵娘。
做軟件開發(fā)多年,CRUD仿佛已經(jīng)形成一種慣性,深入骨髓,按照常規(guī)的結(jié)構(gòu)拆分:表現(xiàn)層、業(yè)務(wù)邏輯層、數(shù)據(jù)持久層,一個(gè)功能只需要個(gè)把小時(shí)代碼就擼完了。
再結(jié)合CTRL+C和CTRL+V 絕世秘籍,一個(gè)個(gè)功能點(diǎn)便如同雨后春筍般被快速克隆實(shí)現(xiàn)。
是不是有種雄霸天下的感覺,管他什么業(yè)務(wù)場(chǎng)景,大爺我一梭到底,天下無(wú)敵!!!

可現(xiàn)實(shí)真的是這樣?
答案不言而喻!!!
初入軟件行業(yè),很多人都會(huì)經(jīng)歷這個(gè)階段。時(shí)間久了,很多人便產(chǎn)生困惑,能力并沒有隨著工作年限得到同比提升,焦慮失眠,如何改變現(xiàn)狀?
悟性高的人,很快能從一堆亂麻中找到線索,并不斷的提升自己的能力。
什么能力?
當(dāng)然是軟件架構(gòu)能力,一名優(yōu)秀的軟件架構(gòu)師,要具備復(fù)雜的業(yè)務(wù)系統(tǒng)的吞吐設(shè)計(jì)能力、抽象能力、擴(kuò)展能力、穩(wěn)定性。
如何培養(yǎng)這樣能力?

我將常用的軟件架構(gòu)原則,做了匯總,目錄如下:

千萬(wàn)不能教條主義,生搬硬套單一職責(zé)
我們?cè)诰幋a的時(shí)候,為了省事,總是喜歡在一個(gè)類中添加各種各樣的功能。未來(lái)業(yè)務(wù)迭代時(shí),再不斷的修改這個(gè)類,導(dǎo)致后續(xù)的維護(hù)成本很高,耦合性大。牽一發(fā)而動(dòng)全身。
為了解決這個(gè)問(wèn)題,我們?cè)诩軜?gòu)設(shè)計(jì)時(shí)通常會(huì)考慮單一職責(zé)
定義:
單一職責(zé)(SRP:Single Responsibility Principle),面向?qū)ο笪鍌€(gè)基本原則(SOLID)之一。每個(gè)功能只有一個(gè)職責(zé),這樣發(fā)生變化的原因也會(huì)只有一個(gè)。通過(guò)縮小職責(zé)范圍,盡量減少錯(cuò)誤的發(fā)生。
單一職責(zé)原則和一個(gè)類只干一件事之間,最大的差別就是,將變化納入了考量。
代碼要求:
一個(gè)接口、類、方法只負(fù)責(zé)一項(xiàng)職責(zé),簡(jiǎn)單清晰。
優(yōu)點(diǎn):
降低了類的復(fù)雜度,提高類的可讀性、可維護(hù)性。進(jìn)而提升系統(tǒng)的可維護(hù)性,降低變更引起的風(fēng)險(xiǎn)。
示例:
有一個(gè)用戶服務(wù)接口UserService,提供了用戶注冊(cè)、登錄、查詢個(gè)人信息的方法,主要還是圍繞用戶相關(guān)的服務(wù),看似合理。
public interface UserService{
// 注冊(cè)接口
Object register(Object param);
// 登錄接口
Object login(Object param);
// 查詢用戶信息
Object queryUserInfoById(Long uid);
}
過(guò)了幾天,業(yè)務(wù)方提了一個(gè)需求,用戶可以參加項(xiàng)目。簡(jiǎn)單的做法是在UserService類中增加一個(gè)joinProject()方法
又過(guò)了幾天,業(yè)務(wù)方又提了一個(gè)需求,統(tǒng)計(jì)一個(gè)用戶參加過(guò)多少個(gè)項(xiàng)目,我們是不是又在UserService類中增加一個(gè)countProject()方法。
這樣導(dǎo)致的后果是,UserService類的職責(zé)越來(lái)越重,類會(huì)不斷膨脹,內(nèi)部的實(shí)現(xiàn)會(huì)越來(lái)越復(fù)雜。既要負(fù)責(zé)用戶相關(guān)還有負(fù)責(zé)項(xiàng)目相關(guān),后續(xù)任何一塊業(yè)務(wù)變動(dòng),都會(huì)導(dǎo)致這個(gè)類的修改。
兩類不同的需求,都改到同一個(gè)類。正確做法是,把不同的需求引起的變動(dòng)拆分開,單獨(dú)構(gòu)建一個(gè)ProjectService類,專門負(fù)責(zé)項(xiàng)目相關(guān)的功能
public interface ProjectService{
// 加入一個(gè)項(xiàng)目
void addProject (Object param);
// 統(tǒng)計(jì)一個(gè)用戶參加過(guò)多少個(gè)項(xiàng)目
void countProject(Object param);
}
這樣帶來(lái)的好處是,用戶相關(guān)的需求只要改動(dòng)UserService。如果是項(xiàng)目管理的需求,只需要改動(dòng)ProjectService。二者各自變動(dòng)的理由就少了很多。
開閉原則
開閉原則(OCP:Open-Closed Principle),主要指一個(gè)類、方法、模塊 等 對(duì)擴(kuò)展開放,對(duì)修改關(guān)閉。簡(jiǎn)單來(lái)講,一個(gè)軟件實(shí)體應(yīng)該通過(guò)擴(kuò)展來(lái)實(shí)現(xiàn)變化,而不是通過(guò)修改已有的代碼來(lái)實(shí)現(xiàn)變化。
個(gè)人感覺,開閉原則在所有的原則中最重要,像我們耳熟能詳?shù)?3種設(shè)計(jì)模式,大部分都是遵循開閉原則,來(lái)解決代碼的擴(kuò)展性問(wèn)題。
實(shí)現(xiàn)思路:
采用抽象構(gòu)建框架主體,用實(shí)現(xiàn)擴(kuò)展細(xì)節(jié)。不同的業(yè)務(wù)采用不用的子類,盡量避免修改已有代碼。
優(yōu)點(diǎn):
可復(fù)用性好。在軟件完成以后,仍然可以對(duì)軟件進(jìn)行擴(kuò)展,加入新的功能,非常靈活。因此,這個(gè)軟件系統(tǒng)就可以通過(guò)不斷地增加新的組件,來(lái)滿足不斷變化的需求。 可維護(hù)性好。它的底層抽象相對(duì)固定,不用擔(dān)心軟件系統(tǒng)中原有組件的穩(wěn)定性,這就使變化中的軟件系統(tǒng)有一定的穩(wěn)定性和延續(xù)性。
示例:
比如有這樣一個(gè)業(yè)務(wù)場(chǎng)景,我們的電商支付平臺(tái),需要接入一些支付渠道,項(xiàng)目剛啟動(dòng)時(shí)由于時(shí)間緊張,我們只接入微信支付,那么我們的代碼這樣寫:
class WeixinPay {
public Object pay(Object requestParam) {
// 請(qǐng)求微信完成支付
// 省略。。。。
return new Object();
}
}
隨著業(yè)務(wù)擴(kuò)展,后期開始逐步接入一些其他的支付渠道,比如支付寶、云閃付、紅包支付、零錢包支付、積分支付等,要如何迭代?
class PayGateway {
public Object pay(Object requestParam) {
if(微信支付){
// 請(qǐng)求微信完成支付
// 省略。。。。
}esle if(支付寶){
// 請(qǐng)求支付寶完成支付
// 省略。。。。
}esle if(云閃付){
// 請(qǐng)求云閃付完成支付
// 省略。。。。
}
// 其他,不同渠道的個(gè)性化參數(shù)的抽取,轉(zhuǎn)換,適配
// 可能有些渠道一次支付需要多次接口請(qǐng)求,獲取一些前置準(zhǔn)備參數(shù)
// 省略。。。。
return new Object();
}
}
所有的業(yè)務(wù)邏輯都集中到一個(gè)方法中,每一個(gè)支付渠道本身的業(yè)務(wù)邏輯又相當(dāng)復(fù)雜,隨著更多支付渠道的接入,pay方法中的代碼邏輯會(huì)越來(lái)越重,維護(hù)性只會(huì)越來(lái)越差。每一次改動(dòng)都要回歸測(cè)試所有的支付渠道,勞民傷財(cái)。那么有沒有什么好的設(shè)計(jì)原則,來(lái)解決這個(gè)問(wèn)題。我們可以嘗試按開閉原則重新編排代碼
首先定義一個(gè)支付渠道的抽象接口類,把所有的支付渠道的骨架抽象出來(lái)。設(shè)計(jì)一系列的插入點(diǎn),并對(duì)若干插入點(diǎn)流程關(guān)聯(lián)。
關(guān)于插入點(diǎn),用過(guò)OpenResty的同學(xué)都知道,通過(guò)set_by_lua、rewrite_by_lua、body_filter_by_lua 等不同階段來(lái)處理請(qǐng)求在對(duì)應(yīng)階段的邏輯,有效的避免各種衍生問(wèn)題。
abstract class AbstractPayChannel {
public Object pay(Object requestParam) {
// 抽象方法
}
}
逐個(gè)實(shí)現(xiàn)不同支付渠道的子類,如:AliayPayChannel、WeixinPayChannel,每個(gè)渠道都是獨(dú)立的,后期如果做渠道升級(jí)維護(hù),只需修改對(duì)應(yīng)的子類即可,降低修改代碼的影響面。
class AliayPayChannel extends AbstractPayChannel{
public Object pay(Object requestParam) {
// 根據(jù)請(qǐng)求參數(shù),如果選擇支付寶支付,處理后續(xù)流程
// 支付寶處理
}
}
class WeixinPayChannel extends AbstractPayChannel{
public Object pay(Object requestParam) {
// 根據(jù)請(qǐng)求參數(shù),如果選擇微信支付,處理后續(xù)流程
// 微信處理
}
}
總調(diào)度入口,遍歷所有的支付渠道,根據(jù)requestParam里的參數(shù),判斷當(dāng)前渠道是否處理本次請(qǐng)求。
當(dāng)然,也有可能采用組合支付的方式,比如,紅包支付+微信支付,可以通過(guò)上下文參數(shù),傳遞一些中間態(tài)的數(shù)據(jù)。
class PayGateway {
List<AbstractPayChannel> payChannelList;
public Object pay(Object requestParam) {
for(AbstractPayChannel channel:payChannelList){
channel.pay(requestParam);
}
}
}
里氏替換
里氏替換原則(LSP:Liskov Substitution Principle):所有引用基類的地方必須能透明地使用其子類的對(duì)象
簡(jiǎn)單來(lái)講,子類可以擴(kuò)展父類的功能,但不能改變父類原有的功能(如:不能改變父類的入?yún)ⅲ祷兀嫦驅(qū)ο缶幊痰?/span>多態(tài)性類似。
多態(tài)是面向?qū)ο缶幊陶Z(yǔ)言的一種語(yǔ)法,是一種代碼實(shí)現(xiàn)的思路。而里氏替換是一種設(shè)計(jì)原則,是用來(lái)指導(dǎo)繼承關(guān)系中子類如何設(shè)計(jì),子類的設(shè)計(jì)要保證在替換父類的時(shí)候,不改變?cè)谐绦虻倪壿嬕约安黄茐脑谐绦虻恼_性。
實(shí)現(xiàn)思路:
子類可以實(shí)現(xiàn)父類的抽象方法 子類中可以增加自己特有的方法。 當(dāng)子類的方法重載父類的方法時(shí),方法的前置條件(即方法的形參)要比父類方法的輸入?yún)?shù)更寬松。 當(dāng)子類的方法實(shí)現(xiàn)父類的抽象方法時(shí),方法的后置條件(即方法的返回值)要比父類更嚴(yán)格。
接口隔離
接口隔離原則(ISP:Interface Segregation Principle)要求程序員盡量將臃腫龐大的接口拆分成更小的和更具體的接口,讓接口中只包含調(diào)用方感興趣的方法,而不應(yīng)該強(qiáng)迫調(diào)用方依賴它不需要的接口。
實(shí)現(xiàn)思路:
接口盡量小,但是要有限度。一個(gè)接口只服務(wù)于一個(gè)子模塊或業(yè)務(wù)邏輯。 為依賴接口的類定制服務(wù)。只提供調(diào)用者需要的方法,屏蔽不需要的方法。 結(jié)合業(yè)務(wù),因地制宜。每個(gè)項(xiàng)目或產(chǎn)品都有特定的環(huán)境因素,環(huán)境不同,接口拆分的標(biāo)準(zhǔn)就不同,需要我們有較強(qiáng)的業(yè)務(wù) sense 提高內(nèi)聚,減少對(duì)外交互。使接口用最少的方法去完成最多的事情。
示例:
用戶中心封裝了一套UserService接口,給上層調(diào)用(業(yè)務(wù)端以及管理后臺(tái))提供用戶基礎(chǔ)服務(wù)。
public interface UserService{
// 注冊(cè)接口
Object register(Object param);
// 登錄接口
Object login(Object param);
// 查詢用戶信息
Object queryUserInfoById(Long uid);
}
但隨著業(yè)務(wù)衍化,我們需要提供一個(gè)刪除用戶功能,常規(guī)的做法是直接在UserService接口中增加一個(gè)deleteById方法,比較簡(jiǎn)單。
但這樣會(huì)帶來(lái)一個(gè)安全隱患,如果該方法被普通權(quán)限的業(yè)務(wù)方誤調(diào)用,容易導(dǎo)致誤刪用戶,引發(fā)災(zāi)難。
如何避免這個(gè)問(wèn)題,我們可以采用接口隔離的原則
定義一個(gè)全新的接口服務(wù),并提供deleteById方法,BopsUserService接口只提供給Bops管理后臺(tái)系統(tǒng)使用。
public interface BopsUserService{
// 刪除用戶
Object deleteById(Long uid);
}
總結(jié)一下,在設(shè)計(jì)微服務(wù)接口時(shí),如果其中一些方法只限于部分調(diào)用者使用,我們可以將其拆分出來(lái),獨(dú)立封裝,而不是強(qiáng)迫所有的調(diào)用方都能看到它。
依賴倒置
軟件設(shè)計(jì)中的細(xì)節(jié)具有多變性,但是抽象相對(duì)穩(wěn)定,為了利用好這個(gè)特性,我們引入了依賴倒置原則。
依賴倒置原則(DIP:Dependence Inversion Principle):高層模塊不應(yīng)直接依賴低層模塊,二者應(yīng)依賴于抽象;抽象不應(yīng)該依賴實(shí)現(xiàn)細(xì)節(jié);而實(shí)現(xiàn)細(xì)節(jié)應(yīng)該依賴于抽象。
依賴倒置原則的主要思想是要面向接口編程,不要面向具體實(shí)現(xiàn)編程。
示例:
定義一個(gè)消息發(fā)送接口MessageSender,具體的實(shí)例Bean注入到Handler,觸發(fā)完成消息的發(fā)送。
interface MessageSender {
void send(Message message);
}
class Handler {
@Resource
private MessageSender sender;
void execute() {
sender.send(message);
}
}
假如消息的發(fā)送采用Kafka消息中間件,我們需要定義一個(gè)KafkaMessageSender實(shí)現(xiàn)類來(lái)實(shí)現(xiàn)具體的發(fā)送邏輯。
class KafkaMessageSender implements MessageSender {
private KafkaProducer producer;
public void send(final Message message) {
producer.send(new KafkaRecord<>("topic", message));
}
}
這樣實(shí)現(xiàn)的好處,將高層模塊與低層實(shí)現(xiàn)解耦開來(lái)。假如,后期公司升級(jí)消息中間件框架,采用Pulsar,我們只需要定義一個(gè)PulsarMessageSender類即可,借助Spring容器的@Resource會(huì)自動(dòng)將其Bean實(shí)例依賴注入。
優(yōu)點(diǎn):
降低類間的耦合性 提高系統(tǒng)的穩(wěn)定性 降低并行開發(fā)引起的風(fēng)險(xiǎn) 提高代碼的可讀性和可維護(hù)性
最后,要玩溜依賴倒置原則,必須要熟悉控制反轉(zhuǎn)和依賴注入,如果你是java后端,這兩個(gè)詞語(yǔ)你一定不陌生,Spring框架核心設(shè)計(jì)就是依賴這兩個(gè)原則。
簡(jiǎn)單原則
復(fù)雜系統(tǒng)的終極架構(gòu)思路就是化繁為簡(jiǎn),此簡(jiǎn)單非彼簡(jiǎn)單,簡(jiǎn)單意味著靈活性的無(wú)限擴(kuò)展,接下來(lái)我們來(lái)了解下這個(gè)簡(jiǎn)單原則。
簡(jiǎn)單原則(KISS:Keep It Simple and Stupid)。翻譯過(guò)來(lái),保持簡(jiǎn)單,保持愚蠢。
我們深入剖析下這個(gè) “簡(jiǎn)單”:
1、簡(jiǎn)單不等于簡(jiǎn)單設(shè)計(jì)或簡(jiǎn)單編程。軟件開發(fā)中,為了趕時(shí)間進(jìn)度,很多技術(shù)方案簡(jiǎn)化甚至沒有技術(shù)方案,認(rèn)為后面再找時(shí)間重構(gòu),編碼時(shí),風(fēng)格隨意,追求本次項(xiàng)目快速落地,導(dǎo)致欠下一大堆技術(shù)債。長(zhǎng)此以往,項(xiàng)目維護(hù)成本越來(lái)越高。
保持簡(jiǎn)單并不是只能做簡(jiǎn)單設(shè)計(jì)或簡(jiǎn)單編程,而是做設(shè)計(jì)或編程時(shí)要努力以最終產(chǎn)出簡(jiǎn)單為目標(biāo),過(guò)程可能非常復(fù)雜也沒關(guān)系。
2、簡(jiǎn)單不等于數(shù)量少。這兩者沒有必然聯(lián)系,代碼行少或者引入不熟悉的開源框架,看似簡(jiǎn)單,但可能引入更復(fù)雜的問(wèn)題。
如何寫出“簡(jiǎn)單”的代碼?
不要長(zhǎng)期進(jìn)行打補(bǔ)丁式的編碼 不要炫耀編程技巧 不要簡(jiǎn)單編程 不要過(guò)早優(yōu)化 要定期做 Code Review 要選擇合適的編碼規(guī)范 要適時(shí)重構(gòu) 要有目標(biāo)地逐漸優(yōu)化
最少原則
最少原則也稱迪米特法則(LoD:Law of Demeter)。迪米特法則定義只與你的直接朋友交談,不跟“陌生人”說(shuō)話。
如果兩個(gè)軟件實(shí)體無(wú)須直接通信,那么就不應(yīng)當(dāng)發(fā)生直接的相互調(diào)用,可以通過(guò)第三方轉(zhuǎn)發(fā)該調(diào)用。其目的是降低類之間的耦合度,提高模塊的相對(duì)獨(dú)立性。
核心思路:
一個(gè)類只應(yīng)該與它直接相關(guān)的類通信 每一個(gè)類應(yīng)該知道自己需要的最少知識(shí)
示例:
現(xiàn)在的軟件采用分層架構(gòu),比如常見的Web --> Service --> Dao 三層結(jié)構(gòu)。如果中間的Service層沒有什么業(yè)務(wù)邏輯,但是按照迪米特法則保持層之間的密切聯(lián)系,也要定義一個(gè)類,純粹用于Web層和Dao層之間的調(diào)用轉(zhuǎn)發(fā)。
這樣傳遞效率勢(shì)必低下,而且存在大量代碼冗余。面對(duì)此問(wèn)題,我們需靈活應(yīng)對(duì),早期可以允許Web層直接調(diào)用Dao。后面隨著業(yè)務(wù)復(fù)雜度的提高,我們可以慢慢將Controller中的重業(yè)務(wù)邏輯收攏沉淀到Service層中。隨著架構(gòu)的衍化,清晰的分層開始慢慢沉淀下來(lái)。
寫在最后,迪米特法則關(guān)心局部簡(jiǎn)化,這樣很容易忽視整體的簡(jiǎn)化。
表達(dá)原則
代碼的可維護(hù)性也是考驗(yàn)工程師能力的一個(gè)重要標(biāo)準(zhǔn)。試問(wèn)一個(gè)人寫的代碼,每次code review時(shí)都是一堆問(wèn)題,你會(huì)覺得他靠譜嗎?
這時(shí)候我們就需要引入一個(gè)表達(dá)原則。
表達(dá)原則(Program Intently and Expressively,簡(jiǎn)稱 PIE),起源于敏捷編程,是指編程時(shí)應(yīng)該有清晰的編程意圖,并通過(guò)代碼明確地表達(dá)出來(lái)。
表達(dá)原則的核心思想:代碼即文檔,通過(guò)代碼清晰地表達(dá)我們的真實(shí)意圖。
那么如何提高代碼的可讀性?
1、優(yōu)化代碼表現(xiàn)形式
無(wú)論是變量名、類名還是方法名,要命名合理,要能清晰準(zhǔn)確的表達(dá)含義。再配合一定的中文注釋,基本不用看設(shè)計(jì)文檔就能快速的熟悉項(xiàng)目代碼,理解原作者的意圖。
2、改進(jìn)控制流和邏輯
控制嵌套代碼的深度,比如if else的深度最好不要超多三層。外層最好提前做否定式判斷,提前終止操作或返回。這樣的代碼邏輯清晰。下面示例便是正確的處理:
public List<User> getStudents(int uid) {
List<User> result = new ArrayList<>();
User user = getUserByUid(uid);
if (null == user) {
System.out.println("獲取員工信息失敗");
return result;
}
Manager manager = user.getManager();
if (null == manager) {
System.out.println("獲取領(lǐng)導(dǎo)信息失敗");
return result;
}
List<User> users = manager.getUsers();
if (null == users || users.size() == 0) {
System.out.println("獲取員工列表失敗");
return result;
}
for (User user1 : users) {
if (user1.getAge() > 35 && "MALE".equals(user1.getSex())) {
result.add(user1);
}
}
return result;
}
分離原則
天下大事,分久必合合久必分。面對(duì)復(fù)雜的問(wèn)題,考慮人腦的處理能力有限,有效的解決方案,就是大事化小,小事化了,將復(fù)雜問(wèn)題拆分為若干個(gè)小問(wèn)題,通過(guò)解決小問(wèn)題進(jìn)而解決大問(wèn)題。
分離的核心思路:
1、架構(gòu)視角
結(jié)合業(yè)務(wù)場(chǎng)景對(duì)整個(gè)系統(tǒng)內(nèi)若干組件進(jìn)行邊界劃分,如,層與層(MVC)、模塊與模塊、服務(wù)與服務(wù)等。像現(xiàn)在流行的DDD領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)指導(dǎo)的微服務(wù)就是一種很好的拆解方式,通過(guò)水平分離的策略達(dá)到服務(wù)與服務(wù)之間的分離。

架構(gòu)設(shè)計(jì)視角下的關(guān)注點(diǎn)分離更重視組件之間的分離,并通過(guò)一定的通信策略來(lái)保證架構(gòu)內(nèi)各個(gè)組件間的相互引用。
2、編碼視角
編碼視角主要側(cè)重于某個(gè)具體類或方法間的邊界劃分。比如Stream流的filter、map、limit,數(shù)據(jù)集在不同階段按照不同的邏輯處理,并將輸出內(nèi)容作為下一個(gè)方法的輸入,當(dāng)所有的流程處理完后,最后匯總結(jié)果。
一些不錯(cuò)分層案例:
1、MVC模型
2、網(wǎng)絡(luò) OSI 七層模型
一個(gè)好的架構(gòu)一定具有不錯(cuò)的分層,各層之間通過(guò)定義好的規(guī)范通訊 ,一旦系統(tǒng)中的某一部分發(fā)生了改變,并不會(huì)影響其他部分(前提,系統(tǒng)容錯(cuò)做的足夠好)。
契約原則
天下事無(wú)規(guī)矩不成方圓,軟件架構(gòu)也是一樣道理。動(dòng)輒千日的大項(xiàng)目,如何分工協(xié)作,保證大家的工作能有條不紊的向前推進(jìn),靠的就是契約原則。
契約式原則(DbC:Design by Contract)。軟件設(shè)計(jì)時(shí)應(yīng)該為軟件組件定義一種精確和可驗(yàn)證的接口規(guī)范,這種規(guī)范要包括使用的預(yù)置條件、后置條件和不變條件,用來(lái)擴(kuò)展普通抽象數(shù)據(jù)類型的定義。
契約原則關(guān)注重點(diǎn):
API 必須要保證輸入是接收者期望的輸入?yún)?shù) API 必須要保證輸出結(jié)果的正確性 API 必須要保持處理過(guò)程中的一致性。如果一個(gè)API被二次修改后,整個(gè)集群的服務(wù)器都要重新部署,保證服務(wù)能力狀態(tài)的一致。
如何做好 API 接口設(shè)計(jì)?
1、接口職責(zé)分離。設(shè)計(jì) API 的時(shí)候,應(yīng)該盡量讓每一個(gè) API 只做一個(gè)職責(zé)的事情,保證API的簡(jiǎn)單和穩(wěn)定性。避免相互干擾。
2、 API 命名。通過(guò)命名基本能猜出接口的功能,另外盡量使用小寫英文
3、接口具有冪等性。當(dāng)一個(gè)操作執(zhí)行多次所產(chǎn)生的影響與一次執(zhí)行的影響相同
4、安全策略。如果API是外部使用,要考慮黑客攻擊、接口濫用,比如采用限流策略。
5、版本管理。API發(fā)布后不可能一成不變,很可能因?yàn)樯?jí)導(dǎo)致新、舊版本的兼容性問(wèn)題,解決辦法就是對(duì)API 進(jìn)行版本控制和管理。
寫在最后
軟件架構(gòu)原則的核心精髓,盡可能把變的部分和不變的部分分開,讓不變的部分穩(wěn)定下來(lái)。我們知道,模型是相對(duì)穩(wěn)定的,實(shí)現(xiàn)細(xì)節(jié)則是容易變動(dòng)的部分。所以,構(gòu)建出一個(gè)穩(wěn)定的模型層,對(duì)任何一個(gè)系統(tǒng)而言,都是至關(guān)重要的。
歡迎關(guān)注微信公眾號(hào):互聯(lián)網(wǎng)全棧架構(gòu),收取更多有價(jià)值的信息。
