国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

舌尖上的狀態機

wuaiqiu / 1437人閱讀

摘要:題記真的猛士,敢于不做設計,直接開始編碼面對業務系統中最復雜的部分狀態模型,有多少程序員就有多少種實現。聊聊狀態模型設計上常遇到的問題和解決的思路吧。

題記

真的猛士,敢于不做設計,直接開始編碼——面對業務系統中最復雜的部分:狀態模型,有多少程序員就有多少種實現。聊聊狀態模型設計上常遇到的問題和解決的思路吧。

正文

有時候我們想做一個富含業務行為,而又足夠通用的技術架構時,剛開始都是信心滿滿,采用各種設計方法,充分考慮未來的需求,畫出系統依賴、數據模型甚至核心類圖,上線時各種性能爆表或者擴展輕松;上線半年之后畫風一轉,代碼堆得到處都是,哪怕再小心維護依然無法逃離“一年一重構”的魔咒,兩年過后連測試同學的TC都寫不出來,發生了什么?

寫到這里又到晚上了,不湊巧零食都被清理干凈,餓得天昏地暗(⊙o⊙)…就設想一下這樣一個場景吧(下面的討論只做場景討論,并非真實業務系統的設計,請專業的同行們不要見怪。

接到一個炒菜機器人的項目,要求能夠按照吃貨的設計做出各種菜式

一分鐘速成方案

大家應該比較熟悉需求或者領域驅動的套路吧,抄起自上而下設計的錘子開始敲釘子:首先,我們的平臺中會有

廚具:各種廚具的基本使用接口和參數規范

菜譜:操作指導

先在腦海中預演一下這樣的設計是怎么運作的:

首先系統應該能夠認識各種不同的廚具,并且知道如何操作它們
案板:切菜程序
炒鍋:翻炒程序、油炸程序
燉鍋:水煮程序、焯水程序
攪拌機:攪拌程序
……
以及它們的清洗程序沒有一一列出

接下來是菜譜,就先來個番茄炒蛋吧

打蛋流程:使用攪拌機,調整參數使其能夠打出均勻的蛋液

番茄流程:使用案板,調整參數使其能夠切出合適的番茄塊

炒鍋流程:使用炒鍋,先放油,燒熱,放蛋,翻炒,放番茄,翻炒,加鹽

出鍋流程:使用盤子,出鍋

畢竟我們花了1分鐘設計出來的廚具+菜譜架構,看看再做幾個需求會變成什么樣子,需求方要求在能做番茄炒蛋的基礎上,做個辣椒絲炒蛋:

打蛋流程:使用攪拌機,調整參數使其能夠打出均勻的蛋液

辣椒流程:使用案板,調整參數使其能夠切出合適的辣椒絲

炒鍋流程:使用炒鍋,先放油,燒熱,放蛋,翻炒,放辣椒,翻炒,加鹽

出鍋流程:使用盤子,出鍋

做到這里,一些同學指出,多數雞蛋搭配的菜譜都是擁有四個標準流程節點:打蛋、切菜、翻炒、裝盤,而在切菜環節中,我們只需要調整參數類型和數值,就可以搭配出“*炒雞蛋”的菜色,至于翻炒環節相對麻煩一些,需要加入很多細粒度的操作才能做出適用于業務發展的擴展性來;接下來的工作重點,要放在翻炒流程的設計上,開放出盡可能多的SPI,讓第三方在我們這個平臺上共同實現翻炒市場。

系統上線半年,出現了各種業務分支:不僅原先官方提供的的番茄炒蛋和辣椒炒蛋獲得很好的市場反饋,微調參數就輕松支持了苦瓜炒蛋、木耳炒蛋甚至榴蓮炒蛋;業務方出現了:我們要開辟湯類市場,先從番茄蛋湯開始吧:

打蛋流程:使用攪拌機,調整參數使其能夠打出均勻的蛋液

切菜流程:使用案板,調整參數切出番茄丁和香蔥段

湯鍋流程:使用湯鍋,加水,燒熱,放蛋,加熱,放番茄,放蔥段,加鹽

出鍋流程:使用湯碗,出鍋

針對原來設計的四套流程,在切菜流程中加入了類似炒鍋流程的多操作支持,接下來又實現了一套全新的湯鍋流程,出鍋也做了些定制。

腦補一下接下來的紅燒肘子、魚香肉絲、清燉羊肉、回鍋肉該怎么實現吧(唉,快餓死了,話說好多程序員做飯都是好手,是真的吧?)




回過頭看看之前的實現,核心流程節點不一定只有4個,每個流程節點下面的子節點可能有多個,如果現在要針對業務方提出的這些葷菜做個重構,該怎么做?

將菜譜系統做成一個多維數組,就像這個樣子:
菜譜ID[子流程ID],然后分別實現這些流程節點并將它們存儲在這個菜譜表格中

看起來應該比較完善了吧,可程序員的第六感還是隱隱約約覺得有哪里不對勁,譬如,在番茄炒蛋和辣椒絲炒蛋這兩個大體相同的流程中,“翻炒”、“加鹽”這兩個節點真的是可復用的嗎?

沒錯,現實架構中往往沒這么簡單,因為

番茄炒蛋的湯多,鹽可以在最后加,也可以在打蛋的時候加,而辣椒炒蛋沒什么湯,鹽要在打蛋的時候加進去

好吧,在不影響流程系統的情況下,我們硬著頭皮在打蛋和炒鍋流程節點上加了個IF判斷(is 番茄炒蛋),然后就……中招了。

從邏輯來講,這一個小小的IF,將我們原先設計的三維數組變了個味,把流程圖畫出來可能是這樣的:

IF...ELSE分支就像小說里邊的二向箔,看起來像是沒有改變原有系統的邏輯,但可是可但是它可是混雜在源碼中而不是存在于配置中的邏輯,慢慢的,這種隨意的維護和簡單實現,開始模糊系統中的主子流程的邊界,接著模糊菜譜和主流程的邊界,將一個有層次的設計一步一步的煮成一鍋皮蛋瘦肉東北亂燉粥。寫代碼的時候很爽,做維護的時候罵娘

這是一個小小的開始,我們可以抱著取舍的心態說:我們可以通過編碼規范的方式要求開發人員在涉及主流程節點的邏輯上不允許使用IF分支來保護架構,只有細枝末節的流程可以使用不規范的編碼方式。

可現實往往沒有那么簡單,流程節點之間也不是完全沒有上下文依賴的情況,絕大多數采用狀態機架構的系統是不會用多維數組劃分狀態的(不信你可以去review代碼),通過邏輯分支搭建的橋梁,整個系統變成一個巨大的狀態機,那么一個高維的狀態機系統投影到單維的系統中會發生什么?(很多視頻網站上有個很好的教學系列《Dimensions》,有一部分內容關于如何通過球極投影理解四維空間)狀態機爆炸了,囧

這種實現的問題還不止于此,由于細粒度的流程是建立在廚具的維度上,而每種廚具對付不同的食材時,還是需要做很多定制化的工作,譬如:

打蛋器/打蛋碗是否能夠加鹽

鍋里倒油/倒水/倒醬油

菜板切圈/切片/切絲

就拿菜板舉例,胡蘿卜切片的手法,和包心菜切片的手法必然是不同的;習慣上對待這種問題的解決手段通常有兩種,一種是把切片的代碼放在菜板上實現,另一種是將復雜性下沉到各種食材上分別實現

如果在菜板上實現,我們就將獲得一個能夠加工天下食材的“超級菜板”,要么是個上帝類,要么是個錯綜復雜的巨型Service;

如果在食材上實現,為了能讓菜板接受各種不同類型食材作為輸入參數,我們很可能會在各種食材的上層抽象一個BaseEdible的基類好傳遞參數,然后要么在菜板上做switch邏輯,要么在BaseEdible中提供各種加工方法的實現,譬如切片/切絲,但粉條或者大米怎么切片?它和胡蘿卜除了都能吃以外還有什么共性?

也許有細心的同學開始考慮在食材上用Command方式來實現行為,這可能也是一種很糾結的做法,Debug成本暫且不說,一個幾乎可以發送所有命令的菜板加上一堆看起來什么命令都能接受的食材實現,怎么保證系統不會讓菜板去把面粉切個絲,也要做不少工作。

一個看上去很美好的設計,在實施的過程中很可能成為下面三者兼備的糟糕實現

狀態機爆炸

上帝類

過度繼承/無用代碼

這時候,比程序員先瘋掉的,大概是聽到程序員說“我做了個小改動,你們回歸一下”這句話的測試同學吧?

換個姿勢

怎樣才能讓這個系統像親愛的母上大人一樣,什么菜都會做呢?

回顧前面的設計,鍋碗瓢盆作為容器,它們本身其實沒有發生過任何變化,只是在盛有不同的食材時,樣子看起來有些不同。按照加工的流程設計狀態機踩了坑,按照容器狀態設計狀態機子節點也踩了坑,那么我們是不是一開始的出發點就跑偏了?

我們不妨換個角度來思考,《舌尖上的中國》教育我們:食材很重要,那么是不是可以從食材入手來設計這個系統?

剛才的設計都是以廚師的工作狀態為出發點做的:我們手上有各種工具,可以通過各種手段來加工食材。但考慮“做菜”這件事情本身,輸入的是食材,輸出的也是食材,真正發生狀態轉換的元素是工具嗎?顯然不是,雞蛋到蛋液到炒蛋這個過程中,鍋碗瓢盆沒有任何變化,從食材的角度入手,也是一個好玩的嘗試。

雞蛋蛋有很多加工方法,煮蛋,煎蛋,荷包蛋,蛋液
辣椒也有很多加工方法,辣椒絲,辣椒圈,辣椒片

根據每一種食材進行抽象,會有一個很好的附帶效果:每個狀態之間的變遷不可逆而且轉移條件沒有多態行為

碗 -> 洗干凈 -> 干凈的碗

雞蛋 -> 敲開,放入干凈的碗并用筷子攪動 -> 蛋液

雞蛋 -> 敲開,放入干凈的碗并用筷子攪動,加鹽,繼續攪動 -> 咸蛋液

鍋 -> 洗干凈 -> 干凈的鍋

干凈的鍋 -> 放油,加熱 -> 熱油鍋

蛋液 -> 放入熱油鍋,翻炒 -> 炒雞蛋

番茄 -> 切碎 -> 碎番茄

辣椒 -> 切絲 -> 辣椒絲

辣椒 -> 切圈 -> 辣椒圈

荷包蛋很難變回生雞蛋的樣子,對吧?話說還真有人能做到,不過即使能變回來也不影響整體的設計

這樣就有了材料的狀態機和轉移函數,之前的架構中,加工工具變成了Services或者Utils,鍋具仍然維持細粒度狀態機,但不再是接受各種食材的上帝組件,我們可以在這樣細粒度的狀態機模型上進行很細致的加工

在不同菜色的加工過程中,這些細粒度的狀態機節點和轉移函數其實都是可以完整復用的,列一個番茄炒蛋的上游流程圖,是不是變得清晰一些?

剩下的部分就不劇透了,設計的樂趣不就在這里嗎?真的餓到全身無力扯不動蛋啦!有機會大家直接討論下,因為類似的問題在交易、物流、工單系統中都有大量的實例,偶爾換個思路,收獲沒準不小滴。

炒蛋·交易核心實戰

在進入這個章節之前,我們再回顧一下,大家是否已經通過腦補,消滅了炒蛋系統中的那些問題設計?

上帝類

過度繼承

非原子的狀態機轉移函數

狀態機爆炸

如果大體上沒什么問題的話,咱們繼續向交易核心系統的設計上折騰起來!

發現了嗎,炒蛋和交易核心系統的架構也有許多相似之處,復雜業務系統編碼中,最困難的工作是:知道在什么情況下做什么事;我們從炒蛋的思路入手,以兩個交易場景看看能不能跑通

按照前文的套路,交易系統的設計同樣也可以例舉出兩種典型的設計路徑:
1、按照資金流、信息流、物流等人們直接感知到的交易元素,自上而下的設計
2、按照商品、資金、優惠券、紅包等交易物料,自下而上的設計

很多同學對于前者的思路應該比較熟悉了,我們在這里重點看一看怎樣以后者的路徑進行交易核心的設計,是否能夠找到一種可以適用于更多業務場景、健壯的交易架構

首先需要明確,交易的本質是什么:多個參與者按照約定,進行財物的轉移或在參與者之間發生服務行為

接下來就是如何在系統中體現這些轉移或者服務行為了,會計記賬法,在資產核算、資金審計等領域都有廣泛的應用,在我們的交易系統設計中,參照會計手段,對每個交易元素的狀態進行建模(主要體現在數據庫Schema上,本文篇幅所限就不展開介紹會計記賬在訂單存儲上的應用了,以后有機會再敘)

多帶帶抽出狀態機來看,用會計術語描述,可以把財物轉移或服務行為抽象成一個簡單的流程

[A]簽訂

[D]借方已履行

[C]貸方已確認

對于任何一種交易元素,無論是否采用了擔保交易或第三方介入服務的情況(如對于商品而言,從賣家發貨,經過快遞,到達買家的過程中:發貨及物流進行中的狀態為D,買家確認收貨為C)當然,完全可以引入更多復雜的細粒度狀態機。為了敘事簡便,在后面的表格中,我們都用A、D、C三種狀態來描述交易元素的轉移狀態。

在一個典型的一口價交易流程中,交易流程如下(默認全部都是擔保交易)

下單

買家已付款

賣家已發貨

買家確認收貨

交易成功

在最樸素的一口價交易流程中,就是買家賣家一手交錢一手交貨的過程,交易元素只有兩個

資金

商品

那么,在交易過程中,這兩種交易元素的狀態是如何變更的?

類型 下單 付款 發貨 收貨 成功
資金 A D D D C
商品 A A D C C

表格中,不難看出交易的各個環節中需要進行的操作

下單(錢A->D,引導買家付款)

買家已付款(貨A->D,引導賣家發貨)

賣家已發貨(貨D->C,引導買家確認收貨)

買家確認收貨(錢D->C,系統打款給賣家)

交易成功

就這樣,從最細粒度的狀態機入手,我們獲得了一個能夠直接明確表示每個State和Transition的設計原型

下面我們再找一個更復雜一點的場景入手

某烘焙供應商接入在線交易,由其分銷商以代理的方式引導用戶選購,用戶在分銷商頁面在線選購商品后,付定金,均分給分銷商及供應商,等店鋪備貨完成之后,通知買家付尾款給供應商,然后供應商發貨,買家收貨,完成交易

首先列出交易流程列表

下單

買家付定金,供應商與分銷商均分

供應商完成備貨

買家付尾款給供應商

供應商發貨

買家確認收貨

交易成功

再列出交易元素

分銷商傭金

供應商定金

供應商尾款

供應商備貨

供應商商品

列出交易流程狀態表格

類型 下單 定金 備貨 尾款 發貨 收貨 成功
傭金 A D D D D D C
定金 A D D D D D C
備貨 A A C C C C C
尾款 A A A D D D C
商品 A A A A A D C

當然也可以簡化一下

類型 下單 定金 備貨 尾款 發貨 收貨 成功
傭金 A D - - - - C
定金 A D - - - - C
備貨 A - DC - - - -
尾款 A - - D - - C
商品 A - - - - D C

如果需要支持使用優惠券的交易呢?再加一行就行

類型 下單 定金 備貨 尾款 發貨 收貨 成功
券(付款減) A D - - - - C
券(下單減) D - - - - - C

用這樣的細粒度表格,很輕松就可以獲得每個State(下單、付定金),以及State之間的Transitions,表格中,不同的列表示交易環節,而不同的行,則表示不同的交易元素

至于逆向流程的支持,其實也很簡單,因為表格中已經清晰的描述了每種交易元素的狀態,將交易元素的發起人和接收人互換,進一步區分交易元素

需要區分的交易元素主要有

平臺中轉或擔保類元素(如現金、紅包、優惠券)

不可退換元素(優惠券、充值卡)

實物(通常所說)

服務

按照交易元素的逆向特征來設計對應的逆向元素生成策略,就可以不用考慮太多細節,簡便的支持逆向流程

至此,我們獲得了一個通過細粒度狀態機表示的交易核心模塊,大家也可以再用其他的交易場景試著套用一下,看看有沒有比較好或者不適合的場景

在線上應用的設計上,還要進一步考慮一些其他的工程因素

如何進行狀態機編碼

如何借助TCC實現最終事務一致性

合約化的交易數據庫Schema設計

這些內容我們在以后的篇幅中慢慢探討吧 :)

小結

在很多復雜業務系統的設計中,往往因為建模角度的選取造成后續維護中的困難

在狀態機/流程引擎的設計上,建議考慮

節點之間的轉移函數是否多態?

節點本身是否多態?

節點是否清晰的映射了需求場景中那些真正發生改變的對象?

新增流程是否需要修改原有代碼?

在類層次設計上,建議考慮

是否存在無用代碼?

是否存在上帝類?

如果存在這些情況,就像蛋糕上的霉斑,看起來只有一星半點,但你敢吃長霉的食物嗎?

附錄 開閉原則

還記得“開閉原則”嗎,就一句話:系統(或者理解為系統中的類、模塊、函數)對于“擴展”應該開放,而對于“修改”應該是封閉的。

這里首先需要界定“擴展”的含義:在不改動原有系統代碼的情況下,新增一個類算不算?新增一個方法呢?如果將它們套用“修改”的語義,對于模塊而言新增一個類是修改,對于類而言新增方法也是修改。開閉原則的邊界似乎沒有那么清晰。

我們可以用一個更簡單的思路來界定開閉原則:是否違背了原有的設計初衷

繼承的問題

《重構》書中花了很大的篇幅向讀者介紹“代碼的壞味道”,有一個“Unnecessary Code”的說法,大致的意思是繼承體系的基類中存在下游子類不需要的行為,或者必須被糾正的情況,工作個三五年的朋友們都或多或少的遇到過某個類的所有子類都在復寫基類方法的情況吧。

在不少實用主義的架構文章中,都提過“使用組合來代替繼承”的觀點,其中流傳最廣的一個段子(抱歉我也不知道是不是真事)是:James Gosling 的某次演講會后Q&A環節中,有人問他,如果重新設計Java語言,你會做什么?JG回答說,我會干掉“類”,倒不是因為“類”本身有問題,而是會用實現(implements)來取代繼承(extends)

就平時項目的經歷而言,基類,尤其是業務系統中的各種基類,是非常難設計的。因為很難在業務剛開始的時候就預想到它最終(結束維護下線時)的樣子,也常常因為這樣,我們看到的大多數Base*命名的類時,除了限定參數類型,它的代碼行為和Object基本無異(譬如BaseCommand,BaseItem,BaseAction我去太多了),讓后續維護的同事邊罵邊寫代碼。

上帝類

又是來自《重構》的點子:“One Class to rule them all, and in the darkness bind them.”,聽起來有點像《魔戒》的臺詞哈,這個理解起來就不像為什么避免繼承那么糾結了,畢竟誰都不愿意維護一段三五千行而且看起來什么事情都能做的代碼吧?哦,有人用一個匯編文件寫了個操作系統,咱們的平臺看起來最多需要兩三個類就夠了。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/64817.html

相關文章

  • 2017-06-19 前端日報

    摘要:前端日報精選京東如何配合業務打造三端融合開發平臺第期聊聊組件開發的邊界把握和狀態驅動插件拾趣網站開發微信小程序實戰二全家桶開發的一個跨三端的應用掘金原生實現五子棋游戲前端學習中文譯年你應該了解的函數式編程個人文章的設 2017-06-19 前端日報 精選 京東618:如何配合業務打造JDReact三端融合開發平臺?【第970期】聊聊vue組件開發的邊界把握和狀態驅動webpack 插件...

    alighters 評論0 收藏0
  • 世界各國對于云計算大力投入,這是為什么?

    摘要:隨著世界各國對云計算的大力投入,以及行業內技術的快速發展,現階段整個產業公司均在進行云計算的產業整合。對于云計算的發展前景,各大互聯網巨頭都相當看好,紛紛為此調整公司的發展戰略,競相推出自己的云計算產品和服務。隨著世界各國對云計算的大力投入,以及IT行業內技術的快速發展,現階段整個IT產業公司均在進行云計算的產業整合。對于云計算的發展前景,各大互聯網巨頭都相當看好,紛紛為此調整公司的發展戰略...

    raledong 評論0 收藏0
  • 重新科學學音標-區別式學舌位(靠感覺和想象)

    摘要:和區別,前者發音時,舌頭位置不但不下拉,還要向上顎貼近,那么發出來音正確后者舌尖向下拉,發音正確,否則不對。 i:和I區別,前者發音時,舌頭位置不但不下拉,還要向上顎貼近,那么發出來音正確;后者舌尖向下拉,發音正確,否則不對。

    zengdongbao 評論0 收藏0
  • 狀態決定視圖——基于狀態的前端開發思考

    摘要:前端與狀態現在的前端開發中,對于狀態的管理是重中之重。有限狀態機那么如何更好的管理前端軟件的復雜度的狀態機思想給出了自己的答案。有限狀態機并不是一個復雜的概念簡單說,它有三個特征狀態總數是有限的。 前提 在現在的前端社區,關于MVVM、Model driven view 之類的概念,已經算是非常普及了。React/Vue 這類框架可以算是代表。而自己雖然有 React/Vue 的使用經...

    miya 評論0 收藏0
  • 從根本出發,探索區塊鏈共識的四個階段

    摘要:狀態機模型區塊鏈用許多的節點共同模擬了一臺多復本的狀態機。區塊鏈共識的四個階段第一階段是加入共識加入共識階段決定了什么樣的節點可以參與共識協議。第四階段是退出共識這是常常被忽略的部分。 在接下來的秘猿科技小課堂里,我們會從技術角度、經濟模型設計角度、以及共識角度來拆解 Nervos 加密經濟網絡中,底層公鏈 CKB 的設計理念。而本文將會作為技術角度核心設計 Cell 模型的預備文章,...

    pekonchan 評論0 收藏0

發表評論

0條評論

wuaiqiu

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<