軟工實踐寒假作業(2/2)


這個作業屬於哪個課程 2021春軟件工程實踐S班 (福州大學)
這個作業要求在哪里 作業要求的鏈接
這個作業的目標 閱讀《構建之法》並提問,完成詞頻統計個人作業,學習使用git以及github,記錄PSP表格等
其他參考文獻 《構建之法》,《碼出高效_阿里巴巴Java開發手冊》

一.閱讀《構建之法》並提問

1.如何定義技能的反面

我看了第二章的技能的反面-魔方和模仿這一節內容,其中說到:

Bill 說技能的反面是  ”Problem Solving”  – “解決問題”, 這個聽起來有點繞,我們看看IT 人士熟悉的一個例子吧。  一個IT 專業的大學生來面試,  簡歷上寫“技能:  精通 Visual Studio C# 編程”。於是面試官請他實際用VS IDE 寫一段程序 (冒泡排序)。一個“不精通”的面試者的編程過程實際上就是一個“解決問題”的過程。例如:
·         嗯, 怎么開始一個C# 的命令行程序呢?  
·         定義數組是怎么弄的?  是“int [] arr”還是“int  arr[]”, 還是 ArrayList,還是 Array 。哦, 我平時都是上網查的.  哦, 我不知道還有 MSDN 網站。
·         嗯, 為什么編譯沒過呢,  哦, 這里少一個分號。
·         嗯, 怎么設斷點? 怎么定義命令行參數? 額, 我要查一查…
 
你發現他把時間都花在“解決 (低層次) 問題”上了, 你想考察的“算法技能”、“C#  程序設計技能” 都無暇顧及。注意, 這是在他認為非常精通的編程工具和編程語言中出現這樣的問題。你要這樣的員工么?
那怎么提高技能呢?  答案很簡單, 通過不斷的練習, 把那些低層次的問題都解決了, 變成不用經過大腦的自動操作, 然后才有時間和腦力來解決較高層次的問題。年輕學生都志向遠大, 上了一些課, 就很想解決高層次的問題。我最近碰到一些學生就非常想做高層次的“科研”,覺得“工程”是基礎, 沒意思。而且我“已經知道怎么做了”,
從科研,或者理論的高度上說, 所有的“技能”都能總結成簡單的”已經知道怎么做了”

我不禁對在軟件編程中為了達到夠格稱之為技能,我們對一個程序語言能夠充分掌握的飽和度需求產生疑惑,我的困惑點在於:以我目前的個人編程經驗和通過與其他同學的交流中總結,大部分同學在運用一些編程工具時都會遇到一些之前尚未非常清晰記住或者因為記憶問題而需要去查閱相關網站或者手冊來輔助自己的代碼工作,這點和書中描述的不經過大腦的自動操作還是有一定差距的.那么,如何准確定義是否真的掌握一門語言呢?在我看來,一個程序員的學習生涯中必定是要掌握經歷多種語言的,考慮到人的記憶所限問題,我想這種必要的查閱步驟還是很有必要的,特別是對於那些工作的語言不局限於一種的程序員,這點在我認為應該不影響其稱這些語言為他所掌握的技能,猶記大一上學期的C語言老師說,這些代碼過程中適量的查閱是很正常很普遍的,因此,我想,對於編程技能而言,能夠短暫迅速地在不過度影響效率的前提下通過查閱的方式達到一個能夠自由方便解決問題的程度應該就可以稱之為”技能”了

2.如何把控反饋

我看了第三章的把反饋做成漢堡包的內容,其中說到:

先來一片面包, 做好鋪墊, 例如可以從雙方的共同點, 團隊共同的願景講起, 讓對方覺得處於一個安全的環境。
再把肉放上,這時就可以把 建設性的意見 (constructive feedback) 油炸好, 加上生菜, 佐料等。
怎么准備這塊肉也有講究: 
我們常說 [feedback], 但是在提供反饋時, 不宜完全沉溺於過去的陳年谷子爛芝麻, 給別人做評價, 下結論。  這樣會造成一種 [你就是做得不好, 我恨你] 的情緒。
我們可以調整一個角度, 把 [feedback], 變成 [feedforward], 強調 [過去你做得不夠, 但是我們以后可以做得更好]
 
在技術團隊里, 我們的反饋還是要着重於 [行為和后果] 這一層面, 不要貿然深入到 [習慣和動機], [本質]. 除非情況非常嚴峻, 需要觸動別人內心深處, 讓別人懸崖勒馬。
 
然后再來一片面包, 蓋上。 這時候可以呼應開頭, 鼓勵對方把工作做好。

看完這段,我的感觸頗深,縱觀自己到目前為止的學習生涯,對待與同學,合作伙伴的平等交流一直是自己比較在意且一直在努力改進的部分,尤其是在面對接下來需要一直打交道的團隊合作,這個項目完成過程中必不可少的環節,我深感交流話術的重要性,文中提到的把反饋做成漢堡包確實是一個十分有效的方法,在溝通時通過一系列的”佐料”將自己的主要內容也就是動機包裹在一系列美麗話術中以讓對方能夠接受你的想法,但我仔細斟酌了自己生活中遇到的讓自己達到一種所謂溝通奔潰的環節,其中又有一些讓我感到束手無策的情況.在心理學上,情緒和理性在大腦中是一個並存的邏輯概念,在一些特殊情況下,你很難做到把控兩者的平衡,也就是當你的情緒奔潰,即處於相當憤怒的情況下,要按照既定的漢堡包規則來表達自己的想法或者接受他人的想法是因人而異且對部分人來說基本是做不到的,而在我們的項目合作中可能有時就會遇到這樣的成員,那么我們又應該如何應對呢?此外,我還注意到,雖然漢堡包”包裹”的肉片使得整個漢堡包”精致”且”易於接受”,但是,我們不難排除遇到在團隊合作中存在喜歡緊緊咬着你說的屬於一小塊”生菜”的內容不放,而特意忽略了你想表達的主要動機,那么在面對這樣的特例時,用漢堡包包裹的話術雖然美麗但似乎沒法直傳達意,但我們又想有一定包裝,漂亮地說出自己地想法,又應該怎么做呢?我十分期待能創造像文中舉得一些科技牛人那樣良好共同合作的體驗,所以個人的小小結論就是,先用漢堡待之,無效則加以直白”生肉”,若再次無效,則宣告溝通失敗,另謀合作伙伴.

3.如何對待項目中不同動機的成員

我讀了第五章的動物世界的故事,其中是這么說的:

在一個團隊中,  不同的成員來自五湖四海, 為了一個共同的目的, 走到一起來了 (至少表面上是這樣). 在一起吃飯的時候大家意氣風發, 群情激奮,但是不同的人對於團隊的承諾是不一樣的

有些人是 豬 - 他們或者辭掉了工作, 投入創業中; 或者這一門軟件工程課是他們的必修課, 他們一定要拿到高分, 才能提高自己的GPA, 申請到好學校。 對他們來說, 要想項目成功, 他們要拿出自己身上的肉, 背水一戰; 一旦失敗, 自己的老本也賠進去了.  他們的投入級別是 - 全身心投入 (committed).
 
有些人是 雞 - 他們能做重要的貢獻, 但是項目一旦失敗, 他們的損失並不大, 他們的生活還可以繼續下去。例子: 有些人周末來給項目幫忙, 平時自己上班; 或者是選修軟件工程課; 或者他們已經保研, 只要這門課混及格就行。 他們的投入級別是 - 參與 (involved).
 
有些人是 鸚鵡 - 他們有漂亮的羽毛, 能說會道, 聯系廣泛, 能提出很多建議, 很多點子. 但是他們不執行, 除了一些人雲亦雲的觀點和一些關於架構的空談之外, 他們沒有其他投入.  一旦項目失敗, 他們就會飛到另一個項目中去。 他們的投入級別是 – 圍觀 (bystander).

這三者在一個項目中的定位讓我對項目的運行產生了一定的思考.毫無疑問的是,在一個項目招兵買馬的階段,吸引來的是各個定位的人才,而評判的標准大部分是對技術的考察,當然這也是開始階段唯一能夠有所評判的東西,三個定位:committed,involved,bystander,在技術上都可以是各自的專精者,然而卻對項目的作用天差地別,他們的態度定位是在日后的項目進行過程中,遇阻后才可以體現的.那么,當我們作為一個項目的負責人時,在發現了各個成員的定位時,應該如何應對呢?在我的理解范圍,這就像是開公司的過程,起步階段,由於缺少資金,需要納入各個股東的部分資金,共享獲得的勞動成果,然而部分任職股東除了提供了那小部分的啟動資金,卻在公司起步階段作威作福,自詡老板,讓團隊的氛圍呈現一種負面情況,那么,當公司進行業務拓展,新划公司時,executioner就將這類股東剔除,保留幾個核心股東就可,純粹在所需時尋所求即可.這和項目的進行是一樣的,由於開始階段,可能缺少相應的idea和經驗,那么廣納人才,即使是為了involved的人員也是沒問題的,也許只是一個泛泛的意見也許就能給團隊提供靈感和發現錯誤,但當項目進行相對成熟,或流水線化時,必要的時候就可以除去僅僅是involved的人員,因為他們對團隊的貢獻已經約為零了,這時再把committed 提拔到項目更核心部分進行運行,相信會事半功倍.

4.我們應該如何看待創新

我看了第八章魔方的故事,其中這樣說到:

有意思的是, 同學發現小飛的口訣 (號稱 C# 方法) 和果凍的“秘訣”(號稱Java 秘訣) 有很大的不同, 雖然它們都能最后達到六面, 但是小飛的口訣是一層一層地實現六面; 而果凍的秘訣是先把每一面中間的十字做成同一顏色, 然后再解決四角的問題。   小飛為此和果凍在 <王屋村學報>, <移山新技術> 上展開了持久的論戰,  爭執孰優孰劣。  與此同時, 一旦口訣成了大路貨, 大家都知道魔方的玩法, 各人能差異化的, 就是執行力 – 就是看誰扭得快。課間的時候, 一些同學都在咔嚓咔嚓地轉魔方,激烈的競爭讓有些同學玩魔方手都酸了, 退出了競爭。大家通過實踐發現, 無論是小飛的方法還是果凍的秘訣都不是關鍵, 手勁巧, 魔法轉得快, 加上一些運氣, 就玩得快。   而且, 圍觀玩魔方的女同學漸漸少了。

對此我產生了對創新需求的一個思考.首先,我們如何定義創新?似乎這並不是根本定義上的,從未出現,而應該是根據區域限制后的存在與否來評斷,我們在做項目的需求分析過程中,特別是軟件設計時,很重要的一步就是對創新需求的分析,那我們應該如何對這部分進行考量呢?我想可以從兩個層面進行入手.第一層面,這部分產品需求是否已經有軟件可以滿足了?如果沒有,那么我們的產品就是建立在一個無人競爭的狀態,因為我們的設計就應該是獨特的,周全的,可以吸引到目標用戶的,功能齊全的新創意可以讓產品在開始之后就立於一個領先一大段的位置,並擁有自己的專利,我想,這是我們在一個完全新意的產品過程中應該考慮的.第二,當我們的產品在不產生版權沖突的情況下已經有了同類競品,那么我們的產品就可以改變方向,以完善,脫穎而出為目標,做得比他人好,就可以算作創意需求的滿足,比如,amazon在全球購物上都有一個很好的成果,在很多地方都超過當年的電商平台,其重要的一點就是產品質量和維度的保證,當然在國內它和阿里是無法相比的,而阿里做得創新性很好的一點就是售后物流客服的完善和信息的大數據存儲,這也是在有競品時的需求分析應該考慮的.

5.如何面對"事后諸葛亮"

我看了第十章的諸葛亮會議,其中是這么說的:

產品發布了,大家松了一口氣。阿超建議大家開一個總結會議,就是事后諸葛亮會議。會議請公司的秘書小芳主持並作記錄。為了讓大家能暢所欲言,阿超和大牛沒有參加會議。為了活躍氣氛,小芳還買了零食、飲料、河曲啤酒等。
 
阿超給小芳一個討論的模板,同時也囑咐小芳不一定要拘泥於模板,要見機行事,根據會議的進展靈活地變動計划。要牢記會議的核心問題是“如果你可以重新來過,什么方面可以做得更好?" 另外, 在問 “為什么” 的時候, 要多問幾次,  層層推進, 找到問題的根源。
例如: 軟件發布后用戶報告了一個大問題。 ”為什么?"
因為程序沒有考慮某種邊界條件.  "為什么在測試階段沒有測試出來?"
因為這個代碼是測試的最后階段才加進去的。  “為什么不通知PM/Test?”
因為dev 認為沒有問題的, 很簡單的修改。 "為什么不通知別人?"
因為dev 認為那些都是軟件工程無聊的規定... dev 是大牛人, 不必遵守的。 “為什么?!”  
 
軟件工程課的目的,主要是讓大家通過做項目,學到軟件工程的知識,而不是低水平重復, 有些團隊在 alpha 階段用比較低水平的方法做了幾個功能, beta 階段還是用比較低水平的辦法,又做幾個功能,  感覺這些同學失去了學習的機會。  寧可少做一些功能, 也要把 單元測試,架構,代碼規范,等提高。

這塊內容讓我十分有共鳴,回顧自己近三年來的代碼生涯,這種事后諸葛亮會議在我看來是十分重要且必須的,它對我的總結和知識的梳理是非常重要的一環,但我通過自身的總結經驗來看,還是對此有所疑惑:我應該怎么提”為什么”?每一份代碼項目的完成必定是經過糾正改錯的,這也是c語言老師所說的對代碼能力提高很有幫助的地方,有時候debug花去的時間要遠遠超過coding 的時間,但在一份正確的項目完成之后,我應該怎么應對這其中更深處暴露的 問題呢?不局限於淺顯的ArrayIndexOutofOrder,我想我應該經常問自己的是,為什么你會這么做?為什么你可以用這樣的思考方式,這對我的代碼思維模式應該是非常重要的,尤其是在面對多種語言學習之后,比如OOP這種思想幾乎是大部分情況下的選擇,我想,我應該對這種算法的”為什么”表現地更加犀利才可以做到代碼地融匯貫通,因此在問”為什么”的時候,我認為,應該偏向的是更本質,更細膩的問題:你為什么會有這個模式,如何建設正確的固有思維,習慣重塑等等,是我認為事后諸葛亮會議非常有意義的地方

附加題:趣事

1.蒸汽計算機

作為公認的編程之父,Charles Babbage發明了世界上首批計算機之一。他將這台新設備稱為分析引擎。其體積超過一棟房屋,由六台蒸汽機驅動並使用打孔卡進行編程。分析引擎有四大主要組成部分:1.轉盤——相當於現代計算機中的CPU;2.存儲——相當於現代計算機中的內存與存儲介質;3.讀取器——相當於輸入機制;4.打印機——用於實現信息輸出。

2.無"毒"病毒

史上第一款電腦病毒,竟然是由防御技術專家Fred Cohen親手設計出來的。他創造電腦病毒的目的僅僅是為了證明程序對電腦感染的可行性,從未希望借此對電腦造成任何危害。但這款程序卻能夠對電腦進行感染,並且能通過軟盤等移動介質在不同計算機之間進行傳播,因而命名為病毒。后來,他又創造出一種主動式電腦病毒,主要目的是幫助電腦用戶找到未受感染可執行文件。

Comment:

在我們的認知中,經常會對一些我們已經習慣的名詞概念有着自己的固定的既有思維和印象,但追根溯源之后,有時會發現很多大相徑庭的根源和背景,我分享的兩篇內容就是如此,它們都是脫離我們的常規認知中的內容,但仔細斟酌,從這一層面推敲它,卻又很有說服力,只是讓故事的發展變得十分戲劇化,但歷史有時就是這樣的,許許多多的如今都來源於當初戲劇化的一個開始,蝴蝶效應出了五花八門的未來,好比一些認知中毫無關聯的語言,但在發展過程中,卻會因為一些特定的歷史事件,比如戰爭,而產生關聯,法語,英語,和日耳曼系語言就是例子.站在這個角度,也許我們在歸納之時,給予考究不一樣的思考角度和思維層面會給我們不一樣的驚喜.

二.Part2:WordCount編程

1.我的githhub項目地址

2.PSP表格

PSP2.1 Personal Software Process Stages 預估耗時(分鍾) 實際耗時(分鍾)
Planning 計划 20 25
• Estimate • 估計這個任務需要多少時間 20 25
Development 開發 1085 1375
• Analysis • 需求分析 (包括學習新技術) 400 450
• Design Spec • 生成設計文檔 40 60
• Design Review • 設計復審 20 20
• Coding Standard • 代碼規范 (為目前的開發制定合適的規范) 25 25
• Design • 具體設計 40 60
• Coding • 具體編碼 500 700
• Code Review • 代碼復審 60 60
• Test • 測試(自我測試,修改代碼,提交修改) 100 90
Reporting 報告 135 120
• Test Repor • 測試報告 50 60
• Size Measurement • 計算工作量 15 10
• Postmortem & Process Improvement Plan • 事后總結, 並提出過程改進計划 70 50
合計 1105 1400

3.解題思路

1.首先,通讀題干要求后,我先確定自己的編寫語言,將代碼功能在腦海里封裝划分成幾個大塊,然后逐一設計題干要求
文件交互讀寫部分

1.要求通過命令行參數
2.參數異常處理
3.文件異常處理

各類要求計數部分

1.將這幾個功能分別通過一個函數實現
2.封裝到一個專門的類中
3.保證類成員的安全設計

2.在具體實現過程中,我先關注到具體功能的大概方向,然后仔細設計糾正其中的細枝末節
設計的函數

WordCount:

  • 文件輸入
  • 文件輸出
  • 和Lib交互

Lib

  • getChars()
  • getLines()
  • getWordsNum()
  • getTopWords()
    在思考識別字符串功能的過程中,我將文件全部存到一個String里面以達到完全隔離功能的目的
    然后每個函數再通過正則表達式來識別特定字符以達到各自的要求

4.代碼規范制定鏈接

代碼規范

5.設計與實現過程

1.將功能分到兩個類WordCount 和 Lib 中

  • WordCount:

in()對文件進行讀入並處理讀入異常
out()對字符串輸出並處理寫異常

  • Lib:
    · getChars()
return word.length() ;

· getLines()

        String s="\\n";
        pattern=Pattern.compile(s);
        matcher=pattern.matcher(Oword);
        while(matcher.find()){
            line++;
        } 
         ...

· getWordsNum()
這是整個獲取單詞數的流程

其中再matcher過程中對hashMap的處理

            while(matcher.find()){
            String key=matcher.group(0);
            Integer value=hashMap.get(key);
            //System.out.println(key);
            if(value!=null){
                hashMap.put(key,value+1);
            }
            else{
                hashMap.put(key,1);
            }
            wordsNum++;
        }

· getTopWords()
由於已經通過getWordNum獲得了一個單詞map,獲取前十個單詞則只需要對map進行排序即可
為滿足字典排序,我先將key值進行排序,再根據value進行排序

hashMap = hashMap.entrySet()
                .stream()
                .sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
                .collect(Collectors
                        .toMap(Map.Entry::getKey
                                ,Map.Entry::getValue
                                ,(e1, e2) -> e1
                                ,LinkedHashMap::new));

這是其中的一個排序,通過對hashMap的Stream實現

6.計算模塊接口部分的性能改進

1.之前本來是想通過一直保留一個和文件交互的流,這樣getLines()和getChars()甚至都可以不用到正則表達式,但之后意識到有安全性封裝問題,所以改用
通過BufferedReader和BufferedWriter進行文件的讀寫,並且一次性讀取到最后然后關閉,保證了讀寫的安全和速度
2.相比與用String數組來存,我這次第一次使用了hashMap進行存儲,並且排序過程去網上學習了stream()的新的排序方法,應該可以加快文件的時間空間利用率

                hashMap = hashMap.entrySet()
                .stream()
                .sorted(Map.Entry.comparingByKey())
                .collect(Collectors
                        .toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));

3.用正則表達式來進行字符串比對會比剛開始想的一個個的進行比較有效率且簡潔明了.

String s="[a-z]{4}[A-za-z0-9]*";

7.計算模塊部分單元測試展示

1.對輸入輸出的參數進行測試

        String infiletxt="inputFile.txt";
        String outfiletxt="outputFile.txt";
        WordCount wordCount=new WordCount("a","b");
        wordCount.main(new String[]{infiletxt,outfiletxt});//正確參數
        wordCount.main(new String[]{});//無參數
        wordCount.main(new String[]{infiletxt});//一個參數
        wordCount.main(new String[]{"a","b"});//參數正確,文件不存在
        wordCount.main(new String[]{infiletxt,outfiletxt,"a"});//多參數

2.對行數進行測試

        String testStr="asdjfoiadusfao\nafljksd454989$%&^";
        int cnt=1000;
        ...
        assertEquals(lib.getLines(),1000);

3.對字符數進行測試

        String testStr="!@#$%^4567";//num:10
        int cnt=666;
        StringBuilder stringBuilder=new StringBuilder();
        assertEquals(cnt*10,lib.getChars());
         ...

4.對單詞數進行測試

        int wordNum=100;
        String testStr="Windows";
        for(int i=0;i<wordNum;i++){
            stringBuilder.append(testStr).append(random.nextInt(999)).append(" ");
        }
        assertEquals(wordNum,lib.getWordsNum());

5.對最高10個單詞測試

        int wordNum=100;
        String testStr="Windows";
        lib.getWordsNum();
        System.out.println(lib.getTopWords());

6.隨機生成1000個單詞測試


耗時:300ms

7.大量大數據測試


耗時:514ms

8.覆蓋率截圖

存在小部分未覆蓋為異常處理

8.模塊部分異常處理說明

  • 命令行參數異常處理
        if(args.length!=2){
            System.out.println("Required ARGS NUM Error");
        }
        else {
            new WordCount(args[0], args[1]);
        }
  • 文件名異常處理
           if (!infile.exists()) {
                System.out.println("File Not Exist Error!");
                return;
            }
  • IOException處理
       catch(IOException e){
            e.printStackTrace();
            System.out.println("ERROR!");
        }

9.心得體會

對於這次的作業體驗是前所未有的,首先是大段的作業說明讓自己在閱讀過程中有點找不着頭腦,生怕忘記了作業的某一項導致缺做,仔細定下心來后,通篇閱讀幾遍總算是有一個大概的作業框圖,之后為了保證完成,列了一個作業列表,仔細核對其中的內容.
知識的學習溫習和新的實踐果然離不開干系.git和github一直是自己想完全掌握的東西,但由於之前沒有相應的作業要求,所以也只是申請了一個賬號在那邊就沒動了.動力是工作的源泉,這次作業的機會給我一個不得不使用它們的理由,也就順理推動了自己去看git的相關知識.除此之外,IDEA的單元測試也是我第一次嘗試,以前都是自己用一些輸入來測試,這次通過這些Test感覺到單元測試的強大之處,以后一定會多用.相比之下,為了完成代碼工作,由於之前是學習過Java的,但也差不多一學期沒動了,很多知識不是很扎實的情況下,為了完成代碼就去查閱了很多的相關資料,代碼的規范等等,在終於完成編碼工作后深感實踐是溫習的第一利器,也為自己接下來要溫習很多之前學過但不是很熟練的知識做好了心里准備.
當然,對於自己作業的情況還是有些小不滿意的,因為時間完成度超過了自己的預期很多,所以發現自己的動手能力還是太差太差,希望在之后的實踐中自己能夠有新的進展!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM