代碼中的軟件工程

開篇

  軟件工程,這門計算機類專業的朋友想必都瞭解過,但其精髓、其重要思想常常浮現在優秀的開源項目裏,大家想要進階,去接觸反而有點晦澀難懂了,有點可遠觀不可褻玩也的味道。本人學習了孟老師的這幾節課程,也做了下其項目,略有點體會,就讓我們一起揭開她的面紗吧!(參考資料見孟寧老師碼雲:https://gitee.com/mengning997/se/blob/master/README.md#%E4%BB%A3%E7%A0%81%E4%B8%AD%E7%9A%84%E8%BD%AF%E4%BB%B6%E5%B7%A5%E7%A8%8B)

環境搭建

  這次項目主要以VS Code + GCC爲工具集,查看一個經典的C/C++項目——MENU(類似LInux的命令效果)

  首先在Vscode下下載C/C++擴展

 

 

 然後配置gcc環境,這裏主要參考了兩篇博文(見文末),最後成功配置好

 

軟件工程一般原理簡介

模塊化設計

這裏主要有三種模塊

 

 

 

  • 程序的入口test
  • 菜單邏輯menu
  • 菜單使用到的數據結構linktable

  那麼重點來了,模塊化的優勢在哪?別急着回答,先思考一下,然後帶着你的想法繼續看下去。先回想一下,傳統開發的痛點在哪。

  首先,如上所述,傳統的開發方式需要等待所有腳本資源加載完成。這個問題最大的弊端就是頁面要等待,因爲資源加載是同步的。你的頁面會出現短暫的空白期,引入的腳本越多,時間越長,如果某一腳本加載失敗,也可能直接掛掉。模塊化的代碼則可以很好的處理這個問題。除了模塊化支持的腳本必須加載進來以外,其他腳本都可以異步請求,不需要頁面等待,可以加速渲染出頁面。requirejs,sea.js等也會做好加載重試和模塊緩存的處理,確保所有模塊運行良好。所有資源加載的時間不會因爲模塊化而加速,但是模塊化能加速渲染,這是優勢1。當然webpack是特例,它和nodejs一樣用 commonjs 規範,爲了達到目的,全部腳本打包到一起再運行,看着和上面觀點相悖,不過現在帶寬足夠,相對而言還是足夠快的,也能減少多腳本加載出錯的風險。接着上面的觀點講,拋開帶寬速度來講,既然網速夠快,那模塊化還有什麼?不妨回想一下,傳統開發時最煩的是什麼?無非3點命名空間。早期爲了避免命名衝突,大衆做法是用一個變量作爲命名空間做隔離,長期開發過程中沒人能記住這個變量是否衝突,它的命名規範是什麼,治標不治本。而模塊化的出現消除了這點。一個模塊內的命名隨自己起,和外界不會衝突,對外的永遠是你exports出來的內容。如果模塊內出現命名衝突,這說明了你的命名水平太低…..好吧,是模塊顆粒不夠小,還可以繼續分割出模塊~代碼重用。其實這點和傳統開發並無兩樣,都是把可複用代碼抽取出function(再通用點會抽象出類,也就是構造函數),獨立文件。但模塊化的好處同樣可以規避命名空間的問題,不必設置變量污染到全局。一般模塊化都有緩存機制,在二次調用時無需再解析,直接獲取到緩存模塊內容。按傳統開發來處理,忽略以上問題,但也耐不住文件太多,引入和管理麻煩。除了amd規範需要依賴前置,我們還可以用cmd規範來寫模塊依賴,想用什麼require什麼,不用再一個個引入js,看着也舒服。而且現在的模塊化工具基本都實現了多規範混搭,想怎麼寫就怎麼寫,只要注意組內規範就行。

  此外就是 管理問題。小公司或個人開發,模塊化能讓自己思路更爲清晰,降低代碼耦合,優秀的模塊能帶來代碼質量質的飛躍,標準的模塊應該是 “分工明細,職責單一,不牽扯需求邏輯” ,它就應該是個萬能的螺絲,不需要可以修改,哪裏需要用哪裏。而中型企業和大團隊則很經常會遇到團隊協作開發,除了會用svn/git等工具管理,各種需求有不同的人負責處理。模塊化對團隊開發會起到協同作用,公共  模塊除了避免重複造輪子的痛苦外,也避免了邏輯混淆。

可重用接口

  在一個系統中,系統的各種功能是由許許多多的不同對象協作完成的。在這種情況下,各個對象內部是如何實現自己的,對人員來講就不那麼重要了;而各個對象之間的協作關係則成爲系統設計的關鍵。小到不同類之間的通信,大到各模塊之間的交互,在系統設計之初都是要着重考慮的,這也是系統設計的主要工作內容。面向接口編程就是指按照這種思想來編程。因此接口的設計好壞與泛化能力直接可以體現一個項目的優秀程度。

  如下代碼塊,MENU程序也設計了好的接口。

  

//linktable.h文件
//LinktableNode結構體只保留了最基本的結點指針,具體的data數據並沒有包含,面向抽象不依賴具體
typedef struct LinkTableNode
{
    struct LinkTableNode * pNext;
}tLinkTableNode;

/*
 * LinkTable Type
 */
//而inktable這個結構體則在引入頭文件的情況下,嵌入了LinkTableNode結構體,並且另外定義了數據頭//尾指針以及互斥量,相當於實現了遍歷功能和線程保護
typedef struct LinkTable
{
    tLinkTableNode *pHead;
    tLinkTableNode *pTail;
    int            SumOfNode;
    pthread_mutex_t mutex;
}tLinkTable;

/*
 * Create a LinkTable
 */
tLinkTable * CreateLinkTable();
/*
 * Delete a LinkTable
 */
int DeleteLinkTable(tLinkTable *pLinkTable);
/*
 * Add a LinkTableNode to LinkTable
 */
int AddLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode);
/*
 * Delete a LinkTableNode from LinkTable
 */
int DelLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode);
/*
 * get LinkTableHead
 */
tLinkTableNode * GetLinkTableHead(tLinkTable *pLinkTable);
/*
 * get next LinkTableNode
 */
tLinkTableNode * GetNextLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode);

   接口的可重用性也可以拿go來舉例,例如m:=map[string]interface{} 這樣就創建了一個string的key類型可以映射到任意的數據類型的value,而這個空接口是所有數據類型皆實現的,這樣便大大提高了接口的可重用性,方便開發設計。

線程安全

  線程安全在多線程環境中是一個很重要的議題,讀讀併發不影響安全性,讀寫併發則特別影響整個系統的可靠性,MENU程序也注意了這方面的保護,以刪除操作爲例

//linktable.c文件
int DeleteLinkTable(tLinkTable *pLinkTable)
{
    if(pLinkTable == NULL)
    {
        return FAILURE;
    }
    while(pLinkTable->pHead != NULL)
    {
        tLinkTableNode * p = pLinkTable->pHead;
      //互斥量加鎖
        pthread_mutex_lock(&(pLinkTable->mutex));
        pLinkTable->pHead = pLinkTable->pHead->pNext;
        pLinkTable->SumOfNode -= 1 ;
        pthread_mutex_unlock(&(pLinkTable->mutex));
     //數據更新完成後釋放鎖
        free(p);
    }
    pLinkTable->pHead = NULL;
    pLinkTable->pTail = NULL;
    pLinkTable->SumOfNode = 0;
    pthread_mutex_destroy(&(pLinkTable->mutex));
    free(pLinkTable);
    return SUCCESS;        
}

  保護線程安全常常加鎖來處理,例如java中的synchronized和lock關鍵詞實現線程保護。

結尾

  那麼,文章到這便結束了,軟件工程的常見設計思想便簡單介紹完了,希望各位能運用到項目中去。

https://www.cnblogs.com/bpf-1024/p/11597000.html

https://blog.csdn.net/zhanglu_1024/article/details/108678165

 

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