軟件開發:組織大規模邏輯的技藝

技藝永恆,生命短暫,機會易逝,實驗莫測,抉擇艱難。

程序員是喫“邏輯”這碗飯的。那麼,怎麼才能安心地喫這碗飯呢?你需要掌握組織大規模邏輯的技藝。

要掌握這門技藝,需要有一些思想方法論來指導。本文談談,軟件業界是如何應對組織大規模邏輯的。

思想

結構化抽象

軟件,本質上是一種可動態而彈性變化的邏輯裝置。結構化,即是將邏輯進行抽象、提煉、分離、聚合,構建成更加縝密、動態、彈性的結構流。

結構化抽象,是軟件設計與開發的第一性原理。軟件開發與設計上的任何問題,如果找不到現成的方法,就可以迴歸到結構化思想與方法論上,通過調整結構來解決問題。所謂“術不成,則返道溯源”。

從單核到多核,是結構化調整;從同步到異步,是結構化調整;至於各種架構模式和設計模式,更是結構模式的固化。可見,結構化思想與方法,是從事軟件設計與開發的第一大法。

詳閱:“軟件設計的第一性原理:結構化抽象”。

關注點分離

要編寫大規模邏輯還保持整潔的秩序,需要做好關注點分離。

我深認爲,軟件中蘊藏着不計其數的大大小小的關注點。軟件開發和設計的本質,就是將關注點分離、組織、連接。 能夠將不同的關注點分離開,再合理有序地組織起來,呈現在代碼裏,就離寫出清晰代碼不遠了。

關注點,可以分爲技術關注點和業務關注點。通俗地理解,一個“關注點”,可視之爲一個“事實”;但關注點的含義,比事實要廣泛得多。

技術關注點,側重於解決某一類技術問題。比如線程池、連接池、事務、冪等、切面、異步、MVC等。 “代碼抽象與分層” 列舉了代碼裏很多細小的抽象和關注點。

業務關注點,側重於描述某個業務點。比如訂單已發貨、訂單全額退款、獲取訂單已退金額,等都是業務關注點。業務關注點可大可小,小至一個業務常量,大至一個下單的基本流程。

掌握關注點分離的思想和技能,就打好了組織大規模邏輯的基礎。

語義爲導向

清晰性至爲重要。

很多程序員喜歡用一些技巧和 tricky 的方式來實現,比如用行爲代表狀態,用“隔離”的行爲來表示“已隔離”的狀態搜索,因爲隔離成功之後就是已隔離狀態。這是因爲行爲與狀態當前只存在一一映射,故可行。

對此,我不敢苟同。我認爲,在滿足其它要素的情況下,語義的清晰性是最高等級的。代碼寫出來,最好一眼就明白是什麼意思,不需要拐彎抹角的推理。行爲就是行爲,狀態就是狀態。兩者不能等同。你可以實現一個行爲狀態映射圖,讓行爲和狀態在其中正確轉化,而在其它地方,就不需要費腦筋了。否則,今天寫幾個映射,明天又添幾個狀態,這些映射關係散佈在代碼裏,以後很容易出BUG,後面維護的人也很頭疼,每個人都得學習一遍這種映射關係。

集中管理與一致性

最後一個重要思想是集中與一致性。所有緊密相關的邏輯,最好放在一起,一致處理。比如“共同複用和共同閉包原則”。

對於上面那種情況,如果真要那麼做,也最好把所有的映射關係都放到一個類裏管理,不要隨手散佈在代碼裏(比如直接寫 if-else-set)。

避免重複,也是保持一致性的方法。之所以要避免重複,是因爲一些相同的邏輯如果散佈在代碼裏,很可能改了這一處漏了那一處,懲罰就是 BUG 的產生,需要花費不必要的時間和精力去修復。

方法

領悟四大核心思想之後,我們要開始構建一系列方法,這些方法可以指導我們有序地組織邏輯。

分解-組合

“分解-組合”是第一萬能之法。任何事情,如果你感覺有點喫力,就把它分解成小的事情,直到你的能力能夠處理的範圍。

分解之後,就要思考如何把分解後求得的子結果組合起來。這一步也很關鍵。任何事情,總可以分解成喫、住、行、說、寫五件事的組合。

分得好,合得也好,那麼“分解-組合”就是一種異常強大的方法。

歸併排序,就是典型的“分解-組合”範例;Map-Reduce,也是將大數據集先分解成多個子數據集調度在不同節點上處理,再進行合併結果集處理。

封裝

一旦當你完成某個工作成果後,將它封裝起來,以備後用。

庫、框架、工具、中間件等,都可以封裝起來。

封裝是快速累積和構建的良好法寶。

模塊化

要構建更大的邏輯,模塊化必不可少。

所謂模塊化,就是把一些緊密相關的邏輯放在一起,使之共存共生。當需要修改某個功能時,儘量只修改這一小部分,影響局部化。

如果不做好模塊化,邏輯就會散佈在整個應用裏,光是把所有相關的地方定位好,就得花費大量時間,還可能遺漏。

此外,軟件的很多功能是聯動的。如果不做好模塊化,軟件就會像蹺蹺板,這裏壓下去了,那裏又翹起來了。BUG 永遠都改不完。

解耦

解耦是針對模塊化而言的。做好模塊化之後,就要做好解耦。

解耦解決的是,如何把關聯又相互獨立的邏輯放置有序,使之獨立變化,互不干擾。比如一個下單之後,要支付、要扣減庫存、要分發優惠券等。如果全部寫在一起,支付出了問題影響扣庫存,扣庫存出了問題影響發優惠券等,纏在一起,難以理清。

解耦是保證現有系統持續擴展的重要方法。微服務本質上就是一種解耦之法。

詳閱: “軟件工程中的耦合與解耦方式

設計

有了思想和方法之後,我們需要學習一些常用設計模式。所謂設計模式,實際上就是結構模式的固化。使用這些結構模式(套路),就能解決很多問題了,根本無需“重新發明輪子”。

設計原則

設計原則是進行軟件設計的基本原則。遵循這些原則,通常可以設計出可擴展的軟件。

詳閱: “【轉載】一些軟件設計原則”。

編程思想

編程思想是設計原則的細化,是實際編程的有力武器。

詳閱“計算機編程領域的三十六種基本思想概覽”。

架構模式

架構模式關乎應用的整體結構,確定應用的數據流結構和控制流結構。選擇合適的架構模式,就是給代碼初步定了個合適的“模塊化結構”。

詳閱:“軟件設計要素初探:架構模式”。

設計模式

設計模式是關於實際問題的解決方案,也是關於職責與交互如何變化擴展的技藝。詳閱:“軟件設計要素初探:基礎設計模式概覽”,“理解設計模式之“道” ”。

構建塊

掌握思想、方法和設計之後,就需要腳踏實地開始幹活了。

首先,要確定一些基本構建塊,通過這些基本構建塊,來構建程序的雛形。

數據結構與算法

數據結構與算法是軟件的基本構建塊。幾乎所有強大、實用而高效的功能都離不開數據結構與算法。這部分的重要性實在不言而喻。

看不到花,不能混說花不存在。因爲花始終散發着清香。你看不到,不代表別人看不到。

詳閱:“【整理】盤點程序員必知必會的常見數據結構和算法

流程模式

流程模式,是組織代碼流程的常用模式。掌握流程模式,可以自如地編寫更復雜的流程。

詳閱:““馴服”業務流程:盤點業務開發中的常見流程模式

結構模式

如前述,結構化抽象是軟件開發與設計的第一大法。掌握常用的結構模式,應對軟件開發各種需求時纔能有章可循,不慌不忙。

詳閱:“軟件的結構模式及結構的擴展

技術手段

即使是邏輯,對於邏輯也是有特定要求的。比如轉賬,這個賬號錢出去了,那個賬號錢就應當以相同數額進來。操作完成之後,兩個賬號的總金額應當保持不變。這就是一個一致性約束。一個操作應當在用戶可接受的時間範圍內完成,這就是一個性能約束。

技術手段是解決實際問題、滿足特定約束的具體方法。掌握這些技術手段,才能應對實際工作中的諸多挑戰,滿足千變萬化的約束要求。

索引、事務、冪等、異步、分區、限流、熔斷等,都是技術手段。

詳閱:“互聯網應用服務端的常用技術思想與機制綱要

中間件

中間件是實際可用的成品構件。熟悉這些構件的用法,才能構建複雜的現代互聯網應用。

  • 數據存儲(MySQL, Mongo, HBase, ES etc.)
  • 消息(Kafka、RabbitMQ etc.)
  • 緩存(Redis、Memcached etc.)
  • 服務發現(Etcd, Eureka etc.)
  • 配置(Appllo, Vault etc.)
  • 容器(Tomat, Jetty, Undertow etc.)
  • 調度系統(K8S etc.)

框架與庫

應用框架爲應用創建了一個應用模板(基本固定好的代碼組織結構),應用只需要根據這個模板填寫實際業務邏輯即可。Spring 和 MyBatis 是 Java 程序員最爲熟知的兩個框架。

庫是程序員手頭常備的工具箱,可以提供很多實用的功能。比如集合、日誌、時間日期處理、編碼、字符串、對象池、併發庫、本地緩存、JSON解析與轉換、HTTP調用、網絡通信等。

編程語言與代碼組織

語言影響思維。編程語言是程序員最直接的武器,用於表達各種邏輯。編程與寫作類似,也是寫下一連串符號;但編程與寫作又不一樣。寫作的內容非常廣泛,兼具理性與感性,而編程的主要內容是表達計算與存儲,理性色彩極濃。

編程語言通常提供了代碼組織的基本方法,影響着代碼的組織。比如 Java 的 Package 就提供了包的概念。在包裏的類可以訪問彼此的公開方法和 package 方法,而包外面的類只能訪問包裏面的 public 方法。編程語言提供的代碼組織主要在於訪問控制上。

小結

看上去,這世界已經爲你提供了這麼多有用的東西,只待你信心滿滿地去構建應用。那麼難點在哪裏呢?

當邏輯鋪天蓋地、排山倒海地席捲過來的時候,人腦沒法駕馭這麼多的邏輯。想一想,光是一個庫,就有幾千行代碼,光是一箇中間件,就有幾萬行代碼。怎麼看得過來呢?因此,即使你掌握了這麼多,還是對大規模邏輯的組織心有餘而力不足。

本文只是揭示了這些大規模邏輯是如何組織起來的,但是如何去駕馭它,依然是難題。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章