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

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

这个作业属于哪个课程 2021春软件工程实践|S班
这个作业要求在哪里 软工实践寒假作业(2/2)
这个作业的目标 阅读《构建之法》并提出问题、学习git、实现WordCount
其他参考文献 博客园,CSDN

阅读《构建之法》并提问

1.创新者与先行者


p351页写到:

大家听了很多创新者的故事,有些人想,他们真了不起,第一个想出了这些美妙的想法,要是我早生几十年,也第一个实现那些想法就好了。其实,大部分成功的创新者都不是先行者,例如搜索引擎,Google是很晚才进入这个领域的。又如 Apple的音乐播放器iPod,发布于2001年10月23日,在它之前市面上已经有很多同类产品了.

论及市场竞争时,人们喜欢用下面这样一些词汇:。

先行者( First Mover )、先发优势(First Mover Advantage,FMA )·

后起者( Second Mover ),后发优势( Second Mover Advantage,SMA)


这是sma的定义:后动优势(late-mover advantage;Second-mover advantage;又称为次动优势、后发优势、先动劣势)是指相对于行业的先进入企业,后进入者由于较晚进入行业而获得的较先动企业不具有的竞争优势,通过观察先动者的行动及效果来减少自身面临的不确定性而采取相应行动,获得更多的市场份额

实际上,搜索了一下SMA的定义后,我觉得一些成功的后发者凭借的并不是后发优势,如微信打败米聊更多的是因为QQ庞大的用户基数可以使微信迅速发展用户流量罢了.同样ie超过mosaic更多的应该是商业策略的原因.

2.事后诸葛亮会议


p339页:

一个里程碑结束了,接下来怎么办?团队有什么经验教训?产品怎么才能做得更好?我们常说“软件的生命周期”—这个软件开发的周期结束了,生命也结束了。我们能不能像医学的尸体解剖一样,把这个软件开发的流程解剖一下?解剖的过程可以叫:Postmortem,Retrospective",Review,事后诸葛亮会议,等等……大多数学校里的软件工程项目结束后大家一哄而散,一些诺言像“我一定会补上文档的”、“我们还会继续开发的”……成了撤退时的疑兵之计,等烟尘散去,同学们早跑没影了。
产品发布了,大家松了一口气。阿超建议大家开一个总结会议,就是事后诸葛亮会议。会议请公司的秘书小芳主持并作记录。为了让大家能畅所欲言,阿超和大牛没有参加会议。为了活跃气氛,小芳还买了零食、饮料、河曲啤酒等。


事后诸葛亮会议是对整个项目的回顾,总结经验教训,但是一些公司里人员流动大,去去留留一个项目能换几茬人,那么事后诸葛亮会议还有意义吗?会不会变成甩锅大会呢?

思考以后觉得个人对参与项目的自我反思还是相当重要的,事后诸葛亮会议要不要成为团队的常设项目还是要因具体情况而定吧

3.qa和test


p316页:

从上面的叙述中不难看出,软件的质量保障(QA)和软件测试(Test)是有很大区别的。然而,当前IT业界经常混用QA和Test这两个名词,很多团队的QA/Test工作是在较低水平上重复。一位曾在微软和雅虎工作过的程序员,有这么一个论断:大多数的开发团队并不需要一个独立的测试角色°。这引起了中国IT业界的热烈讨论,其中一篇影响较大的文章是“我们需要专职的QA吗?”


我了解了QA与TEST的具体信息与区别

相同点 不同点
QA TEST QA TEST
保证和提高产品质量 关注过程SQA 重点是对软件开发过程进行监督管理、控制 关注产品TESTER 重点是对软件开发的成果进行检查、控制
以组织标准过程和项目已定义过程为依据 以产品需求为依据
以评审和审计为主要手段 以模拟各种场景的实际使用为手段
重在发现和提出过程存在的问题 重在发现产品存在的缺陷
重在预防 重在发现和纠正

我的理解是test就是QA的子集,软件测试只是QA的一部分,QA的权限也比test大许多,对于大部分公司来说,职位并不是严格的,很多人可能是一职多能,即是QA也是开发.看了一些回答把国内的测试称作顶着QA头衔的test

4.结对编程

p85:在结对编程模式下,一对程序员肩并肩、平等地、互补地进行开发工作。他们并排坐在一台电脑前,面对同一个显示器,使用同一个键盘、同一个鼠标一起工作。他们一起分析,一起设计,一起写测试用例,一起编码,一起做单元测试,一起做集成测试,一起写文档,等等。

结对编程的好处书中已经详细描述了,那结对编程的不足之处呢?结对编程在现实生活中国内的公司似乎很少采用,是有什么局限吗?

我发现知乎有一个相似的提问:国内为何很少有人做结对编程呢?是确实不好还是属于中国特色

阅读了他们的回答以后,我总结了一些看法:

1、一些内向的程序员不太喜欢在编程中与他人频繁交流

2、管理者怀疑结对编程的产出比不上两个人以传统方式进行开发

3、有些工作不适合结对,结对的人有明显的思维趋同性,可能范一样的错误

实际上,到目前为止,我的感受就是互相沟通一起完成工作确实会比单干少放错、进度快,但是会快多少就不清楚了。

下面还刷到了周筠老师的回答-------

5.典型用户


p218:

定义了最初的典型用户之后,是不是就可以开始写程序了?不,典型用户只是我们的设想,这些都是纸上谈兵,我们还要和这些典型用户的代表交流,理解用户,理解他们的工作方式和需要。然后再修改,细化典型用户。于是,移山公司的员工和实习生花了几天时间,做了不少用户调查,搞了不少头脑风暴,画了无数草图。

小李:(回来报告)除了进一步了解用户的需求,细化了一些功能的设想外,我们还有一个重大发现,我们的第一个典型用户,吴石头,好像不喜欢上网,他事实上不太会用电脑,也搞不懂如何上传照片。凡是和网络相关的事情,都交给了他的儿子。所以我们不得不把吴石头从典型用户中删除。


典型用户是指,按不同维度来区分用户的过程中,在每个维度中能代表目标用户的那类群体。比如,按人数多少来划分的,能代表最多用户特征的群体;按盈利来划分,能代表带来最多盈利价值特征的群体。

但是典型用户的定义和删除让我觉得非常奇怪,典型用户在何种情况下需要进行删除呢?

WordCount编程

Github项目地址

https://github.com/hh-afk/PersonalProject-C

PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划
•Estimate 估计这个任务需要多少时间 20 15
Development 开发
•Analysis 需求分析 (包括学习新技术) 50 60
•Design Spec 生成设计文档 30 30
•Design Review 设计复审 30 30
•Coding Standard 代码规范 (为目前的开发制定合适的规范) 15 40
•Design 具体设计 30 50
•Coding 具体编码 400 700
•Code Review 代码复审
•Test 测试(自我测试,修改代码,提交修改) 200 420
Report 报告
•Test Report 测试报告 50 100
•Size Measurement 计算工作量 20 20
•Postmortem & Process Improvement Plan 事后总结, 并提出过程改进计划 30 30
合计 875 1495

解题思路描述

最开始拿到题目后,想法就是将其拆分成4-5个部分,一部分实现一个功能,编写countchar,countline,countword分别解决,然后词频通过数组存储冒泡排序进行统计。(后面看来在最开始的思考方面应该多花些时间而不是急匆匆的编码)

最后词频统计是用map存储,使用了Merge Sort

analysisFlie类中包含有成员函数countChar、countWord、countLine 进行分析处理,getChars()、getWords()、getLines()进行返回类内私有变量chars、words、lines,countWord同时将统计数据存入map,通过sort函数排序后得到词频。

代码规范制定链接

代码规范

计算模块接口的设计与实现过程

计算字符数:
思路就是一个个读字符,然后累加。但是最开始没有使用noskipws,无法读入空格,之后也遇到了'\r'读不出来的情况,以二进制打开file后解决.

while (!file1.eof())
    {
        file1 >> c;
        if (file1.eof())
        {
            break;
        }
        i++;
    }

计算行数
思路与上面读字符类似,用getline一行一行读,如果遇到空行就不加。

string d;
    while (!file1.eof())
    {
        getline(file1, d);
        if (file1.eof())
            break;
        if (d.empty()) 
        {
            i--;
        }

        i++;
    }

计算词数将所有大写字符转换为小写后操作,先判断是否前四个都是字符 ,是的话将单词存入map

for (i = 0; i < n; i++)
    {
        if (!isChar(b.text[i])) //如果不属于0-9及a-z跳过继续循环
            continue;
        else
        {
            for (j = 0; j < 4 && i < n; j++) //计算存储合适的字符串
            {
                if (!isChar(b.text[i]))
                    break;
                store[j] = b.text[i++];
            }
            if (j == 4)
            {
                for (m = 0; m < 4; m++) //如果不是字母开头则不符合要求
                {
                    if (store[m] < 97 || store[m]>122)
                    {
                        sym = 1;
                        break;
                    }
                }
                if (sym == 0) //符合要求的话将单词存入w,再存入map
                {
                    char* w = new char[1000];
                    b.sums++;
                    for (m = 0; m < 4; m++)
                    {
                        w[k++] = store[m];
                    }
                    while (isChar(b.text[i]) && i < n)
                    {
                        w[k++] = b.text[i++];
                    }
                    w[k] = '\0';
                    load(w);
                    isWord++;
                    delete[]w;
                    k = 0;
                }
                else
                {
                    sym = 0;
                    j = 0;
                }
            }
        }
    }

计算模块接口部分的性能改进

最开始词频方面我是用冒泡排序解决的,与同学交流后得知用map存储的话排序可以方便快速,因为思考的时候没有考虑周到所以最后还是用了归并排序,但是也提高了处理速度.

void merge(Words* a, Words* b, int left, int mid, int right) {
    int i = left, j = mid + 1, m = 1;
    while (i <= mid && j <= right) {
        if (a[i].count < a[j].count) b[m++] = a[j++];
        else b[m++] = a[i++];
    }
    while (i <= mid) b[m++] = a[i++];
    while (j <= right) b[m++] = a[j++];
    for (int n = 1; n <= right - left + 1; n++)
        a[left + n - 1] = b[n];
}

void sort(Words* a, Words* b, int left, int right) {
    if (left < right) {
        int mid = (left + right) / 2;
        sort(a, b, left, mid);
        sort(a, b, mid + 1, right);
        merge(a, b, left, mid, right);
    }
}

归并排序参考自https://blog.csdn.net/qq_43337263/article/details/105218460

另外我发现各个函数实现功能的时候都会对整个文件读取遍历所有字符,计算单词数的函数还不止遍历了一遍

计算模块部分单元测试展示

一开始因为没有接触过单元测试,所以还是先对程序功能进行手动测试,跑出结果后用NOTEPAD++编辑器手动统计字符,行数,词频等,后面学习了单元测试的相关知识,就改用单元测试了

6Z1psH.png

编写的单元测试例子如下:

6VOtW4.png

6VOXXn.png

运行单元测试时出现了一些平时跑程序时没有的问题,花了一些时间来解决问题。单元测试比起手动测试统计来说还是方便了不少,简化了繁琐的重复操作。

6Z1SQe.png

6e3Ol8.png
main函数内没有覆盖到,其他都测试到了
6e3X6S.png

我发现同一个文本在性能分析报告中跑的速度会比直接运行慢好多,并且会生成一个很大的文件,所以后来选了一个小一些的文件来跑进行分析

6VOO6s.png

计算模块部分异常处理说明

如果打不开需要分析的文件,就会输出File open failed

if (!file1.is_open())
    {
        cout << "File open failed" << endl;
    }

如果打不开需要写入结果的文件,就会输出无法写出结果

if (!file2.is_open())
    {
        cout << "无法写出结果" << endl;
    }

心路历程与收获

1、事后想来再进行需求分析,设计复审,具体设计的时候应该多花些时间,在具体编码之前没有仔细审慎的工作,前面节省的一些时间都在后头的编码与测试中成倍的还回来了。进行编码前还是要考虑好设计好,逻辑混乱、想到哪写到哪导致程序中好几个函数没用过(根本用不到的功能),对\t等的处理又没有考虑到。

2、开发时因为没有配置好.gitignore导致代码都是在本地vs上的一个项目内写好,再拷贝到github目录下commit,代码搬来搬去的不仅容易乱还出了一堆错误,比如两个编辑器的代码规范配置不一样导致代码规范一改再改,比如拷贝的时候拷错了地方,下次应该先配置好.gitignore再编写。

3、合理命名变量,及时编写注释是相当重要的,像统计次数的功能中用了一堆i,j,m,k之类的没有好好命名的变量,又没有及时编写注释,第二天就看不懂写了啥了。变量乱七八糟,没有好好命名的代码是shithole,再缺少注释就烂到无法形容了。

4、大学中应该积极的自我学习,像github是很早以前就注册过了,然而直到这次作业才比较实际的使用它,而不是把它当成永远在将要学习的计划里的一门技术。vs中的单元测试和性能探查也是,一直没有实际的应用,只是知道而已。

5、与同伴一起编程一起交流比起一个人埋头闷干要顺的多,比如行数统计和字符统计中遇到\r以及中文等会出问题的情况就是同伴告诉我比较快捷的解决方法的,在优化代码方面几个人一起讨论也比较容易发现自己的不足,比如用通配符实现字符串的比对就会简洁易懂的多,还有排序可以直接用map进行,速度快很多。

6、性能分析中的检测会生成一份非常大的报告并且速度极其慢,2e6的数据跑了半个小时还没有结束,并且占满了我的c盘,或许可能是我的使用方法出了错误。

7、代码规范十分重要,最好在规定好代码规范后配置好ide,再进行编写,并且要按照完全按照规范来,我的代码规范确定好后在编程过程中因为ide配置不一样、规范和个人编写习惯有所差别导致中途更改了规范,完成后又重新审查一遍以符合规范。

posted @ 2021-03-05 10:29  1212211  阅读(146)  评论(3编辑  收藏  举报