業務、設計模式、算法

前言

好久沒有寫心得,一來是懶了,二來是難。早就很想寫一點有關業務邏輯和模式、算法的關係。能夠找到不少理論,但卻不多有理論實際相結合的文章。以致於許多人認爲服務端代碼就是CRUD,算法無用,設計模式無用。還有一種人他們認爲設計模式已經融入本身平常的開發中,不須要特別去在乎。這兩種狀況我都經歷過,在寫了這麼多年代碼後才意識到,本身思考的仍是太淺。這篇文章就算是拋磚引玉吧。算法

抽象

抽象是從衆多的事物中抽取出共同的、本質性的特徵,而捨棄其非本質的特徵的過程。數據庫

在編碼工做過程當中,咱們或多或少、有意無心的會進行一些抽象,其中最多見的,就是將事物轉爲變量。一個簡單的變量就能夠完成大量的抽象,對於一個書店,設計一個「book」變量,就能夠表明全部的書。咱們換一下它的名字改成「goods」,它就能表明任何能夠銷售的商品,而不只僅是書。這一切看起來很是簡單,太簡單了,那咱們怎麼會在最後要重構代碼,技術迭代趕不上業務發展,業務愈來愈好,代碼越寫越苦?設計模式

本質

在寫代碼的時候,尤爲是互聯網常見的服務端業務代碼,一般是將現實行爲作數字化的過程。例如在超市結算的時候,交錢,拿貨走人,在淘寶上要復現這個行爲,就得將錢和商品數字化,能夠被代碼表達。那麼,萬一有人要插隊怎麼辦?萬一網銀忽然沒法支付怎麼辦?萬一商品中有贈品怎麼判斷,有活動商品怎麼判斷,臨時有商品由於碼掃不出(相似缺貨)怎麼辦,所有都須要考慮,而這些現實中可能發生的問題,在線上也幾乎都會發生,代碼就要考慮。架構

無窮大的問題

那麼理論上有多少問題須要考慮呢?這讓我回想起第一節機率論的課上,老師問的一個問題:2我的約好10點見面,10點同時到達的機率是多少?由於到達的狀況是無窮大,因此同時到達機率是0。這個問題也同樣,現實中有無窮多種問題須要考慮,而這正是咱們重構噩夢的開始。看過《銀河系漫遊指南》的同窗應該知道有個「沉思」的超級電腦,它爲了計算出宇宙的究極問題,設計了「地球」這個更增強大的電腦。事情就是這樣,你要用程序表達這個世界,你須要的資源就是這個世界自己,除非你能從更高的緯度空間來設計。函數

妥協

既然咱們不可能表達完整的世界,咱們就須要想辦法去表達部分。微服務

骨架

現代科學能夠根據恐龍的骨骼化石來估計肌肉的走向和大小,從而還原它的外形。咱們不能表達整個世界,但能夠想辦法表達它的骨架。選擇性的表達是最多見的手段,它的重點在於:骨架要在它準確的位置上,不要由於肌肉的缺失而偏離。例如錢,不管在數字錢包的時代仍是紙幣的時代,它在生產生活中的價值是不變的。咱們就認爲咱們找對了骨架:價值。
咱們能夠用 y = ax + b表達任意一條實數域內的直線,可是現實中沒有哪條線是直的,但」直「這件事做爲骨架是對的。編碼

程序就是這個世界的「骨架」。設計

邊界

不少問題在有限的邊界內是是有限的,好比人際關係,在一代直系血親中,只會出現父母和子女,可是擴展到整我的類社會,會出現父親的叔叔,父親的叔叔的爺爺,父親的叔叔的爺爺的老婆的孃家。。。經過邊界約束問題範圍,正是不少DDD設計和微服務架構建設的目的。只不過微服務是一種「死緩」的作法,而DDD只是用於幫助思考,不直接解決問題。爲何說微服務是死緩?不少微服務在一開始是「微」服務,隨着業務複雜,就變成「腫服務」。拆分?很差意思,業務等不起。code

邊界的合理性

當咱們給問題畫上了邊界,怎麼才知道邊界是合理的?合理的邊界應該是:索引

  1. 邊界內元素能夠被完整描述,也就是全集。
  2. 邊界內元素是互斥的。

完整的描述有2種方法

枚舉

好比:邊界 = 性別,值 = [男,女,男改女,女改男,男改女又改男...],每次提到性別這個屬性,總有那麼幾個壞小子要懟我,雖然不是惡意,可是也說明這個邊界有點問題。
邊界改 = 當前性別,值 = [男,女]。
看,經過「當前」2個字修飾的性別,就好多了。

使用公式

以前直線上的點已經說明了這種方法,而公式描述又能夠經過分段函數、取樣函數進一步將邊界合理化,好比年齡,只有正整數。

互斥

被描述的內容在邊界的約束下,元素是惟一的,例如年齡,1歲和2歲就是互斥。若是要描述一羣人的年齡,每每會有同歲的,那麼邊界約束就要增長一個:某人。人在人類現實社會的邊界內,是不會重複的,而一我的的當前年齡只有一個,因此一羣人中某我的的年齡是惟一的。年齡 + 人 共同約束了一個邊界範圍,就像分段函數有多個約束條件。你能夠看到,慢慢的咱們發如今使用數學方法來描述問題,而這些數學方法的能夠經過「算法」最終實現,這就是算法的做用。

妥協後的抽象

有點經驗的研發很快就會發現,上面的例子反應在關係型數據庫中,就是惟一索引和複合惟一索引,這沒什麼稀奇的。接下來我要舉一個栗子來講它到底怎麼毀了咱們幸福的coding生活。有一個項目,要研究不一樣的食草動物吃什麼植物。研發團隊根據OOP思想,抽象了動物,動物行爲,植物。又把植物和動物都抽象爲「生物」以描述共性,完美,開始寫代碼:

動物->吃(植物);//return 要死 or 健康;

好了,一切都很正常,微生物要吃什麼,食肉動物要怎麼吃都沒有問題。還有點擴展性,不錯。

同時,在隔壁的一個部門,接了另外一個需求,他們要研究植物,以及他們能夠做爲那些動物的食物,因而他們寫了另外一種代碼:

植物->被(動物)->吃();//return 能夠 or 不能夠;

老闆以爲兩個部門作的有點重複,大家合併吧,反正都是吃,爲啥要寫兩個?兩邊研發團隊當晚打了一架,要求對方放棄本身的方案。最終動物研究部門由於體力比較好,植物研究部門放棄了本身的方案。次日老闆走進辦公室說:咱們今天研究一種植物叫豬籠草,它吃動物。。。動物部門一咬牙,行,我還有辦法,凡是生物它都有吃,他們能夠相互吃。。。
新的抽象

生物->吃(生物);//return 能夠 or 不能夠;

到這裏,咱們纔看到了真正的骨架,研發團隊發現以前的邊界都是有缺陷的,動物、植物都只是生物圈的一部分,這還沒囊括微生物。重構問題的發生,每每是咱們沒有找到骨架,沒有摸清邊界。在咱們研發的過程中,太多人喜歡寫「動物->吃(植物);」這樣的代碼,很簡單,也知足業務當前需求,若是抽象到「生物->吃(生物);//return 能夠 or 不能夠;」即可能被戴上「過分設計」的帽子。長此以往,你們就怕了。

沿着骨架重構是容易的

若是你在紙上畫了一條短一點的直線,若是須要一條長直線,延長即可,這很容易。若是一開始畫的是弧線,要獲得一條長的直線,你就得擦乾淨重畫(重構),或者換個地方(重寫)。以生物行爲研究爲例,若是一開始就把吃的行爲賦予「生物」這個基礎類,而後只實現動物的「吃」,而不關心植物的吃,好比豬籠草。這就下降了研發壓力,後來出現豬籠草的需求,在植物類中覆蓋掉吃方法,植物就有不一樣的吃法了,微生物也同樣。被吃是一個道理,食物鏈頂端也會被微生物吃掉,一樣能夠放在生物類中。這樣咱們的代碼就是按部就班的,可不斷疊加。

設計模式

終於說到設計模式,幾乎全部的設計模式,都在幫助咱們解決豬籠草問題,只是方式不一樣罷了。可是他們不能解決邊界劃分和骨架尋找問題。這就是爲何不少人以爲,用了設計模式,擴展性也不是很好。那麼說DDD,DDD實際上是在提醒你,你要去尋找邊界和骨架,它也不是告訴你如何去找。以前有個研究DDD的大牛來分享怎麼劃分邊界,有一個簡化版工序,大概有19道吧,反正我是記不得,總之不是一件簡單的事情。

總結

「業務「分析來獲得抽象,分析的方法就是畫邊界和抽骨架的過程,方法我也就那麼點都寫在上面了。 而」抽象「須要數學來描述,」數學」須要算法來編碼,」編碼「須要模式來避免重構。

相關文章
相關標籤/搜索