提問回顧與個人總結
項目 | 內容 |
---|---|
本作業所屬課程 | 2020春季軟件工程(羅傑 任健) |
本作業要求 | 回顧開學初閱讀《構建之法》的提問,並總結本學期的收獲 |
我的課程目標 | 具備一個軟件工程師所需要的素質 |
本作業幫助 | 回顧總結 |
一、第一次作業提問的回顧
- 鏈接到以前提問題的博客。
- 請嘗試對自己曾經提出的問題進行解答,並闡明,是如何通過看書,實踐,或者討論弄清楚的。
- 是否原來的問題還不明白?如果有,請分析。
- 是否產生了新的問題?如果有,請提出。
原博客鏈接:【軟件工程】《構建之法》 & Git+ & CI/CD
單元測試
原文
(P26)單元測試的運行/通過/失敗/不依賴於別的測試,可以人為構造數據,以保持單元測試的獨立性。
問題
面對需要處理大量數據的模塊,人為構造數據就最造成很大的重復性工作。比如上學期寫編譯器的時候,為了測試我的詞法分析模塊的正確性,對一個十分簡單的源程序,我就需要寫上百行的期望輸出,而且單元測試運行完之后,對那些錯誤部分進行分析,發現大部分並不是我要測試的語法分析模塊的錯誤,反而而是我手動構造的期望輸出的錯誤。在對語法分析部分進行測試的時候,這種情況尤甚。有的時候為了構造樣例去寫一些測試樣例的生成器,但是生成器本身也會發生錯誤。
針對處理數據量較大的模塊,我們該怎樣手動構造測試樣例呢?還是說,我們應該換一種單元測試的粒度?
現在的理解
即使測試比較麻煩也需要構造單元測試。
雖然測試的復雜性導致測試本身也會出錯,此時測試和待測程序是一種互相驗證。
但是相比於比較常規的單元測試,如果測試需要的樣例/環境構造比較麻煩,,我認為可以將測試工作的優先級適當降低,先完成更重要的工作。
斷言
原文
(P70)當你覺得某事肯定如何時,就可以用斷言。
問題
雖然書中上下文比較的是斷言和異常處理的問題,但是斷言(assert)很長時間來是我用着很糾結的地方。就我以往用斷言的經驗來看,主要在以下一些地方:
- 自己不打算寫單元測試,用斷言來確定程序運行到某個部分確實是按照我所期望的;
- 為了獲得IDE的輔助,比如當我確定某個向下轉型是安全的時候,寫了
assert obj instanceof MyClass;
,后面的語句需要將obj
強制轉換為MyCalss
時,IDE就會幫我添上強轉; - 相當於一句注釋;
但是有單元測試的情況下,第一類的情況還需要寫斷言嗎?
現在的理解
需要,單元測試是從外部進行測試,而斷言是內部進行測試。
構造單元測試的時候雖然是可以知道實現的內部的,但是卻無法驗證內部某個點的狀態,只能驗證函數結束后的狀態是否正確。
雖然從外部看,如果通過了單元測試,就說明這個函數是正確的,不必驗證內部狀態是否如預期變化,但是在debug的時候能夠方便定位問題。
結對編程
原文
(P80)總之,如果運用得當,結對編程可以取得更高的投入產出比。
問題
這句話給我的第一感覺,就像這句話一樣:
如果方法得當,高考前一個月也可以提高300分。
雖然我的類比有些誇張,但意思是一樣的:后半句話看起來那樣美好,但是它的前提條件就有些說不太清楚了。
書中隨后介紹了結對編程的流程與分工,也列舉了一些注意事項,甚至還提到了一些結對編程中的交流技巧。但是卻忽略了一個問題:什么樣的人/團隊適合結對編程呢?
后文提到了兩人合作的階段:萌芽\(\rightarrow\)磨合\(\rightarrow\)規范\(\rightarrow\)創造。但同時也指出了,並不是所有的合作都能走到最后一步,可能磨合太多后進入“解體”階段。最終走向失敗的合作有可能是是雙方的方式不對,但也可能是雙方口味不合,甚至有一方深深排斥自己工作的時候有另一個在邊上。
國內為何很少有人做結對編程呢?是確實不好還是屬於中國特色? - 小白的回答 - 知乎
這個回答中就提到,並不是所有人都能在交流中保持專注,也並不是所有人都喜歡在一個充滿交流的環境中工作,甚至說,很多人走上了軟件開發的道路就是因為自己需要安靜的環境來保持專注。
選擇結對編程,就意味着要讓兩個人去完成一個人的工作,這本身就是不利於投入產出比的,如果結對編程不能帶來額外的好處,就是虧的。也就是說如果一組合作伙伴快速走向解體,那么就很可能是一次虧本的嘗試。那么相較於關注怎樣進行結對編程,我們是不是更應該關注哪些人適合結對、怎樣挑選結對的伙伴呢?
現在的理解
在兩次結對編程作業中,我感覺結對編程在“攻堅”方便擁有很大的優勢,多人划分任務進行配合的時候,當遇到難點的時候,是很難獲得其他隊友的支持的,一方面,別人要了解你遇到的問題的詳情是需要一定成本的,另一方面,別人也有自己的工作需要做,很可能不願意放下手頭的工作先去幫助別人,此時就往往只能一個人去解決一個困難的問題。但是結對編程的時候,面對一個困難的問題,兩個人是可以馬上開始討論交流的,這在重思考輕編碼的場景下是有效的。
但是結對編程也存在一些問題,一個是輕思考重編碼/配置的場景下,尤其是有現代IDE輔助的情況下,兩個人對着一台電腦進行開發工作,很容易走神,互相影響,效率是不如分頭行動的。另一方面就是結對編程的環境,結對編程需要交流,需要發出聲音,一方面會影響到別人,一方面別人都安靜的時候兩個人交流是很尷尬且放不開的,此外,結對編程需要兩個人在同一時刻同一地點進行編程,這需要一定的妥協。不過最近CodeTogether的推出可以解決這個問題,兩個人可以分別在家中進行結對編程,分別使用自己的電腦閱讀代碼也確實比一個人寫另一個人在同一塊屏幕上看體驗更佳。
典型用戶
原文
(P206)怎樣才能定義典型用戶呢?我們首先要定義用戶的角色。正如戲劇中有正面和反面角色,軟件系統中也有受歡迎的和不受歡迎的典型用戶。
問題
受歡迎的用戶是我們軟件的目標,自然需要作為典型用戶來分析。
但是為什么也要分析不受歡迎的用戶?一方面,針對不受歡迎的用戶的一些策略,如保密、網絡安全等,本身就是軟件質量需要考量的一部分,甚至是受歡迎用戶的需求,單獨把這部分用戶拎出來分析也不會起到很大的補充效果。另一方面,確定會有哪些不受歡迎的用戶並不容易,比如微信起初就沒有把淘寶鏈接、抖音連接、有償朋友圈轉發等視為不歡迎的,這些問題是隨着運營而產生/發現的。
對不受歡迎的用戶的分析投入產出比並不高,可以將其去掉嗎?
現在的理解
對用戶的定義是一個不斷完善的功能,隨着產品的迭代會慢慢添加新的用戶,所以因為不受歡迎的用戶不容易進行分析而不去分析是不對的。
相比於被動地考慮如何構建安全的系統,主動地考慮一個不受歡迎用戶會如何“攻擊”系統是更好的方式,站在一個攻擊者的角度更容易發現系統中的問題。
功能說明書
原文
(P215) 第一,定義好相關的概念。第二,規范好一些假設。第三,避免一些誤解,界定一些邊界條件。第四,描述主流的用戶/軟件交互步驟。第五,一些好的功能還會有副作用。第六,服務質量的說明。
問題
功能說明書的意義就在於嚴格地、無歧義的描述軟件的外部功能,講述交互的方式。但是這樣的功能說明書必定是很長的,相信大部分用戶也不會有閑心來詳細閱讀功能說明書。那么怎樣能快速、有效地引導用戶來按照我們設計的方式來與軟件進行交互呢?這一部分應當是誰的工作呢?
現在的理解
功能說明書是嚴格而無歧義的,其最大的作用其實是作為一個標准而存在,應當是一個出現了問題來查閱的手冊。
將產品的功能展現給用戶,並不應當依靠功能說明書,而應當設計更友好的引導。並且對於用戶的引導,可以舍去很多細節,只需要展示大部分場景下的使用方法即可,當用戶需要一些細節的時候,此時用戶的主觀能動性足夠他去查閱文檔。
引導用戶正確地使用產品,首先應當考慮的是軟件的設計,按鈕、菜單、界面的排布,文字的描述,應當本身就具有解釋性,好的設計應當能讓用戶在沒有引導的情況下就能夠猜出如何使用特定的功能。其次,考慮一些幫助說明,比如在不直觀的功能邊上添加說明、添加?
按鈕、鼠標懸停顯示幫助等等。最后才是比較詳細的說明書,這個說明書應當具有一定的索引功能,能夠幫助用戶快速檢索到需要的幫助信息。
二、知識點總結
軟件工程這門學問有很多 “知識點”, 這門課強調 “做中學” - 在實踐中學習知識點。
請問你們在項目的 需求/設計/實現/測試/發布/維護階段(一共6 個階段)中都學到了什么“知識點”,每個階段只要說明一個知識點即可。
需求階段
需求階段最重要的就是要進行實地調查,要找到用戶真正需要的需求。這樣的需求應當滿足兩個條件:
- 有一定規模的人有這樣的需求;
- 現有的軟件服務在某些方面無法完全滿足人們這樣的需求;
當我們明確這樣的需求之后,我們才能在設計的時候有方向。比如我們做C語言的問答的時候,最開始為了環境配置、報錯信息的處理做了額外的設計。但是當我們拿到CSDN提供的數據集后才發現大部分用戶其實需要的是對代碼的輔助分析,大部分提問中都有代碼,用戶更希望獲得代碼方面的支持。這就導致alpha階段的很多設計打了水漂。
設計階段
設計階段最重要的是組員意見的充分交換。項目中,RESTful設計的時候,就主要是我一個人完成設計,然后再以類似“公示”的方式征求意見,這就導致RESTful設計沒有經過充分的意見交換,后續需要頻繁添改接口。
實現階段
實現階段需要注意的問題是要遵循設計。開發中我們在控制層和用戶服務部分未能夠遵循設計,控制層的工作是根據權限攔截請求,而用戶服務負責用戶的登錄注冊控制,而我們在實現的時候將兩者的實現混合在了一起,用戶服務更像是服務於控制層的輔助工具,所有用戶控制全部由控制層完成,這就導致用戶服務在測試的時候比較困難。
測試階段
單元測試存在難以平衡的兩個問題,一個是測試的有效性,一個是測試的復雜度。當我們要保證測試的有效性時,我們需要為每個模塊定制單元測試,要對模塊可能的輸入、期望的輸出進行覆蓋,要為模塊的依賴構建Mock對象,但是這樣測試本身就具有相當的復雜度,甚至可能測試一個模塊的代碼比這個模塊的實現還要多。而降低測試的復雜度,進行組粒度的測試,追求覆蓋率的時候,對某些模塊的測試就不夠充分,當這個模塊進行復用的時候,就存在潛在的問題。
在人手和時間有限的情況下,我認為應當先聚焦於關鍵模塊、容易出錯的復雜模塊,優先測試實現比較復雜的模塊,即使整體覆蓋率較低,也可以保證系統的正確運行。在后續有富裕的人手的時候再補充次要部分的單元測試。
發布階段
發布階段一個重要的是宣傳。我們組的宣傳就停留在朋友圈,並沒有吸引到多少用戶來使用,但是很多其他組制作了視頻、聯系了相應科目的老師等等。宣傳應當能夠吸引人的注意,比如視頻、圖像,而且要向潛在用戶較多的地方進行投放。
維護階段
生產環境不同於開發環境,是要實際面對用戶的隱私和潛在的攻擊的。所以要注意要禁用包含敏感信息的日志,系統或各個組件的權限按需分配。在維護的時候不免需要多次進行部署操作。為了避免部署時的疏漏,有兩個方法可以幫助部署:
- 構建部署腳本,甚至直接自動持續部署,而不是手動部署。腳本可以反復測試,並且只要輔助腳本構建正確,使用腳本部署發生疏漏的概率就遠遠小於手動部署;
- 不同環境使用不同的配置文件。在實現的時候,將可以配置的選項做成配置文件的形式,這樣就可以通過不同的配置文件來區分開發環境和生產環境的配置,避免忘記修改某些參數導致的隱私泄露問題。
三、項目總結與心得
結合自己在個人項目/結對編程/團隊項目的經歷,談談自己的理解或心得。
一路走來,關於如何做一個“軟件”,總的來說經歷了一個從“編碼是最重要的一步”到“編碼只是重要的一步”的轉變,不可否認完成一個軟件,編碼絕對是主要花費時間的事情,但是有很多其他需要做的事情往往也起着決定性作用。
我簡單用這樣一個公式來描述一個軟件的質量:
這樣就可以比較好地描述我現在對於編碼和軟件的理解,編碼固然是重要的一環,但是差勁的設計或者差勁的維護依然可以讓這個“程序”成為一個質量低下的“軟件”——沒人用,或者用戶體驗不佳,無法長期留住用戶。設計、編碼、運維這三個之間是乘算關系,就是為了說明,其中每一步都做得好,軟件整體就會變得特別好,只要一步做得很差,就能導致整個軟件被拖累,一個軟件的生命周期中,我們應當兼顧着三個層面,不應當過度犧牲某一步。
設計,包括需求分析、功能設計、結構設計,以及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
目前還是個空殼子,還沒有開始是因為我需要先學一下前端技術:)