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