忍不了,客戶讓我在一個(gè)接口里兼容多種業(yè)務(wù)邏輯
故事
小貓的風(fēng)波已經(jīng)過去了,這幾天,小貓?jiān)诎舶残男牡財(cái)]著系統(tǒng)現(xiàn)狀方案,準(zhǔn)備著下次月會(huì)的分享。
這天,原本靜謐而又和諧的辦公室卻被開放平臺(tái)老六抱怨聲打破了。
“不改,別給我打電話了!說幾遍都沒用。這是一個(gè)研發(fā)的底線.....”
沒過一會(huì),產(chǎn)品老汪擔(dān)心老六對(duì)其"對(duì)臉開大",孫子似地提著杯咖啡找到了老六。老汪是明事理的產(chǎn)品經(jīng)理,為人處事兒這方面沒得說。
“這事兒,我也為難,兄弟,幫幫忙,來喝杯咖啡解解乏。我也知道這種客戶很難搞,但是我們是乙方,沒辦法,這年頭大環(huán)境擺在這里,賺錢不容易,大家互相體諒一下?!?/p>
老六接過老汪的咖啡,氣呼呼地抿了一口。
“上次發(fā)布商品的時(shí)候讓我把修改商品屬性和新增商品信息放到一個(gè)接口也就算了,這次還讓我干脆把上架到貨架直接包到一起?那后面我們這接口還咋維護(hù)了?后面是不是把商品添加到活動(dòng)中也往這一個(gè)接口上堆啊?你讓我到后面咋維護(hù)么?他們公司的lowb研發(fā)懂不懂軟件設(shè)計(jì)原則啊......”
產(chǎn)品老汪在旁邊連連點(diǎn)頭,"兄弟,消消氣,消消氣"。
“要不這樣吧,咱們拉上對(duì)面研發(fā)一起聊聊吧,看看雙方是否都可以讓讓步......”。
于是老汪和老六一起來到一間會(huì)議室,約客戶開始了在線會(huì)議。
單一職責(zé)原則
大家有沒有遇到老六一樣的遭遇。由于業(yè)務(wù)要求,接口或者某個(gè)模塊中耦合了太多可能不相干的事情。在這里你們是如何處理的呢?關(guān)于這點(diǎn)咱們要引出單一職責(zé)原則這樣一個(gè)軟件設(shè)計(jì)原則。
對(duì)于單一職責(zé)原則,官方術(shù)語:單一職責(zé)原則,英文縮寫SRP,全稱Single Responsibility Principle。There should never be more than one reason for a class to change。一個(gè)類或模塊應(yīng)該有且只有一個(gè)改變的原因。如果一個(gè)類擁有多個(gè)職責(zé),這些職責(zé)之間的耦合會(huì)導(dǎo)致系統(tǒng)變得不穩(wěn)定和難以維護(hù)。
在OOP里面,高內(nèi)聚、低耦合是軟件設(shè)計(jì)追求的目標(biāo),而單一職責(zé)原則可以看做是高內(nèi)聚、低耦合的引申,將職責(zé)定義為引起變化的原因,以提高內(nèi)聚性,以此來減少引起變化的原因。職責(zé)過多,可能引起變化的原因就越多,這將是導(dǎo)致職責(zé)依賴,相互之間就產(chǎn)生影響,從而極大的損傷其內(nèi)聚性和耦合度。單一職責(zé)通常意味著單一的功能,因此不要為類實(shí)現(xiàn)過多的功能點(diǎn),以保證實(shí)體只有一個(gè)引起它變化的原因。
可見無論從官方定義,還是對(duì)“單一職責(zé)”名稱的解釋,都能很好的理解單一職責(zé)原則的意義。其實(shí)在軟件設(shè)計(jì)中,要真正用好單一職責(zé)原則并不簡單。
老貓覺得如果需要遵循這樣的原則,最關(guān)鍵的地方還是在于職責(zé)的劃分。不過說到這個(gè)職責(zé)劃分又是比較偏向于業(yè)務(wù)性質(zhì)的,其和產(chǎn)品需求是分不開關(guān)系的。咱們就拿老六遇到的這個(gè)事情來分析一下。
一個(gè)發(fā)布商品的例子
說明:下面demo的表現(xiàn)形式,咱們都會(huì)用到類圖的方式,關(guān)于類圖的相關(guān)知識(shí)點(diǎn),大家有興趣可以看這里“類圖知識(shí)點(diǎn)”。
第一版
咱們一起看一下這個(gè)例子,如下圖:
接口
上面的圖中,我們看到了有一個(gè)發(fā)布商品的接口類以及實(shí)現(xiàn)。在其中,我們看到其中包含了發(fā)布商品的基礎(chǔ)信息,發(fā)布圖片信息,發(fā)布規(guī)格信息,將商品加入商品池,將商品加入售貨架,將商品加入某個(gè)活動(dòng)。
我們一起來看一下上述的設(shè)計(jì)是否存在問題?很多時(shí)候其實(shí)是有爭議的。
單一職責(zé)原則要求一個(gè)接口或類只有一個(gè)原因引起變化,也就是一個(gè)接口或類只有一個(gè)職責(zé),它就負(fù)責(zé)一件事情,原則上來說,單純從客戶角度,如果能保證客戶后續(xù)需求不會(huì)變更,以商品發(fā)布作為顆粒度,那么它是合理的。因?yàn)闃I(yè)務(wù)上已經(jīng)約定好,里面有商品屬性信息維護(hù),有商品行為信息維護(hù)。如果沒有新的業(yè)務(wù)概念提出來,頂多后續(xù)內(nèi)部改造的也就是屬性變更以及上下架和商品池維護(hù)變更。這種角度來說是合理的。
但是這種不變更的保證誰能擔(dān)保呢?另外接口也不是針對(duì)這一家客戶開放的,當(dāng)然考慮通用性。
第二版
這不沒多久業(yè)務(wù)又接了一家新客戶,他們的要求是發(fā)布商品就是商品信息的發(fā)布。剩余行為無需做強(qiáng)綁定,上下架行為由對(duì)方運(yùn)營人員選擇性執(zhí)行,沒必要新品一發(fā)就上架。那現(xiàn)在的這套就打又破了之前的單一原則。因?yàn)橛捎跇I(yè)務(wù)的要求,咱們要將行為拆分成下面這種模式:
第一次拆分
上面的業(yè)務(wù)看起來更加清晰一些,咱們把屬性同步設(shè)置單獨(dú)抽離,針對(duì)操作商品的行為也單獨(dú)封裝為另外一個(gè)行為接口。系統(tǒng)功能可拓展,接口可復(fù)用的角度來說,無論是第一個(gè)版本還是第二個(gè)版本,看起來都比較適用。這么一來,看起來兩個(gè)客戶的業(yè)務(wù)都遵循了單一職責(zé)的原則。雖然這種方案會(huì)引來第一個(gè)客戶的研發(fā)的不滿,因?yàn)閷?duì)于他們來說可能會(huì)調(diào)用兩次(當(dāng)然我們也可以通過門面模式將其整合,當(dāng)然這是后話),但是站在系統(tǒng)本身的設(shè)計(jì)角度來說,是比較合理的。但是這樣的一個(gè)抽取方式真的夠了么?
第三版
又來了一家客戶,由于對(duì)方公司有自己的運(yùn)營想法,對(duì)方不希望用我們的活動(dòng),他們希望有自己的活動(dòng),并且需要我們給其單獨(dú)定制,那么此時(shí)咱們又發(fā)現(xiàn),單一職責(zé)的這個(gè)設(shè)計(jì)原則又被打破了,因?yàn)槲覀冃枰槍?duì)活動(dòng)去做定制,為了遵循職責(zé)單一原則,所以這時(shí)候需要我們將活動(dòng)行為單獨(dú)剝離。然后就有了下面這樣的情況。
第三次拆分
這次的接口看起來更加靈活,滿足單一模式的同時(shí),滿足了以上所有的業(yè)務(wù)。但是這就夠了么?
顯然不夠,我們還是會(huì)遇到各種業(yè)務(wù)需求的變動(dòng),但是上述的抽取在當(dāng)前的業(yè)務(wù)下面看起來是比較適用的。能夠cover住大部分的場(chǎng)景了。對(duì)于后續(xù)的業(yè)務(wù)拓展也比較友好。
總結(jié)
上述的例子比較極端,老貓其實(shí)主要想和大家一件事情,所謂的單一職責(zé)的軟件設(shè)計(jì)模式并不是絕對(duì)的,我們會(huì)根據(jù)業(yè)務(wù)的需求形態(tài)做出動(dòng)態(tài)調(diào)整。如何遵循好單一職責(zé)的設(shè)計(jì)原則,其實(shí)還是需要我們能夠?qū)I(yè)務(wù)有一個(gè)比較精準(zhǔn)的領(lǐng)域劃分。小伙伴們,你們覺得呢?