金融软件工程作业七:代码分析二

金融软件工程作业

对编程风格、代码可读性、代码设计质量情况的分析

TodoMVC

编程风格

TodoMVC源代码中的命名风格较好,也非常值得学习,这种命名风格使得源代码的理解变得更加容易一些,我们通过以下的代码块分析其命名风格。

showItems(items) {
	this.$todoList.innerHTML=this.template.itemList(items);
	}
removeItem(id) {
	const elem = qs(`[data-id="${id}"]`);
	if (elem) {
		this.$todoList.removeChild(elem);
	}
}

由以上这一代码块,我们能够看出TodoMVC源代码中对于函数名的命名风格,代码块中展示的两个函数为showItems函数和removeItem函数,这里可以看出函数名的命名是采用该函数的实际功能进行命名,与此同时,命名使用驼峰命名规范,使得名字看起来更易读。

const listItem = qs(`[data-id="${id}"]`);
const input = qs('input.edit', listItem);
listItem.removeChild(input);
listItem.classList.remove('editing');

const todos = this.getLocalStorage();
todos.push(item);
this.setLocalStorage(todos);

这一代码块向我们展示了TodoMVC源代码中函数使用的变量的命名风格也是使用了变量所代表的实际意义,这一点对于理解代码的含义格外重要。

TodoMVC源程序中的代码缩进也做的较好,可以通过源代码中的一个函数代码块可以得知。以下的代码块便可看出TodoMVC源代码使用的缩进风格为K&R风格,并且再所有的源代码中一以贯之。

	bindAddItem(handler) {
		$on(this.$newTodo, 'change', ({target}) => {
			const title = target.value.trim();
			if (title) {
				handler(title);
			}
		});
	}

代码可读性

TodoMVC源代码再注释这一方面上做的较好,源代码中的所有的函数部分都有对于该函数功能说明的注释,比如说:

	/**
	 * Remove an item from the view.
	 *
	 * @param {number} id Item ID of the item to remove
	 */
	removeItem(id) {
		const elem = qs(`[data-id="${id}"]`);

		 if (elem) {
			this.$todoList.removeChild(elem);
		}
	}

以上的代码块向我们说明了这一函数的实际功能是什么,值得学习的是,在注释部分中以下的部分给出了这一函数的参数的类型以及参数的实际意义,这样的注释使得理解代码较为简单。

/* @param {number} id Item ID of the item to remove*/

代码的易读性做的也较好,这一点体现在,源代码分成了多个文件,每个文件负责源代码应有的功能模块,使得对于代码的阅读变得简单,这样代码的易读性较好。以上对注释的分析也体现了TodoMVC源代码较易读的原因。

设计层面的质量情况

模块化

真正的模块化,并不是简单文本意义上的,而是与逻辑相关的有逻辑意义的。一个模块应该像一个集成电路芯片,我们能见到能使用的都很清晰,它定义了良好的输入和输出。 在实际的变成过程中,函数是实现模块化、评价模块化的合理方式。在TodoMVC项目中,模块化做的较为理想,其模块化体现在:

  1. TodoMVC源代码中避免了过长的函数,在源代码中函数的最长代码行也不超过20。
  2. TodoMVC源代码的函数部分基本做到了单一函数只实现了单一功能,没有功能的冗余。
  3. 尽量使用局部变量和参数,在TodoMVC源代码中没有使用全局变量。
单入口单出口

"Follow the single-entry/single-exit rule. Never write multiple return statements in the same function."

经过对代码的审核可以得知,TodoMVC项目中并不是完全做到了单入口单出口,有一部分函数仍然是有多个返回值,比如说:

	find(query, callback) {
		const todos = this.getLocalStorage();
		let k;

		callback(todos.filter(todo => {
			for (k in query) {
				if (query[k] !== todo[k]) {
					return false;
				}
			}
			return true;
		}));
	}

信息隐藏

在TodoMVC项目中,信息隐藏的实现是通过类的封装实现的。封装的实现使得类的成员属性没有特别要求是“私有”的,这一点做到了信息隐藏。

Partner项目

编程风格

Partner所写项目中,命名不具有一定的规范,需要进一步的改进,比如说,源代码中仍然存在以汉语拼音作为命名方式的情况,比如说:

struct shuju //struct结构体构建栈
{
int data[100];
int top;
};

且代码中命名没有指向性,一些变量的命名不能让人明晰变量的实际含义,如:

char* str = (char*)malloc(sizeof(char) * 200);

这里变量的实际含义就没有得以体现,不结合问题的实际含义难以理解变量代表的含义。但总体的命名还是能够帮助理解源代码的实际含义如:

void InitOperateSymbol(struct fuhao* StackSymbol) //符号栈非空
{
StackSymbol->top = -1;
}

这一示例的代码块中不论是函数的命名方式还是变量的命名方式都较符合规范,易于理解。

而代码的缩进风格较不唯一,在项目的源代码中即存在K&R风格,也存在Allman风格。如:

//K&R
if (i == 0 && str[i] == '-') {
v[t++] = str[i];
}

//Allman
char Randfuhao(struct fuhao* StackSymbol)
{
return StackSymbol->symbol[StackSymbol->top];
}


代码缩进风格的不一致会使得源代码看起来并不美观,也不方便理解代码的逻辑。

代码可读性(易于理解、注释)

代码的序言性注释完成度较高,但是没有完全做到序言性注释需要具备的基本信息,在这一部分中只给出了模块应该完成的功能,但却没有对函数的接口信息给出解释,这一点需要完善。源代码中的功能性注释在main函数部分做的较好,但在其他的模块也应该实现对函数功能的解释,从而帮助读者对代码的理解,避免出现不易理解的情况。

由于在核心的功能部分有较详细的注释说明,这让读者能够较容易地理解代码基本功能,因此对于整个源代码的理解也较容易。

设计层面的质量情况

模块化

尽管看起来项目的源代码中main函数代码行较多,但是项目的模块化实现的较好,原因在于代码实现了尽可能小的划分代码功能,并将其划分为单独的模块,尽管main函数有较多的代码行,但检查代码可以得知,main函数中做到了功能的封装,没有多余的功能部分,代码行长的原因在于核心功能的实现逻辑较为繁琐。因此这一项目也做到了单一函数实现单一功能,且没有全局变量,这使得其模块化实现的较好。

单入口单出口

代码中存在函数多返回值的情况,除此之外,一部分函数没有给出返回值,这一点较不符合规范。

void Inshuju(struct shuju* StackNum, int num)
{
StackNum->top++;
StackNum->data[StackNum->top] = num;
}

int judge(char ch) {
if (ch == '(')
{
return 1;
}
if (ch == '+' || ch == '-') 
{
return 2;
}
else if (ch == '*' || ch == '/') {
return 3;
}
else if (ch == ')') {
return 4;
}
}

信息隐藏

代码中没有对函数参数、变量等的链接属性的说明,因此没有做到专门的信息隐藏,但是由于局部变量只能在对应的函数内引用,可以算上一种“信息隐藏”,但代码本身没有信息隐藏部分,这并不可取。

对代码的修改

//对单入口单出口的修改
void InitOperateSymbol(struct fuhao* StackSymbol) //符号栈非空
{
	StackSymbol->top = -1;
	return;
}

/*将switch语句转化成if语句,因为原switch语句中没有给出default部分且switch语句容易产生一定的问题*/
int Math(int v1, int v2,char c)
{
int sum;
if(c=='+')
    sum = v1+v2;
if(c=='-')
    sum = v1-v2;
if(c=='*')
    sum = v1*v2;
if(c=='/')
    sum = v1/v2;
return sum;
}

//代码的缩进风格使用一致的K&R风格
while ((c = getchar()) != '\n') //非空字符{
str[i] = c;
i++;
}
if (str[i] != ')') {
i--;
Infuhao(&symbol, '(');
}

//对命名的修改
struct Data //struct结构体构建栈
{
int data[100];
int top;
};
/*符号栈*/
struct Character
{
char symbol[100];
int top;
};
char* inputStr = (char*)malloc(sizeof(char) * 200);//表示计算器所输入的字符串


附:Partner项目代码

#include<stdio.h>
#include<stdlib.h>


/*数据栈*/
struct shuju   //struct结构体构建栈 
{
	int data[100];
	int top;
};


/*符号栈*/
struct fuhao
{
	char symbol[100];
	int top;
};


void InitOperateNum(struct shuju* StackNum)    //数据栈非空 
{
	StackNum->top = -1;
}


void InitOperateSymbol(struct fuhao* StackSymbol)    //符号栈非空 
{
	StackSymbol->top = -1;
}


/*存入数据栈*/
void Inshuju(struct shuju* StackNum, int num)
{
	StackNum->top++;
	StackNum->data[StackNum->top] = num;
}


/*存入符号栈*/
void Infuhao(struct fuhao* StackSymbol, char ch)
{
	StackSymbol->top++;
	StackSymbol->symbol[StackSymbol->top] = ch;
}


/*读取数据栈*/
int Randshuju(struct shuju* StackNum)
{
	return StackNum->data[StackNum->top];
}


/*读取符号栈*/
char Randfuhao(struct fuhao* StackSymbol)
{
	return StackSymbol->symbol[StackSymbol->top];
}


/*从数据栈取出数据*/
int Putshuju(struct shuju* StackNum)
{
	int x;
	x = StackNum->data[StackNum->top];
	StackNum->top--;
	return x;
}


/*从符号栈取出符号*/
char Putfuhao(struct fuhao* StackSymbol)
{
	char c;
	c = StackSymbol->symbol[StackSymbol->top];
	StackSymbol->top--;
	return c;
}


/*符号优先级判断*/
int judge(char ch) {
	if (ch == '(')
	{
		return 1;
	}
	if (ch == '+' || ch == '-') {
		return 2;
	}
	else if (ch == '*' || ch == '/') {
		return 3;
	}
	else if (ch == ')') {
		return 4;
	}
}


/*四则运算*/
int Math(int v1, int v2, char c)
{
	int sum;
	switch (c) {
	case '+': {
		sum = v1 + v2;
		break;
	}
	case '-': {
		sum = v1 - v2;
		break;
	}
	case '*': {
		sum = v1 * v2;
		break;
	}
	case '/': {
		sum = v1 / v2;
		break;
	}
	}
	return sum;
}


int main()
{
	struct shuju data;
	struct fuhao symbol;
	InitOperateNum(&data);   //调用数据 
	InitOperateSymbol(&symbol);  //调用符号 
	int i, t, sum, v1, v2;
	char c;
	i = t = sum = 0;
	char v[100] = { 0 };
	char* str = (char*)malloc(sizeof(char) * 200);
	while ((c = getchar()) != '\n')  //非空字符 
	{
		str[i] = c;
		i++;
	}
	str[i] = '\0';
	for (i = 0; str[i] != '\0'; i++) {
		if (i == 0 && str[i] == '-') {
			v[t++] = str[i];
		}
		else if (str[i] == '(' && str[i + 1] == '-') {
			i++;
			v[t++] = str[i++];
			while (str[i] >= '0' && str[i] <= '9') {
				v[t] = str[i];
				t++;
				i++;
			}
			Inshuju(&data, atoi(v));
			while (t > 0) {
				v[t] = 0;
				t--;
			}
			if (str[i] != ')') {
				i--;
				Infuhao(&symbol, '(');
			}
		}
		else if (str[i] >= '0' && str[i] <= '9') {
			while (str[i] >= '0' && str[i] <= '9') {
				v[t] = str[i];
				t++;
				i++;
			}
			Inshuju(&data, atoi(v));
			while (t > 0) {
				v[t] = 0;
				t--;
			}
			i--;
		}
		else {
			if (symbol.top == -1)
			{        //如果符号栈没有元素,直接把符号放入符号栈 
				Infuhao(&symbol, str[i]);
			}
			else if (judge(str[i]) == 1) { //如果此符号是'(',直接放入符号栈 
				Infuhao(&symbol, str[i]);
			}
			else if (judge(str[i]) == 2) { //如果此符号是'+'或'-',判断与栈顶符号是优先级 
				if (judge(Randfuhao(&symbol)) == 1) { //如果栈顶符号是'(',放入符号栈 
					Infuhao(&symbol, str[i]);
				}
				else if (judge(Randfuhao(&symbol)) == 2) { //如果栈顶符号是'+'或'-',则出栈运算 
					while (symbol.top >= 0 && data.top >= 1) { //循环出栈
						v2 = Putshuju(&data);
						v1 = Putshuju(&data);
						sum = Math(v1, v2, Putfuhao(&symbol));
						Inshuju(&data, sum); //将运算结果压入数据栈 
					}
					Infuhao(&symbol, str[i]); //新符号进栈 
				}
				else if (judge(Randfuhao(&symbol)) == 3) { //如果栈顶符号是'*'或'/',则进符号栈 
					while (symbol.top >= 0 && data.top >= 1) { //循环出栈
						v2 = Putshuju(&data);
						v1 = Putshuju(&data);
						sum = Math(v1, v2, Putfuhao(&symbol));
						Inshuju(&data, sum); //将运算结果压入数据栈 
					}
					Infuhao(&symbol, str[i]); //新符号进栈 
				}
				/*栈顶符号不可能是')',故不做判断*/
			}
			else if (judge(str[i]) == 3) { //如果此符号是'*'或'/',则判断与栈顶符号是优先级
				if (judge(Randfuhao(&symbol)) == 1) { //如果栈顶符号是'(',放入符号栈 
					Infuhao(&symbol, str[i]);
				}
				else if (judge(Randfuhao(&symbol)) == 2) { //如果栈顶符号是'+'或'-',则进符号栈
					Infuhao(&symbol, str[i]); //新符号进栈
				}
				else if (judge(Randfuhao(&symbol)) == 3) { //如果栈顶符号是'*'或'/',则出栈运算 
					while (symbol.top >= 0 && data.top >= 1) { //循环出栈
						v2 = Putshuju(&data);
						v1 = Putshuju(&data);
						sum = Math(v1, v2, Putfuhao(&symbol));
						Inshuju(&data, sum); //将运算结果压入数据栈 
					}
					Infuhao(&symbol, str[i]); //新符号进栈
				}
			}
			else if (judge(str[i]) == 4) { // 如果此符号是')',则出栈运算直到遇到'('
				do { //循环出栈直到遇到'('
					v2 = Putshuju(&data);
					v1 = Putshuju(&data);
					sum = Math(v1, v2, Putfuhao(&symbol));
					Inshuju(&data, sum); //将运算结果压入数据栈 
				} while (judge(Randfuhao(&symbol)) != 1);
				Putfuhao(&symbol); //括号内运算结束后使'('出栈 
			}
		}
	}
	free(str); //释放内存空间
	while (symbol.top != -1) {
		v2 = Putshuju(&data);
		v1 = Putshuju(&data);
		sum = Math(v1, v2, Putfuhao(&symbol));
		Inshuju(&data, sum);
	}
	printf("%d", data.data[0]);

	return 0;
}


posted @ 2021-04-27 10:57  kkkcmd  阅读(131)  评论(0)    收藏  举报