十餘年前的入職C語言培訓作業-控制台日曆(中)

十餘年前的入職C語言培訓作業-控制台日曆(中)

來自專欄 我的代碼世界

接上篇繼續回顧控制台日曆小程序的開發過程。上篇鏈接《十餘年前的入職C語言培訓作業-控制台日曆(上)》,強烈建議按順序閱讀。

軟體設計

「設計」這個辭彙顯得有些高冷,其實我覺得,所謂軟體設計,就是思考如何用程序實現用戶需求的過程。

學習過軟體工程這門學科的同學們,可能對設計理論並不陌生,在教科書中,對於面向過程的設計方法,我們會接觸到數據流圖、流程圖;而對於面向對象的設計方法,需要採用UML圖(類圖、時序圖等)設計工具,以精確表述軟體的設計。總體說來,軟體設計分為概要設計詳細設計兩個階段。前者將程序劃分為子模塊,並定義各子模塊間的介面;後者主要解決各子模塊如何實現等細節問題。理論宣講到此為止,想知道更多的,就直接翻翻軟體工程的教科書吧。

設計軟體的輸入輸出

因為在需求分析階段,並沒有精確定義到可以用來指導編寫代碼的程度,所以此時就到了精確地定義出這些的時候了。需要注意的是,要再次查看一下已定義好的需求,程序的輸入輸出千萬不要有遺漏需求的情況發生。

  1. 命令行參數:無
  2. 標準輸入:三組數字,以空格分格。如:2006 09 22
  3. 標準輸出:
  • 用戶輸入正確時,列印用戶輸入的日期和該月的日曆。示例格式如下:

  • 用戶輸入錯誤時,列印出錯信息。(不要吐槽我十年前的英語水平了吧……居然把"format"說成是"form",語法也好不自然……)

(1)用戶輸入並非三組數字時,列印:Bad form of your input.

(2)用戶輸入年份小於1或大於9999時,列印:Invalid Year Input.

(3)……

…………(此處省略)…………

設計軟體的實現方法

最近在知乎上看到一些同學詢問為什麼看別人的代碼都能看懂,而輪到自己動手時卻不知如何去做的問題。我覺得,這很有可能是因為:一是沒有對要解決的問題做到精確地定義和描述,二是沒有從軟體設計的角度思考過如何解決問題。對於小型程序的設計,我們往往採用面向過程的設計,這也是每個程序員都必須掌握的技能。

記得有一個被某屆春晚帶火了的笑話,說要把大象裝冰箱總共分幾步?答案是3步。

  1. 把冰箱門打開
  2. 把大象裝進去
  3. 把冰箱門關上

對於人類來說,這是眾人皆知、理所當然的事,特地講出來所以變成了一個笑話。但對於程序員來說,在軟體設計時,卻需要不斷地重複從事著將詳細執行步驟明確化,並交給計算機執行的工作。我的理解,面向過程的設計,便是將解決複雜問題的過程逐級細化,最終的程序代碼,便是細化的終點。而這個最終的精細化程度,取決於使用的計算機語言,和該語言提供的標準庫以及第三方庫的支持範圍。但值得注意的是:形成代碼的最終細化過程,雖然也是設計的一部分,但應該留到編碼實現階段完成。

好的,我們開始考慮,要根據用戶輸入的日期獲得該月對應的日曆輸出,總共分幾步?

  1. 獲取並校驗用戶輸入
  2. 列印該月的日曆作為輸出

既然是逐級細化,那第一級就只有兩步好了。當然,這是我的設計,大家也可以認為需要更多的步驟。(說只用一步是不行的,因為只用一步的話算不上是對過程的細化,也就不是設計過程了)

然後,我們考慮進一步的細化分解,但此時應該注意的是,在考慮[1. 獲取並校驗用戶輸入]的時候,暫時忘記[2. 列印該月的日曆作為輸出]這件事吧,反之同理。這樣,我們就分別面對了兩個不同的問題。依此類推,最後,在編碼階段前,我將該程序的步驟分解如下

劃分模塊,設計模塊間的介面

有了關於程序實現步驟的思考,下一步便是劃分模塊和設計模塊間的介面了。每個模塊也可以進一步劃分成更小的子模塊。類似這樣自頂向下,層層推進,就是使用計算機程序解決問題的基本思路和方法。模塊和模塊間的介面設計,是一個程序設計好壞的根本體現。可能大家也都聽說過「高內聚、低耦合」的基本原則,今後的其他文章中我會再深入一些描述自己的心得。

對於本程序而言,所謂的模塊可以抽象成C語言的「函數」,而介面,便是函數的輸入輸出參數以及返回值的定義。另外,必要的全局變數,也可以成為模塊間的介面之一。現在看十年前我寫的這個程序,雖然只使用的三個全局變數用來保存用戶輸入的年月日,但這並不是一個好的設計,這三個全局變數其實是可以避免使用的。另外,由main函數直接調用「日曆輸出模塊 」中的各個函數也顯得非常不自然。事到如今,只好自我解嘲,一句話,成長需要歷練。

大致的模塊劃分如下圖所示:

值得一提的是,除了業務上,用於處理用戶輸入的模塊和用於日曆輸出的模塊,還增加了一個公用模塊,這個模塊一般是用來實現一些其它各個模塊公用到的一般性操作。比如在這個程序中,無論是較驗用戶輸入的數據,還是列印日期序列,都需要計算出給定年月的日數,所以把這個函數提取出來,放在了公用模塊中。但這樣做也會增加模塊間的耦合度。其中的利弊權衡,以及對未來程序可維護性的影響都是我們在軟體設計中需要著重考慮的方面。

再重複一次吧,現在看來,我十年前的這個設計,並不是個很好的設計,只是一個可行的設計。閱讀本文的讀者們也可以試著練習一下,拿出自己更好的設計方案。

另外,我當年只是在草稿紙上畫出了這個設計的草圖,目前文章中的圖都是我重新整理的。設計本身並不拘泥於文檔,對於一個人的項目來說,有時甚至可以把整個設計只在頭腦中構思好便開始寫代碼。但我仍然強烈地建議把設計可視化,即使是在草稿紙或黑板上,構思好軟體的整體設計後再開始動手寫代碼,這是從學校作業程序編寫到產品程序開發的必經之路。

下一篇文章,準備再簡要介紹一下編碼實現以及測試。

(下篇鏈接:十餘年前的入職C語言培訓作業-控制台日曆(下))


推薦閱讀:

一個成功的 Git 分支模型(適用於商業應用開發)
【視頻】80%的軟體測試人員都會遇到的28個誤區--上
一個性能測試人員的愛情故事
《微服務設計》閱讀筆記(一)微服務
軟體開發怎麼這麼貴,它的價格是怎麼定義的?

TAG:編程 | C編程語言 | 軟體開發 |