软件工程-个人项目作业

个人项目作业

1.Github项目地址

https://github.com/yasoudream/WordCounter

2.实现程序前,模块开发预计时间

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

3.解题思路

编程语言的选择

控制台的程序,体量不会很大,用C++开发成本太大了,字符串的处理都要一堆封装,直接用脚本语言C#开发成本低得多,因此就选择C#进行开发。(其他编程语言没学= =)

需要用到的新知识

C#的文件IO基本上是用一遍查一遍(因为实在太多东西了),因为说要用到WindowsGUI来选择文件,我猜测应该会有一个API是生成一个选择文件的窗口,并返回选择到文件的路径。查的过程中,由于并不是很多人会在控制台程序中用到这个API,所以踩了不少坑,但最终还是找到正确的写法。

结构的分析

因为有较多的功能,如果将所有功能都写死的话处理起来非常麻烦,不如将每一个功能封装成一个类,需要用到时就实例化出一个对象进行使用。
当然,要有一个最主要的类,去“赋予”他功能,这样上层就不需要了解功能具体怎么用。(类似组合模式)
除此之外,还需要两个类:处理输入命令的类,读取文件的类。这两个类还要对用户的输入进行错误处理。
使用的时候,直接在主程序上按:获取命令->按命令实例化功能,获取文件内容->实例化计数器并赋予功能->计数->输入 即可。

遇到的困难

设计“功能”类接口自然是最麻烦的,因为要考虑到接口的泛用性,而且不到真正上手进行编码永远不知道还有什么隐藏的需求,所以接口一改再改。最后抽象出“开始”,“过程中”,“结束”这三个计数阶段,极大地满足了泛用性。
除此之外,如何将命令和“功能”匹配也是一大问题,由于有些功能需要用户输入的参数(至少架构保留了这样设计的可能性),代码通用化就变得非常麻烦,因此不得已情况下(也有可能是本人技术不到位 = =)写出了一些生硬的代码。

4.设计实现过程

类,接口,结构体等设计

经过3的思考之后,已经基本上能确定类如何封装了:

1.Program

最顶层的类,也是入口函数的所在类,负责使用封装好的所有类来满足用户的需求。

主要实现功能
  1. 实例化出各种类的对象。
  2. 运用对象进行数据处理。
  3. 将处理好的结果输出给用户。
  4. 提示用户的错误输入。
主要函数/方法

static void Main(string[] args) 入口函数
private static void ErrorHanding(CommondInfo.ErrorType type) 错误信息的处理与反馈

2.WordCounter

使用功能的类,拥有一个包含各种功能对象的列表,逐个字符扫描输入的字符串并计数。

主要实现功能
  1. 处理扫描的逻辑。
  2. 将扫到的字符传递到功能对象中,并统计。
主要函数/方法

private void CountMain() 处理逻辑的最主要方法
public void AddFunction(WordCounterFunction func) 添加一个功能
public void SetString(string str, bool countImmediately = true) 设置好要计数的字符串
public string GetAllLogs() 获得整合好的计数信息的字符串

3.WordCounterFunction (接口)

设计最久的类,虽然简单,但是是整个程序功能的基石
要实现的功能都要实现此接口,衍生的类都是计数功能的具体类

主要方法

int GetInitialValue() 返回一个初始值,为一些不是由0开始计数的功能提供泛用性
int OnStringHead(char letter) 第一个字符被访问时调用这个方法
int OnTraverse(char letter) 除了第一个和最后一个字符,访问字符时都会调用
int OnStringEnd(char letter) 访问最后一个字母且该字母不是最后一个时调用
string GetLogHead() 获得这个功能的字符串形式表达

4.LineCount,WordCount等继承自WordCounterFunction的类

具体的计数功能,具体功能具体实现,但实现都依赖于WordCounterFunction接口。

5.CommondInfo (结构体)

用来承载CommondReader返回的信息

    /// <summary>
    /// 命令蕴含信息
    /// </summary>
    public struct CommondInfo
    {
        public bool isError;    //是否是错误信息
        public enum ErrorType { FileError, CommondError, FileNotChosen}
        public ErrorType errorType; //错误的类型
        public List<WordCounterFunction> funcs;    //命令指定的功能
        public string filePath;  //文件地址
        public void SetError(ErrorType type)
        {
            isError = true;
            errorType = type;
        }
    }

6.CommondReader

处理命令的类

主要实现功能
  1. 将Main函数收到的指令打包成CommondInfo实例
主要函数/方法

public static CommondInfo GetInfo(string[] commond) 接受命令,返回整理好的信息

7.FileDataReader

负责读取文件,处理文件路径信息的类

主要实现功能
  1. 读取文件内容
  2. 获得文件路径
主要函数/方法

public static bool GetDataByPath(string filePath, out string data) 通过完整路径来获得信息
public static string GetFilePathByGUI() 用GUI获得文件路径

关键类的使用与继承 简单图示

5.代码说明

  1. WordCounter类的CountMain()方法可以说是程序的核心了,它处理了功能和字符串的关系,并计数。
/// <summary>
/// 计数主方法
/// </summary>
private void CountMain()
{
	isDirty = false;
	Init(); 
	if (currentString == null)
		return;
	//当前字符下标
	int currentLetterIndex = 0;

	//第一个字符
	if (currentLetterIndex < currentString.Length)
	{
		for (int i = 0; i < functions.Count; i++)
		{
			functionsCount[i] += functions[i].OnStringHead(currentString[currentLetterIndex]);
		}
		currentLetterIndex++;
	}
	else//空串
	{
		return;
	}
	//中间的字符
	while (currentLetterIndex < currentString.Length - 1)
	{
		for (int i = 0; i < functions.Count; i++)
		{
			functionsCount[i] += functions[i].OnTraverse(currentString[currentLetterIndex]);
		}
		currentLetterIndex++;
	}
	//最后一个字符
	if (currentLetterIndex == currentString.Length - 1)
	{
		for (int i = 0; i < functions.Count; i++)
		{
			functionsCount[i] += functions[i].OnStringEnd(currentString[currentLetterIndex]);
		}
	}

	return;
}
  1. FileDataReader的GetFilePathByGUI()使用了我以前没用过的方法获得文件路径
/// <summary>
/// 用GUI获得文件路径
/// </summary>
public static string GetFilePathByGUI()
{
	OpenFileDialog open = new OpenFileDialog();
	//设置窗口标题
	open.Title = "Choose your file";
	//设置筛选器
	open.Filter = "C Source file(*.c)|*.c|Text file(*.txt)|*.txt";
	//不可多选
	open.Multiselect = false;
	if (open.ShowDialog() == System.Windows.Forms.DialogResult.OK)
	{
		//获取文件路径
		return open.FileName;
	}
	return null;
}

6.测试运行

程序实现功能

本程序实现了-x、-c、-l、-w指令以及-a指令中的注释行计数和空行计数,
支持c源文件和txt文本文档文件,
还实现了多指令同时计数(结果按输入指令的顺序输出)

测试使用样例

#include <stdio.h>
//this is a test code file
/*
Multiline
comment
*/
int main()
{
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        //loop ten times
        printf("%d ", i);
    }

    return 0;
}//End of the program

测试过程

  1. 打开控制台,输入指令,弹出WindowsGUI窗口

  2. 选择要进行计数的文件,可以选择文件的类型,测试用的是c源文件(如上图)

  3. 如图为输出结果,完全符合预计

7.性能探测

我使用了60W+行500W+字符的.c文件作为性能探测的测试样本,测试结果如图

由于使用的是O(n)的算法,所以速度还是非常快的,OnTraverse方法是每经过一个字符都调用一遍的,由于数据量较大,占比多在所难免。而各个功能的OnTraverse方法占比不同的主要原因是每个功能实现的难度不同,其中对注释进行计数的功能最为复杂(因为要考虑到多种不同的注释情况,比如说多行注释),所以也暂时找不出优化的方法。

8.实现完程序后,实际花费时间

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 10 5
Estimate 估计这个任务需要多少时间 10 5
Development 开发 165 176
Analysis 需求分析(包括学习新技术) 20 15
Design Spec 生成设计文档 10 10
Design Review 设计复审(和同事审核设计文档) 0 0
Coding Standard 代码规范(为目前的开发置顶合适的规范) 5 3
Design 具体设计 20 13
Coding 具体编码 60 70
Code Review 代码复审 30 20
Test 测试(自我测试,修改代码,提交修改) 20 45
Reporting 报告 40 55
Test Report 测试报告 20 30
Size Measurement 计算工作量 10 10
Postmortem & Process Improvement Plan 事后总结,并提出过程改进计划 10 15
Total 合计 215 236

9.项目小结

总的来说,这是一次非常重要的开发体验。
过去的时候由于刚步入编程领域,因此写代码都是想到什么写什么,虽说有过团队合作的经历,但并没有太大的提高,也并未用过软件工程的各种开发测试手段。
经过这一次的个人项目开发,我意识到了规划的重要性。由于前期规划尚不完全充分,所以中后期的编码和测试耗时比预计的要多得多,导致了整个开发时间变长。但又因为还是做了适当的规划,所以并没有影响太多。在以前还未接触规划时,复审修改时间至少是编码时间的两倍,这让我意识到规划的重要性。
比较可惜的是,由于个人技术和时间等各种原因,未能完成-s命令和-a命令中的代码行计数,但是至少自己来说这个完成度还是可以接受的,如果有时间的话以后也可能会翻出这个文件来进行补充(由于封装了WordCounterFunction接口,加个功能还是不难的,主要是实现上的问题)。
以后还将继续努力。

原文地址:https://www.cnblogs.com/yasoudream/p/12468790.html