本文是對這兩個文檔的總結:

in nek:狀態機退出方法?

zhuanlan.zhihu.com
圖標
in nek:不為天下先2?

zhuanlan.zhihu.com
圖標

目的是基於這兩個文檔的一些場景,進一步討論一下什麼樣的邏輯屬於設計文檔,什麼樣的邏輯屬於代碼邏輯。

我以前就寫過一些討論,談到「代碼不能代替設計文檔」,或者「這樣的邏輯不屬於設計文檔,它應該屬於編碼」。但我沒有更好的邏輯來更直接地說明這兩個結論的原因。但有了這兩個推演,我想我有更好的邏輯來描述這兩個問題了。

在上面這個狀態機退出方法的討論中,我給出了三個獨立的邏輯:

  1. 狀態機
  2. 把刺激通道化,排隊化(比如通過鎖或者排隊設計)
  3. 狀態退出過程保護

我認為,這些東西,就是「設計邏輯」。比如你定義系統有5個狀態,在每個情形下做什麼,然後看有沒有狀態的處理是不對的。

這種東西靠看代碼是看不出來的(除非你對著代碼重新畫一次),在代碼升級、修改的時候不看著這個狀態機,你代碼寫得對不對,你也是不能肯定的,它非常燒腦。

這種邏輯就屬於設計文檔。

反之,如果你寫,在abc_op1()的時候,先上A鎖,然後修改x變數,然後給T發一個信號,然後修改狀態到sx2,然後解A鎖。這個就不屬於設計文檔的邏輯了,因為這種東西和代碼的邏輯是重疊的,在設計文檔中寫好了,搬到代碼去都一樣,拿個Word,Latex之類的東西寫出來,還不如直接寫到代碼中呢,你寫成文檔,還需要同步,這完全喫飽了撐的,這就屬於浪費時間了。

同樣的,你寫對刺激排隊的策略,你說你把什麼行為看做是對狀態機的刺激,他們有哪些線程(包括中斷等)可以把刺激送進來,你用什麼鎖來保證進去以後相關的執行過程可以排隊。這個邏輯就屬於文檔。但你討論每個刺激函數怎麼寫,這個就不是設計了。這是編碼,就算不寫在.c,.go中,它們依然是代碼。

就算你進行抽象,比如abc_op[1..3]()的行為是這樣這樣……,abc_op[4..5]()的行為是那樣那樣。這仍屬於編碼。因為他們並不是獨立於代碼的建模。關鍵是這些邏輯寫在文檔中,沒有一個明確的邏輯鏈可以驗證它對不對。你既沒有說這些opX是你的操作的全集,也沒有說他們屬於哪個線程,也沒有和狀態機的維護關聯在一起。這裡就沒有獨立的邏輯鏈可以建。這個邏輯獨立到文檔中的意義在哪裡?它的邏輯對還是不對?不看代碼都不會知道。既然如此,把它寫出來,作為代碼之外的獨立推演,有什麼用?

其他的,我們總共有多少模塊,模塊間關係是什麼。有多少版本,版本間什麼關係。整個API系列是怎麼樣的,能否通過全部列出來,進行一個全局Review,看看是不是丟了一個維護介面導致API空間不自恰(比如有創建沒有釋放,有保存沒有恢復等)……這些邏輯,都屬於設計文檔。有時,流程確實也可以屬於文檔,只是那個流程非常關鍵,需要被獨立抽取出來,避免和代碼中其他邏輯混在一起無法辨認,等等。

設計文檔是補充編碼之外的獨立邏輯,常常還是在更高一層抽象,判斷全局的東西是否發生的缺失,不自恰,這一類問題的。

正如我們在《不為天下先2》中提到的,架構(高層)設計的目的不是下層設計的預演,它工作在另一層邏輯上,它是預期下一層設計是要加入新的邏輯的,但它為下一層設計設定另一層邏輯的限制,保證下一層設計可以聚焦,我們推演完了狀態機。下一層只要考慮我現在是從什麼跳到什麼上,不需要再擔心這樣跳法會不會出什麼問題。推演完鎖的原則,下一層設計只需要考慮設定的鎖的原則,什麼情況用什麼鎖,就可以保證不會發生死鎖,或者不會在某個流程中少用了鎖。每個邏輯屬於什麼層次的設計,其實大部分時候是有明顯的分界的,它們通常通過邏輯鏈的關聯度進行分層隔離,比如某個邏輯要素不在和任何人相關,它就會被獨立出來,它就會屬於那一層,這樣代碼纔有立體一說。否則就又都壓平了。

推薦閱讀:

相關文章