1. 程式人生 > >從編程到軟件工程

從編程到軟件工程

入門到 當前 基本 必須掌握 資源 服務 說話 網絡請求 oat

前言:

本文是我自己在三年多的軟件編程學習上的親身體驗,我將自己學到的體會到的進行記錄下來,很明顯,對於我是最適用的,不喜勿噴。至於為什麽要記錄下來,一是對我自己目前階段的想法的梳理總結,二是希望給以後的新人一些經驗參考。

雖然我自身是C#語言的忠實踐行者,但我也不排斥其他的語言,諸如java,python,還有網頁三大件語言等等,至於C和C++,語言確實很強大,只是應用方向多半為底層開發,或是對性能要求到變態的程序開發,在一般的中小型系統開發,工具開發中,開發效率實在比不上C#或是java,如果只是開發個windows的一個軟件小工具,無疑C#的開發效率是最高的,幾秒鐘就可以生成一個窗口界面。有時候會有人問我,學習什麽編程語言好?這真的是一個太難回答的問題,需要從自身的喜好,能力,希望從事的行業多方面考慮的,至於說薪資,無論哪個語言學的厲害了,薪資都是沒有上限的,前提是真的學精通了。話說回來,無論學習哪種語言,這都一條孤獨而又寂寞的道路,就是選擇了相當長的時候不說話,只是和電腦在打交道,選擇了不停的折磨自己,就為了體驗在完成功能或解決BUG時的快感,所以,一般不建議妹紙學習編程。

我要說的東西和具體的語言關系不大,編程語言都是有共通的,剛開始學習的時候,基本都是從語言的變量類型來學習的,什麽bool,byte,int,float,double,string(對python來說,雖然對象沒有類型,但是實例仍然是有類型的)等等,然後再學習流程控制語言,大多數的編程語言都會提供if-else,do-while,while,for循環等等,然後介紹輸入輸出,讀寫文件,方法和類,類的概念實在是太大了,通常都會花很多章節來說明,模塊和組件,有的還有怎麽做界面。

幾乎所有的編程語言的書都會有《從入門到精通》的系列,之所以都是這麽命名,因為它真的是入門而已,這些說也基本都只是介紹我上述所說的知識,之所以不深入也是有原因的,對於初學者,一次灌入的信息量太大,會導致難以接受。而言更關鍵的問題是,很多編程大神,經驗豐富的程序員,也僅僅是自身比較厲害,並不代表傳授知識厲害,會編程和怎麽把編程技巧傳授給新人,這實在是兩個完全不同的領域。而且大神體會的都是一些抽象後的精髓,對於新人來說,都是一些虛無飄渺的東西。就好比,我們要學習兵法,老師來一句“兵者詭道也”,鬼知道應該什麽搞,但是對於一些經驗豐富的人來說,可以一點就通。

一般反復多讀幾次《從入門到精通》的書,基本上算是真的入門了,可以稱之為程序員了,這時候需要找一些小的項目練練手,比如寫寫一些小工具,文件統計處理的,或是寫個小遊戲,俄羅斯方塊之類的。猜數字遊戲,迷宮遊戲之類的,一般這樣幾個項目下來就一門語言的編程就大抵清楚了,但是這和軟件工程還差得遠呢,雖然軟件工程也是由一些簡單嗯的語句組成的,但是這個區別就像,你肯定懂中文,但是寫不出史記,甚至很難寫出一篇優美的文章。

說了這麽說廢話,接下來才是真正的主題,從編程到軟件工程,在我的經驗上得出的結論是需要走三個大階段才能完成,接下來分別論述下這三個大階段。在此處不將算法的研究放進來,以來因為算法的研究又差不多算另一個領域了,二來一般人實現功能,基本都是從網上直接搜索來的,一般網上公開的算法都是相對比較成熟的,至少比我想的算法要好。

1.代碼提煉

在掌握了一門語言的基本類型和語法後,基本上都是自己開發一些小的軟件了,個人的比如小型聊天軟件,俄羅斯方塊,五子棋,隨機數遊戲。至於企業軟件,如果沒有相應的環境,確實難以想象可以做什麽系統,所以在此處我推薦開發一個賬戶登錄,密碼修改等等軟件,這是所有企業軟件必備的,等這種小軟件做多了,自然就遇到瓶頸了。我這時候就在想,簡單的我都會,雖然代碼醜了點(但是自己看不出來啊,不然肯定改掉了),難的我也做不了,陷入了一種尷尬的境地。

要想突破自己的水平,就要學習提煉代碼,這是一個漫長的技術,很難一下子掌握,即使有了這個思想,也是一步步慢慢來的。在剛開始寫代碼的時候,一個方法裏總會寫大量的代碼段,而且當時的想法是“這麽寫挺好的,順序執行,通俗易懂”,現在來想想,確實也沒錯啊。但是寫了很多個方法,軟件後,會出現這種情況。一小部分的代碼出現在了你的軟件中的多個地方,寫的時候復制一下沒什麽,問題是這個邏輯以後改起來就麻煩了。所有出現這個復制的代碼段全都得改,一開始學習的時候我就經常苦惱於這種問題,也是因為入門的書上不會講解這種軟件開發思路。舉個例子,你在你的程序中多個地方都使用了數據庫連接字符串,當你服務器的IP地址變化的時候,就需要對所有的字符串進行變更,如果一開始就只定一個變量時,以後更改起來會非常的塊。

有了這個思路的時候,後面寫代碼的時候不知不覺就會按照功能對代碼進行方法提煉,比如代碼塊中有一段是現實數據加密解密的,就很適合提煉出單獨的加密解密方法,這樣就可以在程序的多個地方應用同一套邏輯體系,方便以後的變更。如果提煉了許多功能類型差不多的方法,這時候可以考慮將這些方法放到專門的一個類中,起一個合適的名稱,方便自己以及別人查找。在你提煉了很多方法和類的時候,做的多了,會發現一些類或方法比較通用,需要在你自己開發的多個程序之間共用,以前的做法是在每個程序項目中進行拷貝,但是在優化或者BUG修復的時候就需要對每個應用程序進行維護升級了,而且很容易出錯,留下安全隱患。

這時候可以考慮將通用的功能類提煉成一個單獨的組件,方便在所有的程序中共用,你需要明白,提煉組件不是一件容易的事情,我們需要循序漸進的來做,剛開始可以提煉一些簡單的類,方法,然後學習方法指針(C#中是委托),這個是進階編程必須掌握的技能,一定要多花時間研究,它可以實現方法中的部分邏輯分離實現,從而達到更高的代碼重用,在C#中還有一個技術是泛型,也是代碼重用的關鍵技能(也可以成為算法重用)。這些技術可以提煉一些你以前代碼中會碰到一個代碼塊因為中間的一點點代碼不同而又很難提煉(剛開始學習時通常是參數提煉的方式)出來的情況。特別說明,程序的註釋非常重要,之前我聽到一個總監曾對開發人員說:“程序開發完成後,註釋不要太多,影響項目加載速度,能刪就刪”,對此我不敢茍同,我的習慣是除了每個功能塊方法必須的註釋以外,在類庫的頂端也會著重說明類的功能,已經使用的示例,在方法的內部也會分步驟進行說明方法到底實現了什麽功能。對於程序員來說,寫出來的程序絕大多數是被人來看的,包括我自己,良好的註釋絕對大大提升別人(甚至是長時間以後的自己)理解代碼的速度。

如果你一直按照這種思維方式學習和寫代碼,久而久之你就代碼自然就會有很強的維護性和可讀性(通常對於自己來說,我剛開始學習時,看到這種代碼就覺得反人類啊,方法這裏跳那裏,跳來跳去哪裏都不知道了),也可以形成自己的組件庫來加速應用程序的開發。但是有一點還是不得不明白,代碼的提煉並沒有任何的提升應用程序的性能,相反的還有可能損失了一點點性能,而且更要命的是對於異常的控制更加的困難,將在下一節說明。

2.異常處理

我相信所有寫過代碼的人都碰到過開發的應用程序突然奔潰的場景,我們在開發測試功能的時候發生了一些異常導致程序奔潰無所謂,沒什麽損失,只要發現異常並修復就好了,比如說經常會碰到空對象的操作,數組的索引值小於0或超出最大值,更復雜的異常在多線程的程序中發生了資源競爭或是邏輯錯誤導致的,這種異常往往更難重現,非常難以排查,即使在今天,開發多線程的程序仍然是一件非常困難的事情。

上述所說的異常在學習和開發中肯定會經常碰到,但是本節所說的異常要比我們平時所說的異常要上升一個層次,上述所說的代碼異常在調試階段基本都能測試出來,並能完美的解決這些異常。本節所說的是業務邏輯的異常,通常在我們的程序中都會需要讀寫文件的代碼,網絡請求訪問的代碼,恰恰是這兩個功能是最容易出現異常且無法避免(比如說讀取文件,誰知道用戶會不會手賤把它刪除了呢,至少網絡功能,會不會拔網線)。

如果你的應用程序業務比較復雜,比如服務器程序(S)向設備A請求數據,S再進行分析存儲到文件或數據庫,將結果發送給設備B進行響應操作,這是一個典型的業務處理模型。我們要清楚,如果每個環節順利完成,代碼也會非常簡單,但是問題就是中間的每個環節都是可能發生異常的,而且是不可避免的,尤其是建立在局域網的通信基礎上,誰知道什麽時候會不會壞,會不會不穩定。我們需要做不僅僅是捕獲異常不讓應用程序奔潰這個事情,還要結合業務邏輯來實現,假設S-A失敗了怎麽辦,S分析保存失敗了怎麽辦,前面都成功了,S-B失敗了怎麽辦,是否需要狀態的額外提醒,是否需要回滾狀態,這裏的例子只有三個步序,如果長達十步的操作,任一環節的出錯都要仔細考慮應對結果。

我們在調用第三方組件的類庫時(包括微軟自身的類庫),也是沒有辦法控制方法不發生異常,因為這些代碼本就不是我們自己寫的,好在微軟類庫提供的方法明確表明了調用該方法時可能會發生的異常,好讓調用者心裏有數,好根據自身的需求進行處理異常,這就要求我們自己在開發類庫時也要遵循相應的準則,例如我們在類庫時開發了一個類,包含了一個事件,在實現事件調用的時候,就應該把調用的異常重新拋給調用者的應用程序,假如類庫吞噬掉了一個異常,但是對於調用者卻一無所知,應用程序卻還在運行,會留下非常大的安全隱患。

並不是所有的代碼都要增加異常捕獲,雖然程序健壯了,但是開發效率和性能都下降了,至於中間的取舍平衡,只能靠經驗來自己摸索。

3.狀態恢復

我們的應用程序(尤其是服務端的程序)總要處理一些數據,而且經常是24小時不間斷的在處理數據,緩存數據。這部分的內容總結起來其實很短,只有一句話,我希望我的軟件系統即使現在關閉,再打開,對生產環境的影響微乎其微。怎麽解釋呢,還得使用例子再說明。

假設我們的服務器端應用程序需要訪問現場10臺設備的數據(此處簡單處理,只限於讀取),每臺設備都是一致的,包含設備狀態(是不是在運行中),實時數據(比如說溫度壓力),條碼等等。我們的軟件系統除了實時讀取數據外,還需要進行緩存數據,比如,每臺設備緩存的溫度壓力曲線,比如上一次設備停機時間,啟動時間(用來計算設備的總運行時間),設備狀態等等。

想象一下,如果我們因為需要更新軟件程序需要關閉程序幾秒鐘,然後再運行,這時候所有的狀態數據全部丟失,系統重新進行計數統計,如果你的系統本就不在意這些緩存信息,當然就不需要這些考慮了,但是一個完善的,友好的軟件系統就需要考慮這些不為人所註意的方面,因為在我的經驗看來,運行在現場的軟件系統經常會因為各種各樣的原因導致程序需要重啟一下,對此,我們在設計程序的時候,就要充分的考慮到這種情況,對運行到當前的狀態進行存儲,下次運行時可以直接恢復,並且可以檢測上次關閉的時間,根據程序關閉時間長度來選擇性的恢復狀態。

如果一個系統只有一個服務器,狀態恢復相對也會簡單一點,如果是分布式的多服務器端程序交互,整個系統狀態的恢復將會是極其困難的,即時開完完成後,也需要花大量的時間來測試系統的穩定性。

參考資料------《重構改善既有代碼的設計》

------《CLR Via C#》

一個C#版本的C-S項目模板地址:https://github.com/dathlin/C-S-

從編程到軟件工程