軟體工程結課總結
提問回顧與個人總結
專案 | 內容 |
---|---|
本作業所屬課程 | 2020春季軟體工程(羅傑 任健) |
本作業要求 | 回顧開學初閱讀《構建之法》的提問,並總結本學期的收穫 |
我的課程目標 | 具備一個軟體工程師所需要的素質 |
本作業幫助 | 回顧總結 |
一、第一次作業提問的回顧
- 連結到以前提問題的部落格。
- 請嘗試對自己曾經提出的問題進行解答,並闡明,是如何通過看書,實踐,或者討論弄清楚的。
- 是否原來的問題還不明白?如果有,請分析。
- 是否產生了新的問題?如果有,請提出。
原部落格連結:【軟體工程】《構建之法》 & Git+ & CI/CD
單元測試
原文
(P26)單元測試的執行/通過/失敗/不依賴於別的測試,可以人為構造資料,以保持單元測試的獨立性。
問題
面對需要處理大量資料的模組,人為構造資料就最造成很大的重複性工作。比如上學期寫編譯器的時候,為了測試我的詞法分析模組的正確性,對一個十分簡單的源程式,我就需要寫上百行的期望輸出,而且單元測試執行完之後,對那些錯誤部分進行分析,發現大部分並不是我要測試的語法分析模組的錯誤,反而而是我手動構造的期望輸出的錯誤。在對語法分析部分進行測試的時候,這種情況尤甚。有的時候為了構造樣例去寫一些測試樣例的生成器,但是生成器本身也會發生錯誤。
針對處理資料量較大的模組,我們該怎樣手動構造測試樣例呢?還是說,我們應該換一種單元測試的粒度?
現在的理解
即使測試比較麻煩也需要構造單元測試。
雖然測試的複雜性導致測試本身也會出錯,此時測試和待測程式是一種互相驗證。
但是相比於比較常規的單元測試,如果測試需要的樣例/環境構造比較麻煩,,我認為可以將測試工作的優先順序適當降低,先完成更重要的工作。
斷言
原文
(P70)當你覺得某事肯定如何時,就可以用斷言。
問題
雖然書中上下文比較的是斷言和異常處理的問題,但是斷言(assert)很長時間來是我用著很糾結的地方。就我以往用斷言的經驗來看,主要在以下一些地方:
- 自己不打算寫單元測試,用斷言來確定程式執行到某個部分確實是按照我所期望的;
- 為了獲得IDE的輔助,比如當我確定某個向下轉型是安全的時候,寫了
assert obj instanceof MyClass;
,後面的語句需要將obj
強制轉換為MyCalss
- 相當於一句註釋;
但是有單元測試的情況下,第一類的情況還需要寫斷言嗎?
現在的理解
需要,單元測試是從外部進行測試,而斷言是內部進行測試。
構造單元測試的時候雖然是可以知道實現的內部的,但是卻無法驗證內部某個點的狀態,只能驗證函式結束後的狀態是否正確。
雖然從外部看,如果通過了單元測試,就說明這個函式是正確的,不必驗證內部狀態是否如預期變化,但是在debug的時候能夠方便定位問題。
結對程式設計
原文
(P80)總之,如果運用得當,結對程式設計可以取得更高的投入產出比。
問題
這句話給我的第一感覺,就像這句話一樣:
如果方法得當,高考前一個月也可以提高300分。
雖然我的類比有些誇張,但意思是一樣的:後半句話看起來那樣美好,但是它的前提條件就有些說不太清楚了。
書中隨後介紹了結對程式設計的流程與分工,也列舉了一些注意事項,甚至還提到了一些結對程式設計中的交流技巧。但是卻忽略了一個問題:什麼樣的人/團隊適合結對程式設計呢?
後文提到了兩人合作的階段:萌芽\(\rightarrow\)磨合\(\rightarrow\)規範\(\rightarrow\)創造。但同時也指出了,並不是所有的合作都能走到最後一步,可能磨合太多後進入“解體”階段。最終走向失敗的合作有可能是是雙方的方式不對,但也可能是雙方口味不合,甚至有一方深深排斥自己工作的時候有另一個在邊上。
國內為何很少有人做結對程式設計呢?是確實不好還是屬於中國特色? - 小白的回答 - 知乎
這個回答中就提到,並不是所有人都能在交流中保持專注,也並不是所有人都喜歡在一個充滿交流的環境中工作,甚至說,很多人走上了軟體開發的道路就是因為自己需要安靜的環境來保持專注。
選擇結對程式設計,就意味著要讓兩個人去完成一個人的工作,這本身就是不利於投入產出比的,如果結對程式設計不能帶來額外的好處,就是虧的。也就是說如果一組合作夥伴快速走向解體,那麼就很可能是一次虧本的嘗試。那麼相較於關注怎樣進行結對程式設計,我們是不是更應該關注哪些人適合結對、怎樣挑選結對的夥伴呢?
現在的理解
在兩次結對程式設計作業中,我感覺結對程式設計在“攻堅”方便擁有很大的優勢,多人劃分任務進行配合的時候,當遇到難點的時候,是很難獲得其他隊友的支援的,一方面,別人要了解你遇到的問題的詳情是需要一定成本的,另一方面,別人也有自己的工作需要做,很可能不願意放下手頭的工作先去幫助別人,此時就往往只能一個人去解決一個困難的問題。但是結對程式設計的時候,面對一個困難的問題,兩個人是可以馬上開始討論交流的,這在重思考輕編碼的場景下是有效的。
但是結對程式設計也存在一些問題,一個是輕思考重編碼/配置的場景下,尤其是有現代IDE輔助的情況下,兩個人對著一臺電腦進行開發工作,很容易走神,互相影響,效率是不如分頭行動的。另一方面就是結對程式設計的環境,結對程式設計需要交流,需要發出聲音,一方面會影響到別人,一方面別人都安靜的時候兩個人交流是很尷尬且放不開的,此外,結對程式設計需要兩個人在同一時刻同一地點進行程式設計,這需要一定的妥協。不過最近CodeTogether的推出可以解決這個問題,兩個人可以分別在家中進行結對程式設計,分別使用自己的電腦閱讀程式碼也確實比一個人寫另一個人在同一塊螢幕上看體驗更佳。
典型使用者
原文
(P206)怎樣才能定義典型使用者呢?我們首先要定義使用者的角色。正如戲劇中有正面和反面角色,軟體系統中也有受歡迎的和不受歡迎的典型使用者。
問題
受歡迎的使用者是我們軟體的目標,自然需要作為典型使用者來分析。
但是為什麼也要分析不受歡迎的使用者?一方面,針對不受歡迎的使用者的一些策略,如保密、網路安全等,本身就是軟體質量需要考量的一部分,甚至是受歡迎使用者的需求,單獨把這部分使用者拎出來分析也不會起到很大的補充效果。另一方面,確定會有哪些不受歡迎的使用者並不容易,比如微信起初就沒有把淘寶連結、抖音連線、有償朋友圈轉發等視為不歡迎的,這些問題是隨著運營而產生/發現的。
對不受歡迎的使用者的分析投入產出比並不高,可以將其去掉嗎?
現在的理解
對使用者的定義是一個不斷完善的功能,隨著產品的迭代會慢慢新增新的使用者,所以因為不受歡迎的使用者不容易進行分析而不去分析是不對的。
相比於被動地考慮如何構建安全的系統,主動地考慮一個不受歡迎使用者會如何“攻擊”系統是更好的方式,站在一個攻擊者的角度更容易發現系統中的問題。
功能說明書
原文
(P215) 第一,定義好相關的概念。第二,規範好一些假設。第三,避免一些誤解,界定一些邊界條件。第四,描述主流的使用者/軟體互動步驟。第五,一些好的功能還會有副作用。第六,服務質量的說明。
問題
功能說明書的意義就在於嚴格地、無歧義的描述軟體的外部功能,講述互動的方式。但是這樣的功能說明書必定是很長的,相信大部分使用者也不會有閒心來詳細閱讀功能說明書。那麼怎樣能快速、有效地引導使用者來按照我們設計的方式來與軟體進行互動呢?這一部分應當是誰的工作呢?
現在的理解
功能說明書是嚴格而無歧義的,其最大的作用其實是作為一個標準而存在,應當是一個出現了問題來查閱的手冊。
將產品的功能展現給使用者,並不應當依靠功能說明書,而應當設計更友好的引導。並且對於使用者的引導,可以捨去很多細節,只需要展示大部分場景下的使用方法即可,當用戶需要一些細節的時候,此時使用者的主觀能動性足夠他去查閱文件。
引導使用者正確地使用產品,首先應當考慮的是軟體的設計,按鈕、選單、介面的排布,文字的描述,應當本身就具有解釋性,好的設計應當能讓使用者在沒有引導的情況下就能夠猜出如何使用特定的功能。其次,考慮一些幫助說明,比如在不直觀的功能邊上新增說明、新增?
按鈕、滑鼠懸停顯示幫助等等。最後才是比較詳細的說明書,這個說明書應當具有一定的索引功能,能夠幫助使用者快速檢索到需要的幫助資訊。
二、知識點總結
軟體工程這門學問有很多 “知識點”, 這門課強調 “做中學” - 在實踐中學習知識點。
請問你們在專案的 需求/設計/實現/測試/釋出/維護階段(一共6 個階段)中都學到了什麼“知識點”,每個階段只要說明一個知識點即可。
需求階段
需求階段最重要的就是要進行實地調查,要找到使用者真正需要的需求。這樣的需求應當滿足兩個條件:
- 有一定規模的人有這樣的需求;
- 現有的軟體服務在某些方面無法完全滿足人們這樣的需求;
當我們明確這樣的需求之後,我們才能在設計的時候有方向。比如我們做C語言的問答的時候,最開始為了環境配置、報錯資訊的處理做了額外的設計。但是當我們拿到CSDN提供的資料集後才發現大部分使用者其實需要的是對程式碼的輔助分析,大部分提問中都有程式碼,使用者更希望獲得程式碼方面的支援。這就導致alpha階段的很多設計打了水漂。
設計階段
設計階段最重要的是組員意見的充分交換。專案中,RESTful設計的時候,就主要是我一個人完成設計,然後再以類似“公示”的方式徵求意見,這就導致RESTful設計沒有經過充分的意見交換,後續需要頻繁添改介面。
實現階段
實現階段需要注意的問題是要遵循設計。開發中我們在控制層和使用者服務部分未能夠遵循設計,控制層的工作是根據許可權攔截請求,而使用者服務負責使用者的登入註冊控制,而我們在實現的時候將兩者的實現混合在了一起,使用者服務更像是服務於控制層的輔助工具,所有使用者控制全部由控制層完成,這就導致使用者服務在測試的時候比較困難。
測試階段
單元測試存在難以平衡的兩個問題,一個是測試的有效性,一個是測試的複雜度。當我們要保證測試的有效性時,我們需要為每個模組定製單元測試,要對模組可能的輸入、期望的輸出進行覆蓋,要為模組的依賴構建Mock物件,但是這樣測試本身就具有相當的複雜度,甚至可能測試一個模組的程式碼比這個模組的實現還要多。而降低測試的複雜度,進行組粒度的測試,追求覆蓋率的時候,對某些模組的測試就不夠充分,當這個模組進行復用的時候,就存在潛在的問題。
在人手和時間有限的情況下,我認為應當先聚焦於關鍵模組、容易出錯的複雜模組,優先測試實現比較複雜的模組,即使整體覆蓋率較低,也可以保證系統的正確執行。在後續有富裕的人手的時候再補充次要部分的單元測試。
釋出階段
釋出階段一個重要的是宣傳。我們組的宣傳就停留在朋友圈,並沒有吸引到多少使用者來使用,但是很多其他組製作了視訊、聯絡了相應科目的老師等等。宣傳應當能夠吸引人的注意,比如視訊、影象,而且要向潛在使用者較多的地方進行投放。
維護階段
生產環境不同於開發環境,是要實際面對使用者的隱私和潛在的攻擊的。所以要注意要禁用包含敏感資訊的日誌,系統或各個元件的許可權按需分配。在維護的時候不免需要多次進行部署操作。為了避免部署時的疏漏,有兩個方法可以幫助部署:
- 構建部署指令碼,甚至直接自動持續部署,而不是手動部署。指令碼可以反覆測試,並且只要輔助指令碼構建正確,使用指令碼部署發生疏漏的概率就遠遠小於手動部署;
- 不同環境使用不同的配置檔案。在實現的時候,將可以配置的選項做成配置檔案的形式,這樣就可以通過不同的配置檔案來區分開發環境和生產環境的配置,避免忘記修改某些引數導致的隱私洩露問題。
三、專案總結與心得
結合自己在個人專案/結對程式設計/團隊專案的經歷,談談自己的理解或心得。
一路走來,關於如何做一個“軟體”,總的來說經歷了一個從“編碼是最重要的一步”到“編碼只是重要的一步”的轉變,不可否認完成一個軟體,編碼絕對是主要花費時間的事情,但是有很多其他需要做的事情往往也起著決定性作用。
我簡單用這樣一個公式來描述一個軟體的質量:
\[軟體質量 = 設計質量 \times 編碼質量 \times 運維質量 \]這樣就可以比較好地描述我現在對於編碼和軟體的理解,編碼固然是重要的一環,但是差勁的設計或者差勁的維護依然可以讓這個“程式”成為一個質量低下的“軟體”——沒人用,或者使用者體驗不佳,無法長期留住使用者。設計、編碼、運維這三個之間是乘算關係,就是為了說明,其中每一步都做得好,軟體整體就會變得特別好,只要一步做得很差,就能導致整個軟體被拖累,一個軟體的生命週期中,我們應當兼顧著三個層面,不應當過度犧牲某一步。
設計,包括需求分析、功能設計、結構設計,以及UI設計。
- 需求分析,是面向“人”,即使用者的,要做的是從日常的觀察中找到潛在的需求,然後通過實際的調研明確具體的使用者痛點,以及市場現狀,要明確使用者需要軟體在哪些方面做得好,那些方面達到“及格”水準就行,以及確認“及格”水準是個什麼樣子。
- 功能設計就是通過一系列功能的設計,功能間的配合,來滿足使用者的需求,羅列出需要哪些功能,勾勒出一個大致的形狀。
- 結構設計就是從編碼出發,設計一個高內聚低耦合的可維護系統,能夠提供設計中的需求。
- UI設計,互動設計,包括軟體的互動邏輯,以及使用者看到的介面,這是我最近漸漸意識到一個重要的方面。我們的團隊專案,alpha階段的UI設計得很糟糕,不僅功能的排布比較困難,而且看起來就很簡陋,沒有使用的慾望,但是beta階段我們專門開討論會研究了UI的設計,一起畫出了期望的UI的樣子,beta階段不僅功能的排布比較從容,而且使用者介面就討喜得多。好的介面設計可以讓使用者直觀地瞭解到互動方法,好的互動設計應當是自然而明確的。在人們審美越來越高的今天,UI的設計也越發地重要,蘋果就憑藉著流場而自然的軟體UI設計獲得了大量使用者的青睞,無論IOS還是macOS,軟體的加分確實成為了蘋果佔據如今地位重要的一環。如今各大手機廠商也都注意到UI設計的重要性,從以前專注於硬體的差異化,慢慢到現在開始注意使用者互動體驗上,都說明一個好的UI十分重要。
運維,維護,是對於程式中存在的問題的修復、新的功能的新增、使用者問題的反饋,運營,則是要加上程式配套的說明書/教程、宣傳。在完成團隊專案的時候,我們有很多功能要藉助第三方的類庫/軟體來實現,在同類別進行比較的時候,我是按照“哪一個軟體/類庫能夠儘快用上”作為標準來選擇的,但是在最後回過頭來看,被我選中的軟體/類庫,都是其文件/引導做的比較好的。在更大的範圍內,一個友好的歡迎頁,容易找到的說明書文件,能夠(相對)及時獲得反饋的技術支援,能夠很大程度上地吸引使用者使用我們的軟體。
而編碼,則是考研開發人員對技術的掌握能力,將設計落地實現的過程。我把測試歸在編碼質量中,因為測試是驗證程式是否實現了規劃的功能的依據,測試是從使用者的角度來考慮一個模組/程式,開發是從模組內部的實現來考慮這個模組/程式,開發和測試應當是相輔相成的。API文件也應當在編碼這一步產生,API文件不同於設計階段的文件,它的目的是為了程式碼的可維護性,讓後來接受專案的人或未來的自己能夠明白一個模組的作用,它是程式碼的自然語言抽象,也應當和開發工作繫結在一起的。一個能夠持續的編碼過程,應當就是開發、測試、API文件同時推進的過程。
經過這一學期的軟工的學習和嘗試,讓我對軟體的生命週期有了新的認識,再加上這學期的團隊專案有些意難平(一方面最後選擇的題目我確實有些不喜歡,另一方面時間有些短,很多技術也是現學的,實現有些糟糕),讓我有些手癢癢,所以我最近在打算開發一個線上的工具集,作為長期的一個練手專案。
我給它的定位就是一個小的工具集,能夠解決很多不大不小的問題,來鍛鍊我自己不斷髮現痛點、給出解決方案的能力,也鍛鍊一下我對軟體生命週期的控制。我希望它能夠解決那些不夠大不夠頻繁,以至於特地為其開發一個軟體有些浪費,但是又確實存在的問題。
當前能夠想到的需求有這些:
- 本地用markdown寫完部落格之後,上傳部落格的時候,需要將圖片一個一個單獨傳上去,而無法保留原本的路徑,我希望有一個圖床工具,能夠將和圖片一起打包成zip的markdown部落格進行解析,將圖片適當壓縮儲存在圖床中,將markdown文件中的相關路徑替換成圖床的url,這樣在上傳部落格的時候只需要將zip上傳到圖床,然後將轉換後的markdown貼上進部落格網站即可。將來如果要建部落格站,也可以呼叫它來獲得更好的服務。
- 檔案編碼的識別和統一工具。當代碼中存在中文的時候,檔案的編碼就比較重要了(比如上學期寫編譯器的時候因為要輸出中文,所以上傳OJ的時候要統一編碼),但是主流IDE等工具作為“英語世界”的開發者開發的,對於檔案編碼的支援其實並不是那麼好,比如無法方便地將工程中已經存在的檔案的編碼轉換為特定的編碼。我希望有一個轉換工具,能夠批量識別檔案的編碼並轉換成特定編碼,我希望能夠指定字符集,比如常用簡體中文,能夠排除掉一些備選的編碼格式,這個格式似乎合法,但實際上按照這個編碼格式來轉碼會把整個檔案的中文程式設計一坨人類無法閱讀的東西。
後續可能還會有新的需求被發現,新增進來。
結構設計上,我計劃採用微服務的思路,有一個統一的使用者管理服務,有一個統一的主頁和導航,然後每一個小工具都是一個微服務,這樣在新增新的工具的時候不至於大動干戈,而且這樣也利於其它感興趣的開發者參與開發,他們完全可以獨立地開發一個微服務,而不至於對其餘部分造成破壞。
宣傳規劃上,一方面通過SEO(搜尋引擎優化),讓搜尋引擎能夠爬取到小工具的資訊,並且爬取到的資訊能夠匹配儘可能多的相關關鍵詞,另一方面通過知乎、百度經驗、百度知道、思否、部落格園、csdn、簡書、B站等問答社群、內容社群來引流。
物理支援上,前些天趁著618優惠購買了一些雲資源,打算等專案推進一段時間後再進行備案。
專案管理上,使用github,雖然網路時不時有些不穩定,需要科學方法,但是相比於國內比較方便的gitee,github上大多數功能面向開源軟體都是免費的,尤其是CI/CD工具,而gitee在這方面似乎都是收費的。而gitlab的話,如果在雲伺服器搭建一個gitlab似乎會佔用不少資源,而且對部署的探索搞不好會把伺服器搞崩幾次,所以最後還是選擇了github來作為主倉庫,考慮在gitee建立映象倉庫。
這個專案可能會持續很久,可能會有很多年,希望自己能夠堅持下去。
倉庫地址:https://github.com/SnowPhoenix0105/ToolSite
目前還是個空殼子,還沒有開始是因為我需要先學一下前端技術:)