Eyes can't shine unless there's something burning bright behind.
broad
retrieve
narrow

软工实践寒假作业(2/2)

|这个作业属于哪个课程 |


:---😐:---😐:---:
| 这个作业要求在哪里 |
|这个作业的目标|阅读《构建之法》并提问,完成词频统计个人作业,学习使用git以及github,记录PSP表格等|
|其他参考文献|

一.阅读《构建之法》并提问

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的,但也差不多一学期没动了,很多知识不是很扎实的情况下,为了完成代码就去查阅了很多的相关资料,代码的规范等等,在终于完成编码工作后深感实践是温习的第一利器,也为自己接下来要温习很多之前学过但不是很熟练的知识做好了心里准备.
当然,对于自己作业的情况还是有些小不满意的,因为时间完成度超过了自己的预期很多,所以发现自己的动手能力还是太差太差,希望在之后的实践中自己能够有新的进展!

posted @ 2021-03-05 21:54  221801233陈鹏桢  阅读(170)  评论(9编辑  收藏  举报
]