程式碼中的軟體工程

五里霧發表於2020-11-10

本文在中科大軟體學院孟寧老師的指導下完成,意在通過對小型程式的分析來幫助體會軟體工程方法、思想。

本文的參考資料及資源來自:軟體工程——碼農的自我修養

 1. VS Code開發環境配置

1.1 VS Code 安裝

    有關VS Code的安裝配置可以參考該資料:https://mp.weixin.qq.com/s/sU9Wh12JZqQyJfEBBafFAQ

    在本文中,我們對此進行簡單介紹。我們可以在VS Code官網下載,它是一款免費的輕量級IDE,通過擴充套件外掛可以支援多種語言。

    下載地址:https://code.visualstudio.com/#alt-downloads

1.2 C/C++編譯器下載

    在VS Code中,當我們通過載入擴充套件外掛來實現支援時,它並不會包含語言的編譯器與偵錯程式。這裡所說的編譯器與偵錯程式,對於C++而言,即為MinGw。

    C/C++編譯器則為MinGw: http://www.mingw.org/

    下載完成並安裝後,我們可以得到以下目錄:

    

1.3 環境變數配置

    通常情況下,在安裝MinGw時,會建議或者要求我們將其新增到環境變數中。但如果我們忘記了配置,也可以通過以下方法手動進行配置。

    首先,我們需要獲取到MinGw在我們系統中的安裝位置,如上所示,即為:C:\MinGw 。

    之後,右鍵點選此電腦 -> 選擇屬性 -> 高階系統設定 -> 環境變數,在此處可以管理我們計算機的環境變數。我們找到下方系統變數中的Path選項:

    

    通過編輯Path選項,新建新增新環境變數地址,填入上述安裝位置。需要注意的是,我們此時需填入C:\MinGw\bin。

    以上即為C/C++編譯器的環境安裝配置過程。對於其他語言,如Java、Python、Golang,我們均可以採用此種方法。

    我們可以通過命令提示符檢視MinGw的安裝是否成功,使用gcc -v指令:

    

1.4 VS Code中配置C/C++開發環境

1.4.1 載入C/C++擴充套件

    在VS Code左側擴充套件選項中,我們搜尋C++即可搜尋到外掛,點選Install後即可安裝完成:

    

1.4.2 task.json與launch.json配置

    VS Code通過 task.json 配置檔案來獲取編譯程式的方式,通過修改task.json就可以利用VS Code進行gcc編譯原始碼了。

    一個參考的task.json檔案配置如下:

{
    "version": "2.0.0",
    "tasks": [
        {
            "type": "shell",
            "label": "gcc build active file",
            "command": "gcc",
            "args": [
                "-g",
                "${file}",
                "-o",
                "${fileDirname}/${fileBasenameNoExtension}"//
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            }
        }
    ]
}

    關於task.json的格式,可以參考以下文件:https://go.microsoft.com/fwlink/?LinkId=733558

    

    VS Code通過launch.json配置檔案中的配置來呼叫偵錯程式對程式進行除錯。通過快捷鍵Ctrl+Shift+P,搜尋launch.json,可以開啟launch.json進行配置。

    一個參考的launch.json的配置檔案如下:

    

{

    "version": "0.2.0",
    "configurations": [
        {
            "name": "gcc build and debug active file",
            "type": "cppdbg",
            "request": "launch",
            "program": "${fileDirname}/${fileBasenameNoExtension}",
            "args": [],
            "stopAtEntry": true,
            "cwd": "${workspaceFolder}",
            "environment": [],
            "externalConsole": false,
            "MIMode": "gdb",
            "setupCommands": [
                {
                    "description": "Enable pretty-printing for gdb",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                }
            ],
            "preLaunchTask": "gcc build active file",
            "miDebuggerPath": "C:\\MinGW\\bin\\gdb.exe" // 此處為上述的MinGw安裝地址
        }
    ]
}

    關於launch.json檔案的配置,可以參考以下文件:https://go.microsoft.com/fwlink/?linkid=830387

1.4.3 驗證安裝

    可以看到,執行以下示例成功,說明環境配置成功!

    

    

2. 程式碼中的軟體工程思想

2.1 程式碼註釋風格

    良好的註釋風格,可以幫助程式碼閱讀者、維護者等人員快速明確地瞭解到程式碼所實現的功能等,方便Debug。一個優秀的程式碼註釋風格如下,這是孟寧老師所給出的案例,非常值得學習:

    

    從中,可以清楚地看到,這個檔案中的程式碼的檔名、作者、模組名、語言型別、執行環境、版本時間以及描述等,對我們理解程式碼十分有幫助。

2.2 模組化設計

2.2.1 模組化原理

    模組化(Modularity)是指,在指軟體系統設計時保持系統內各部分相對獨立,以便每一個部分可以被獨立地進行設計和開發。這樣使得我們在開發過程中,每一個模組都執行自己單一的功能目標,可以獨立於其他軟體模組,這樣也就方便了開發人員之間的配合。

    模組化的基本原理是關注點的分離(Soc,Separation of Concerns),翻譯成中文其實大概便是“分而治之”。

2.2.2 模組化程度

    在實際的軟體設計開發中,我們一般使用耦合度(Coupling)和內聚度(Cohesion)來作為軟體模組化程度的高低,它們同樣是判斷一個軟體設計優良程度的重要參考。

    耦合度,是指一個軟體中各個模組之間的依賴程度,從高到低在大致上可以分為緊密耦合(Tightly Coupled)、鬆散耦合(Loosely Coupled)。我們所追求的是鬆散耦合,它表明我們所設計的軟體各個模組的依賴程度較低,是比較好的設計。

 

     內聚度,是指一個軟體模組內部各種元素之間互相依賴的程度。理想的內聚是功能內聚,也即一個模組只做一件事,只完成一個主要功能。

2.2.3 模組化設計:原始碼分析

    在實現模組化設計時,我們可以將多種資料封裝成結構體、將實現功能封裝成一個檔案,可以幫助我們簡化程式碼:

    

    

    

    在上圖中,我們將資料結構以及函式宣告放在一個linklist.h的標頭檔案中,其實現放在linklist.c檔案,需要呼叫時再在main函式中去呼叫,這樣就實現了較低的耦合度,也方便我們作為程式設計師去debug。

2.3 軟體模組介面

2.3.1 介面的定義

    介面是通訊雙方共同遵守的一種規範,在軟體系統內部一般的介面方式是通過一組API函式來約定軟體模組之間的溝通方式。物件導向與程式導向的語言在對介面的是實現一般不相同。前者的介面是物件對外開放的一組屬性和方法的集合;後者則是資料結構和操作這些資料結構的函式等。

2.3.2 介面的應用

    

    上圖借用了孟寧老師提供的工程原始碼檔案中的某個原始碼檔案。在這個檔案中,可以看到其宣告瞭許多介面,這些介面的實現會放在.c檔案中。但不通過具體的實現程式碼,我們依然可以通過介面的名稱、引數、返回值以及相關描述瞭解到大概的功能,這些介面的也可以被多次重複使用,提高了程式碼的複用性。

2.4 執行緒安全

2.4.1 執行緒的基本概念

    執行緒(thread)是作業系統能夠進行運算排程的最小單位。它包含在程式之中,是程式中的實際運作單位。一個執行緒指的是程式中一個單一順序的控制流,一個程式中可以併發多個執行緒,每條執行緒並行執行不同的任務。

2.4.2 可重入與執行緒安全

    執行緒安全問題都是由全域性變數及靜態變數引起的。若每個執行緒中對全域性變數、靜態變數只有讀操作,而無寫操作,一般來說,這個全域性變數是執行緒安全的;若有多個執行緒同時執行讀寫操作,一般都需要考慮執行緒同步,否則就可能影響執行緒安全。

    可重入(reentrant)函式可以被多個執行緒併發執行,而通常不會導致由於共享資料而導致結果錯誤;不可重入函式則在多個執行緒併發執行時,如果不能保持執行緒互斥,那麼就會導致結果的錯誤。對於軟體工程而言,我們應該以比較悲觀的方式去評估函式:即可重入函式不一定是執行緒安全的,可能是執行緒安全的函式就不是執行緒安全的函式,不可重入函式一定不是執行緒安全的函式。

2.4.3 執行緒安全:程式碼分析

    首先,對於執行緒安全,我們所關注的焦點一般在以下幾個方面:

      i. 所有的函式是不是都是可重入函式:分析函式有沒有訪問臨界資源,若有則必須仔細分析其互斥的處理過程;

      ii. 不同的可重入函式有沒有可能同時進入臨界區:讀寫互斥應該如何去考慮;

    下面,同樣地,通過孟寧老師所給出的程式碼,我們選擇一段程式碼來加以解釋:

    

    在需要進入臨界區時,我們對程式碼進行了加鎖操作以確保其他函式無法使用臨界區的資源,當我們退出臨界區後及時解鎖供以其他函式訪問,最後銷燬互斥鎖,這就是一個典型的執行緒安全程式碼。

3. 總結

    孟寧老師於課堂上講解的各個知識點,使我對軟體工程中的開發思想、方法有了更深一步地理解。同時,經過這篇部落格的書寫,我對這些知識的理解更上一層樓了,更為重要的是,動手能力也有所提高,在此深表感謝。

    最後,再次強調,參考資料於此:軟體工程——碼農的自我修養

 

相關文章