重新溫習軟件設計之路(1)


如果說學習數據結構和常用算法可以幫助我們寫出較為高效的代碼,那么學習軟件設計相關知識則可以幫助我們寫出較為高質量的代碼,本文是我學習課程《軟件設計之美》的學習總結的第一部分。

1 什么是軟件設計?

軟件設計,是一門關注長期變化的學問,它不是開發者的入門課。作為初級程序員,往往首選的追求是實現一個具體的功能,不能看到一個軟件長期的變化。

設計是為了讓軟件在長期更容易適應變化。

— Kent Beck

以排序算法為例,快速排序的平均復雜度是 O(nlogn),而插入排序是 O(n^2)。所以,一般我們說快速排序比插入排序有優勢。但是,這種優勢只有在數據規模達到一定程度之后才能體現出來,否則二者差別其實並不明顯。因此,對比這兩個排序算法的優劣關鍵在於數據的規模。

所以,我們可以發現算法和軟件設計類似,二者對抗的都是規模問題,算法對抗的是數據的規模,而軟件設計對抗的是需求的規模。

換句話說,只有在長期的需求積累前提下,規模問題才會凸顯,而(好的)軟件設計,正是應對需求規模問題的解決方案。

那么,到底什么是軟件設計?

是具體技術實現?是框架和中間件?是設計模式?.......

上面說道,軟件設計要關注長期變化,需要應對的是需求規模的膨脹,而上面這些提到的誤解則都是不斷在變的東西,沒有直擊問題本質。

軟件設計其實是在軟件開發過程中,建立起一個統一的結構,便於參與這個過程的所有人都能有一個共同的理解,類似於建築圖紙。

而這個統一的結構,我們可以將其理解為一個模型,它是一個軟件的骨架,是一個軟件之所以是這個軟件的核心

模型的粒度可大可小,小到可以是一個類(class),也可以大到一個系統(system)。

一般好的模型,都是“高內聚,低耦合”的。

模型可以分層,由底層的模型提供接口,構建出上層的模型。因此我們需要做的,其實是 理解模型、建立模型 和 評判模型的優劣 等等

但是,僅僅有好的模型對於軟件設計要解決的問題還不夠,還需要軟件設計的另一部分,它就是規范。所謂規范,就是限定了什么樣的需求應該以什么樣的方式去完成。

規范的作用主要在於,維系軟件長期的演化。正如我們在實際項目開發過程中,團隊成員基礎各異,老成員一個沒留神,新成員就可能會創造出一種新的寫法,我們的項目就會走向不可控。因此,我們會看到無數的規范,如代碼規范、開發規范、數據庫規范、DevOps規范等等。但是,如果發現規范不符合軟件設計原則,那也得修正規范,使之得以匹配才能不拖累演化。正如每個項目由於其業務的不同,需要不同的架構,也由於其業務處於不同的階段,需要的架構也不同。規范也一樣,它依賴於模型的發展,如果模型發展了,規范阻礙了模型的發展,那也需要對規范進行調整。

模型和規范,二者相輔相成,它們構成了軟件設計的主體內容。

換句話說,軟件設計=模型+規范

2 軟件設計的第一步:分離關注點

對於稍微大一點的軟件設計,我們最常用的方法就是分解大問題為一個個的小問題來各個擊破再進行組合。如何分解與組合,是軟件設計中需要考慮的重要問題。正如當下十分流行的微服務架構風格,就是分解與組合的典型案例。

在軟件設計中的第一步,就是要考慮好分解的粒度,不合適的粒度會為軟件日后的演化埋下很多坑。因此,我們常常聽說軟件設計需要首先“分離關注點”。

圖片來源《軟件設計之美》

多多關注非功能性需求

對於一個系統來說,它的軟件設計不應該只考慮功能性需求,還需要考慮它的非功能性需求。鄭曄老師強調道,我們需要主動去發現和挖掘這些非功能性需求。這也讓我聯想到過往的一些架構設計,它們很多時候都是由很多非功能性如數據一致性要求、響應速度要求、可用性要求等等。

圖片來源《軟件設計之美》

分離關注點的常見問題

(1)將業務處理和技術實現混淆在一起

在分離關注點的常見問題中,最典型的一個問題就是將業務處理和技術實現兩個關注點混淆在一起

比如,常見的將業務代碼和多線程處理放在一起,但是不加以限制地去修改代碼會容易引發多線程的相關問題如資源競爭、數據同步等。又如,我們可能會涉及到需要分布式事務、分庫分表的場景,但是我們是不是該想想我們的業務真的需要分布式事務嗎?是不是業務划分沒有做清楚,才造成了DB的壓力?很多大佬都說,分布式事務的最好解決方案其實是 能不上分布式事務就不上分布式事務

我們程序員往往喜歡認為所有的問題都是技術問題,試圖用技術問題解決所有問題。但是,鄭曄老師強調道,任何試圖用技術去解決其關注點的問題,只能是越陷越深。

(2)不同的數據變動方向引起的混淆

在分離關注點的常見問題中,另一個典型問題就是不同的數據變動方向

比如,在一個.NET應用里邊,做數據庫訪問是用EF好,還是Dapper或者SqlSugar好,又或者直接原生ADO.NET好?對於普通的增刪查改,使用ORM如EF這類會快速又省事,但是對於一些復雜場景,我們又會擔心自動生成的SQL有性能問題,還是覺得自己手寫SQL優化來得直接和實在,感覺挺糾結的。而之所以出現工具選擇的困難,很多時候可能都是由於我們將兩種使用頻率不同的場景混在了一起,比如將前台訪問(CRUD)和后台訪問(統計報表)混在了一起,如果將其分開,那是不是也就不糾結了?

又如,我們常聽說的動靜分離,其實就是將變的和不變的內容分開;數據庫讀寫分離,就是將讀操作和寫操作從數據庫層面分開;CQRS,就是將命令和查詢操作從代碼層面分開。......

因此,不同的數據變動方向,就是一起可以分離的關注點。

分離關注點的目的

分離關注點,一來可以避免后續演化過程可能會發生的許多相關問題,二來可以幫助我們發現不同模塊間的共性,更好地進行設計。

因此,我們在軟件設計的第一步,就是要發現和分離關注點,發現的關注點越多越好,粒度越小越好。

3 被忽略的重要因素:可測試性

分離關注點是軟件設計的第一步,它常被我們所忽略。此外,還有另一個常被人忽略的因素:可測試性。我們可以看到,可測試性,其實是屬於非功能型需求中的一個需求點,但是這個點往往不被我們所重視,因而常常被忽略。

圖片來源《軟件設計之美》

鄭曄老師說道,我們在開發過程中欠下的許多技術債,從本質上來說都是因為忽略了“可測試性”這個需求。因為,我們將軟件拆分后,它們就是一個一個的小模塊,如果不僅可能保證每一個小模塊的正確性,是很難從最外圍的整體系統的角度去驗證正確性的。我們都知道,Bug Fix的成本在軟件開發過程中是逐步升高的,如果能在前面盡可能的發現更多的Bug,所花費的成本是要比在線上排查修復低很多的。因此,盡早暴露問題而不要總是等到集成測試才暴露其實早已是我們的共識,而可測試性就是利於我們盡早暴露問題的解決方案之一。

那么,如何考慮可測試性,簡而言之,就是我們在設計的時候問一下自己,這個 方法/模塊/系統 怎么測試?將每一個小模塊做了足夠的測試,就會有足夠穩定的模塊,進而才有高效的集成測試。

舉個例子,我們在開發.NET應用程序時一般都會借助依賴注入和接口設計來將外部依賴項進行隔離,再使用一些Mock框架(如Moq、NSub等)對這些外部依賴項進行模擬,然后根據這些模擬對象來進行單元測試的編寫。又或者針對數據訪問層的單元測試,我們也往往會使用Mock框架將DB用內存來模擬,我們要做的就只是保證模擬出來的內存模擬實現 和 接口定義的行為保持一致即可。

下面是一段常見的不具有和具有可測試性的.NET代碼:

(1)服務層調用數據庫訪問層的代碼,不考慮可測試性

public class ProductService
{
    private DBProductRepository repository = new DBProductRepository();

    public Product GetProduct(long id)
    {
        return repository.GetProduct(id);
    }
}

(2)服務層調用數據庫訪問層的代碼,考慮了可測試性

public class ProductService
{
    private readonly IProductRepository _repository;

    public ProductService(IProductRepository repository)
    {
        _repository = repository;
    }

    public Product GetProduct(long id)
    {
        return _repository.GetProduct(id);
    }
}

在實際項目中,我們也往往會碰到一些遺留系統的維護,這些系統的設計往往沒有考慮可測試性,因此很難做單元測試。這時,我們可以采用一些強力的Mock框架,比如JustMock(要收License費用),它可以幫助我們模擬如靜態函數、.NET基本函數庫、日期對象等開源Mock框架如Moq等所無法模擬的對象,進而幫助提高模塊的可測試性。當然,使用強力的Mock框架只是術,還是需要在設計時就考慮可測試性才是道。

關於在.NET應用中編寫單元測試,我也有寫一個小系列的文章介紹,歡迎閱讀。傳送門:點擊這里

圖片來源:JustMock官網

4 小結

本文我們學習了什么是軟件設計,一句話概括,軟件設計=好的模型+適合的規范。軟件設計的第一步是分離關注點,分離的關注點越多越好,粒度越細越好。在軟件設計時不要忘記可測試性,只有將一個個的小模塊做了足夠的測試,才會有穩定的構造塊,便於后續的集成測試。

最后感謝鄭曄老師的《軟件設計之美》課程,讓我獲益良多,我也將它推薦給各位園友,值得訂閱!

參考資料

鄭曄,《軟件設計之美》(極客時間課程,推薦訂閱學習

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM