提問回顧與個人總結
項目 | 内容 |
---|---|
本作業所屬課程 | 2020春季軟體工程(羅傑 任健) |
本作業要求 | 回顧開學初閱讀《建構之法》的提問,并總結本學期的收獲 |
我的課程目标 | 具備一個軟體工程師所需要的素質 |
本作業幫助 | 回顧總結 |
一、第一次作業提問的回顧
- 連結到以前提問題的部落格。
- 請嘗試對自己曾經提出的問題進行解答,并闡明,是如何通過看書,實踐,或者讨論弄清楚的。
- 是否原來的問題還不明白?如果有,請分析。
- 是否産生了新的問題?如果有,請提出。
原部落格連結:【軟體工程】《建構之法》 & Git+ & CI/CD
單元測試
原文
(P26)單元測試的運作/通過/失敗/不依賴于别的測試,可以人為構造資料,以保持單元測試的獨立性。
問題
面對需要處理大量資料的子產品,人為構造資料就最造成很大的重複性工作。比如上學期寫編譯器的時候,為了測試我的詞法分析子產品的正确性,對一個十分簡單的源程式,我就需要寫上百行的期望輸出,而且單元測試運作完之後,對那些錯誤部分進行分析,發現大部分并不是我要測試的文法分析子產品的錯誤,反而而是我手動構造的期望輸出的錯誤。在對文法分析部分進行測試的時候,這種情況尤甚。有的時候為了構造樣例去寫一些測試樣例的生成器,但是生成器本身也會發生錯誤。
針對處理資料量較大的子產品,我們該怎樣手動構造測試樣例呢?還是說,我們應該換一種單元測試的粒度?
現在的了解
即使測試比較麻煩也需要構造單元測試。
雖然測試的複雜性導緻測試本身也會出錯,此時測試和待測程式是一種互相驗證。
但是相比于比較正常的單元測試,如果測試需要的樣例/環境構造比較麻煩,,我認為可以将測試工作的優先級适當降低,先完成更重要的工作。
斷言
(P70)當你覺得某事肯定如何時,就可以用斷言。
雖然書中上下文比較的是斷言和異常處理的問題,但是斷言(assert)很長時間來是我用着很糾結的地方。就我以往用斷言的經驗來看,主要在以下一些地方:
- 自己不打算寫單元測試,用斷言來确定程式運作到某個部分确實是按照我所期望的;
- 為了獲得IDE的輔助,比如當我确定某個向下轉型是安全的時候,寫了
,後面的語句需要将assert obj instanceof MyClass;
強制轉換為obj
時,IDE就會幫我添上強轉;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
目前還是個空殼子,還沒有開始是因為我需要先學一下前端技術:)