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

这个作业属于哪个课程 2021春软件工程实践|S班
这个作业要求在哪里 软工实践寒假作业(2/2)
这个作业的目标 1:阅读《构建之法》并提问。 2:WordCount编程完成个人作业。
其他参考文献 博客园,csdn,oschina社区

作业基本信息...

1.1 阅读《构建之法》并提问

问题1 面对庞大而又零碎的需求,是如何从中化繁为简,捋顺思路,将各个需求串起来已进行功能的实现的呢?

​ 我看了这样一段文字:“这是刚性需求,或辅助性需求?需求的量有多大? 需求会一直存在么? 很多同学想象力非常丰富,觉得一定会成千上万的用户来使用我想出来的软件。那么可以实践一下,找到10个潜在用户,他们表示“一定会试用你的软件”, 那么就算你找到了合适的需求 (Need). ”
Q1
有一个困惑,例如当项目组在与用户沟通完用户需求后,面对庞大而又零碎的需求,是如何从中化繁为简,捋顺思路,将各个需求串起来已进行功能的实现的呢?

我的思考:想要化繁为简需要有序的思考,那么我们就应该把庞大需求进行一步一步的分析与抽取,要找到需求之间的共性,以及各个需求之间的联系,从而按需实现功能。

问题2 那么作为团队中的一员,如何快速的找到自己的定位,完成自己可以胜任的任务呢,并且作为初入职场的新手程序员,如何让正确而又全面的向领导展现自己能力呢?

​ 在团队合作阶段的萌芽阶段,书中着重介绍了领导的做法,之前假期在公司实习的时候,刚进去几天我自己是处于一种懵的状态,不太了解自己要干啥,也了解自己的能力到底能做什么,那么作为团队中的一员,如何快速的找到自己的定位,完成自己可以胜任的任务呢,并且作为初入职场的新手程序员,如何让正确而又全面的向领导展现自己能力呢?

我的思考:快速的找准定位是可以提高我们的工作效率的,也可以加大自己的工作激情,但是有没有什么方法可以帮助我们适应陌生的环境呢?

问题3 假如作为一个个人开发者,考虑的测试情况总是有限的,有什么办法可以帮助我们拓宽自己对软件可用性测试的思维呢?

​ 在测试的章节中,书中提到“九条:对于一个功能,用户可能的输入千差万别,我是不是得写成千上万个测试用例?阿亨:没必要,我们可以把纷繁的情况归类到几个类型中。”后归类了几个类型,
Q3
但是假如作为一个个人开发者,考虑的测试情况总是有限的,有什么办法可以帮助我们拓宽自己对软件可用性测试的思维呢?

我的思考:我觉得专门设置了软件测试这一岗位足以说明软件测试的重要性,但是软件测试又是如何以各种各样的情况去“***难”我们开发的软件,作为一名软件测试师在接到一个项目时是以怎样的眼光去看待又是怎样着手实践的呢?希望可以得到解答。

问题4 有着各自的见解,无法迅速的达到统一,所以请问在这种情况下到底应该如何达成一样的意见,团队又应该如何去沟通交流思想呢?

​ 在书中了解到了可以用思维导图来表示实体和实体之间的关系,看到以下这句话:““一图胜千言”, 人们经常用图形来帮助他们了解概念,强化记忆。思维导图是其中的一个例子。思维导图没有严格的语法定义,一般来说是从图形的正中开始写下一个概念,然后按照绘图者所关心的属性扩展,几乎每个人都能马上开始画图。这个看似简单的工具其实很适合团队一起讨论和理解核心概念 – 例如,我们的主要用户有什么特点、什么需求。”,类似的思维导图我们在之前的uml课程中也有画过,但是当时出了一个小插曲就是小组内的成员对于思维导图的绘画有着各自的见解,无法迅速的达到统一,所以请问在这种情况下到底应该如何达成一样的意见,团队又应该如何去沟通交流思想呢?

问题5 请问作为程序员在接受了用户这样的体验反馈后会做出如何的调整呢,如何在软件收益和软件使用体验之间做出均衡呢?这种现象是否也是与我们国内用户受众以及软件开发的范围所决定的呢?

​ 在用户体验这一章,老师提到了用户体验和质量这一问题,并举了一个小例子,

Q5

​ 我有个疑问,例如现在的某手机支付软件,为了利润投加了很多影响用户观感的广告,经常在使用的时候会无奈地误触到,作为用户我感觉软件的使用不够清爽,而反观某b视频软件,在广告以及一些不影响软件主要播放功能的小功能方面安排的很好,请问作为程序员在接受了用户这样的体验反馈后会做出如何的调整呢,如何在软件收益和软件使用体验之间做出均衡呢?

我的思考:程序的优化与更新必须不断地接收与聆听用户的反馈,满足用户需求,提高用户体验是第一指标,要做出好用耐用的软件,而不是把收益摆在第一位,不然就会被同行的低价高质量的产品所打败。作为微博的重度使用者常常在观看视频时因为层出不穷的广告投放而被迫跳转到了其他应用,后来只好改用微博国际版,无广告界面清爽,这种现象是否也是与我们国内用户受众以及软件开发的范围所决定的呢?

1.2 附加题

​ 有趣的冷知识:在日本销售的手机中,有 90% 以上都是防水的,据说是日本的女性有拿着手机洗澡的习惯。卡西欧的 Canu 502S(又名G'zOne)是第一款防水手机,事实上日本的电子产品很多都有防水功能,包括电视机。除了日本公司外,很少国外公司会提供类似产品,但他们会针对日本市场推出防水的产品。
​ 个人思考:看完这个冷知识,我想说供需关系在IT行业处着重要的地位,我们的开发和设计都是为了满足用户的需求,而用户的各种需求又不断刺激更多软件的出现,两者就是鱼和水的关系。

2.1 Github项目地址

WordCount源代码地址

2.2 PSP表格

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

2.3 解题思路描述

看到题目时我先从要求实现的主要功能入手,有四个功能就可以分成四个函数

	1. int Countchar(fstream & in ,fstream &out);
	2. int Countword(fstream & in ,fstream &out);
	3. int Countline(fstream & in ,fstream &out);
	4. void sort();
	
	然后可以定义一个属于输入文件的file类,又因为单词存在出现次数的差异,如果单独定义数组来记录单词出现的次数,可能在程序的健壮性和可读性上不够完美,
于是将word定义为一个单独的类,将单词名与单词出现次数增加在一起,通过vector容器来进行存储一个文件中的出现的单词,但是考虑到map容器在查找效率上优于vector
容器,于是决定弃用自定义vector类,改用Map<string,int>,在文件统计各项的函数中,使用到了fstream文件流类进行读取与输出,因为很久没有fstream类进行代码
编写了,于是作业期间通过阅读文档复习了一下fstream类的用法,同时对读取字符进行各种情况下的判断以进行不同的操作。

2.4 代码规范制定链接

代码规范制定

2.5 设计与实现过程

首先定义了myfile类,用来定义文件的各种信息

	class myfile
	{
	public:
	myfile()
	{
		this->fileName = "";
		this->characterNum = this->wordNum = 0;
	}
	myfile(string s)
	{
		this->fileName = s;
		this->characterNum = this->wordNum = 0;
	}
	int Countchar(fstream& in, fstream &out);
	int Countword(fstream& in, fstream &out);
	int Countline(fstream& in, fstream &out);
	void Sortmap(fstream &out);
	string fileName;
	int characterNum;
	int wordNum;
	map<string, int> wMap;
	};

Countchar函数的实现

采用了流式的逐字符读取,关键在于读入流需要设置不跳过换行符和空白符。同时考虑到windows的换行为"\r\n"并非"\n",所以fstream打开文件的方式必须设置为ios::in|ios::binary.

关键代码

        in.unsetf(ios_base::skipws);//设置不跳过换行符和空白符
	in >> tempCh;
	while (!in.eof())
	{
		totalCount++;
		in >> tempCh;
	}

Countword函数的实现

也是采用了逐字符读入的方法,设置一个字符串不断的加入新的字符,当读入分割符时对前面的字符串进行分析,判断是否满足单词的条件,若满足则加入map容器中,不满足则跳过,下面有一个流程图帮助理解

1614774835662

整体关键代码

以下为单词在map容器中的存储过程。

	//通过find函数判断单词是否在map容器中出现过,若出现过则增加迭代器返回的指针的值既单词次数即可,未出现过则加入新的元素。
        map<string, int>::iterator it = this->wMap.find(wordString);
	if (it != this->wMap.end())
	{
			it->second++;
	}
	else
	{
			this->wMap.insert(pair<string, int>(wordString, 1));
	}
	wordString = "";

局部关键代码

以下为约束单词的代码,后来了解到正则表达式也是个十分好的解决方法。

        in >> temp;
	if (temp <= 'Z'&&temp >= 'A')//如果字符为大写字母将其转换为小写字母
	{
		temp += 32;
	}
	if (!(isalpha(temp)||isdigit(temp)))//判断是否遇到分割符,是则执行以下条件
	{
		if (wordString.length() < 4)//如果单词长度小于4,则直接跳过并清除字符串内容
		{
			wordString = "";
			continue;
		}
		for (int i = 0;i < 4;i++)
		{
			if (!isalpha(wordString[i]))//判断字符串前四位是否为字母,不为字母则直接结束循环
			{
				wordString = "";
				isWord = false;
				break;
			}
		}
		if (!isWord)//如果不为单词则继续执行下一个while循环,不执行以下代码
		{
			isWord = true;
			continue;
	        }
		---

Countline函数实现

​ 题意说包含任何非空白字符的行都为有效行,所以只要判断逐行读取后判断该行内是否存在非空白字符即可,使用了isspace()函数进行对字符的判断

关键代码

	getline(in, line);
	for (int i = 0;i < line.length();i++)
	{
		if (!isspace(line[i]))//对文件进行逐行的读取,判断是否存在非空白字符。
		{
			totalCount++;
			break;
			}
	}

Sortmap函数的实现

​ 因为map容器是基于红黑树进行的存储,不适用于sort函数,所以将map容器中的元素拷贝到一个线性的容器vector下,再通过自定义排序函数进行排序,从而输出前十的单词。

关键代码

	vector< pair<string, int> > vec(this->wMap.begin(), this->wMap.end());
	sort(vec.begin(), vec.end(), Sortwordtimes);

Sortwordtimes函数

​ 先根据单词的次数进行排序,再通过单词的字典序进行排序。

关键代码

	if (wordA.second == wordB.second)
	{
		return wordA.first < wordB.first;
	}
	return wordA.second > wordB.second;

​ 同时将myfile类封装进lib.h文件中,将重要的三个功能封装在lib.cpp下,实现了自定义的接口。

2.6 性能改进

性能

由图可知计算单词次数的Countword函数耗时占比比较大,在原来采用自定义vector容器的情况下选择改用了map容器,节省了计算时间。

同时在单词不满足单词条件的情况下直接跳出循环,不执行多余的代码。

同时输出流选择在main函数中打开一次后通过append的方式避免了多次开启关闭。输入流也采用了Clear函数来清除符号位。

2.7 单元测试

对封装的三个功能进行了主要的测试。
1.Countchar函数

自定义了一个“lksdad[]\r\n8686"字符串,用来测试在遇到windows遇到/r/n的情况下字符读入是否正确,并进行了10000次的循环。

关键代码

        myfile *mf = new myfile("test1.txt");
        string str = "lksdad[]\r\n8686";//代码中间省去了文件的读写,只展示主要部分
        fstream in;
        for (int i = 0;i < 10000;i++)
        {
	  in << str;
        }
        int count = 10000 * (str.length()+1);
        Assert::AreEqual(count , mf->Countchar(in2, out));

2.Countword函数

同样自定义字符串"ijoawo\r583ksdjad newwave66 software",夹杂了字母数字空格以及特别的特殊符号,并同样进行了10000次的循环

关键代码

        myfile *mf = new myfile("test1.txt");
        string str = "1\r\n\t\n\n\ \n56ad";
        fstream in;
        for (int i = 0;i < 10000;i++)
        {
    		in << str;
        }
        int count = 2+9999*1;//因为字符串末尾没有分割符,第一次判断为两个单词,后续的判断句子中的第一个单词与前一个连接在一起,所以后续次数为1
        Assert::AreEqual(count, mf->Countline(in2, out));

3.Countline函数

定义了”1\r\n\t\n\n\ \n56ad“字符串,测试正常情况的有效行,包含空白字符的行,和单纯为换行符的行。

        myfile *mf = new myfile("test1.txt");
        string str = "1\r\n\t\n\n\ \n56ad";
        fstream in;
        for (int i = 0;i < 10000;i++)
        {
    	  in << str;
        }
        int count = 2+9999*1;//与上述情况相同,因为字符串末尾没有分隔符的原因,出现首尾相连。
        Assert::AreEqual(count, mf->Countline(in2, out));

3次测试的截图如下

测试结果

代码覆盖率截图
代码覆盖率

2.8异常测试

1.命令行参数传入的判断

	if (argc != 3)
	{
		cout << "参数输入错误";
        exit(0);
	}

2.文件无法正常打开时的判断

        if (!in.is_open())
	{
		cout << "无法打开文件:"<<argv[1] << endl;
		exit(0);
	}

3.在countword函数中判断文件是否读到末尾

        if (in.eof())
	{
		cout << "文件到达末尾" << endl;
		return -1;
	}

2.9 心路历程与收获

​ 心路历程:这次的作业给我最大的感觉就是好像在坐过山车!!!不断的从山顶跌倒谷底,周始往复,为什么这么说?因为一开始我看到这个作业布置乐观地觉得不会有太大的难度,按步骤实现即可,谁知道当真正实操起来以后,被众多的规则与条件给惊到了,这一次作业应该也是大学以来最严谨的一次作业了吧!!!每当我解决了一个小问题还没享受完喜悦,下一个问题又是接踵而至!
只能不断地查阅资料来解决。好在是顺利完工了!
​ 收获:1.心理收获
(1)心理上最大的收获是我的耐心得到了锻炼,面对工具的不熟悉以及各种无厘头的报错,我可以保持一个好的心态去面对,并且相信可以逐一解决,每当我坚持不下去感到毫无头绪求解无门的时候,我的解决方法就是睡半小时让自己重启一下๑乛◡乛๑
(2)其次是自己感觉自己也更加细心了,会与同学讨论各种情况下的输入考虑,以及与同学比对最后测试结果的相同性,来发现自己的错误在哪,期间也不断地在对自己的代码进行查缺补漏。
(3)会有去追求更优的欲望!虽然我知道我的程序仍然不是最优秀,最有效的解法,但是我在以往的大学生涯中,对于代码的理解就是等解决问题就行,暴力一点,这一次会想着用更优解来解决提高程序的效率,缩短求解的时间,了解map容器在查找效率与排序效率和vector容器的异同,而且一开始想着使用c的方式对文件进行读取,但是代码可读性就变得很差,于是考虑到了使用c++的流式读取。也算是一点小改变了(^U^)ノ~YO
2.学习收获
(1)了解了git和github desktop的使用,对git的语法规则有了一定的了解。
(2)了解了map容器与vector的自定义排序写法。
(3)学习了vs2017下的单元测试,了解了一些好用的扩展使用方法。
(4)复习了c++的知识。
​ (5) 学习了简单的代码规范的制定

posted @ 2021-03-04 19:43  nafup  阅读(95)  评论(3编辑  收藏  举报