结对编程收获
这次编程作业一路走来,学到了很多编程中实际很有用的东西,比如总体框架的设计,真的不能自己瞎想,应该到网上去查找相似的代码,先把别人代码的框架大致都看懂了,最好多看几份代码,比较一下几个框架的优点和缺点,然后在写代码之前规划好每个函数的输入输出,只有这样才算有点高年级同学写代码的样子。虽然在作业过程中,甲方的要求一直在改变,但是当你有了一个整体的框架感后,重构起来也能有全局感。
总体架构的设计启发
总体结构包括两个类,fomularNode和fomularCore,前者是表达式树的节点,包括必要的成员信息,后者是主要模块,public函数包括Calc Generate setting等主要函数。实现这些类的时候,对C++的public,private相关用法更加熟悉了。其实感觉现在C中的struct也可以这样写了。
其实我现在是学习安全出身的,所以区分private和public的意义我还是很清楚的,但是感觉我们这次作业应该会没有那个黑客闲着回来攻击我们。。。所以个人想法还是尽可能地还是用public,这样使得高层类不会重复调用底层类的组合引发错误。最后再根据实际情况改为private。当然如果真正地去写一些应用的软件的时候,写的时候还是要以安全第一。
class fomularNode
{
public:
int value;
bool chFlag;//if true,value is ascii
fomularNode* lchild;
fomularNode* rchild;
fomularNode() :value(0),chFlag(false),lchild(NULL),rchild(NULL){}
fomularNode(int val, bool flag, fomularNode* lch=NULL, fomularNode* rch=NULL)
{
value = val;
chFlag = flag;
lchild = lch;
rchild = rch;
}
};
class fomularCore
{
private:
vector<fomularNode*> fomulars;
vector<char> ops = {'+','-','*','/','^','(',')'};//all ops 需要保持最后两个是括号!
vector<string> finalRes;//最终结果,和Generate返回值一一对应
int maxopNum = 5;//每个表达式中运算符个数
int range = 100;//操作数数的上限
int precise = 2;//输出精度(最大为6)
int fomuNum;//表达式个数
int MaxRange = 100000;//运算中出现的最大数
bool fractionflag = true;//是否进行分数运算
double result[MAX_FOMU_NUM];//原始字符串运算结果
bool okFlag[MAX_FOMU_NUM];//判断原始字符串是否符合要求
public:
//省略
}
实现的主要类函数包括:
COREDLL_API string Calc(string inputFomu)
{
int multi;
int tp;
string tpFomu;
long res;
multi=findMultiple(inputFomu);
if (fractionflag&&multi != 1&&!withDot(inputFomu))//有浮点'.'就认为不是分数运算
{
tpFomu.append(to_string(multi));
tpFomu.append("*(");
tpFomu.append(inputFomu).push_back(')');
res = int(arthimetic(tpFomu));
tp = gcd(res, multi);
tpFomu = to_string(int(res/tp));
if(multi/tp!=1)
tpFomu.append("/").append(to_string(multi/tp));
return tpFomu;
}
else
{
//这里似乎有概率访问越界
tpFomu=to_string(arthimetic(inputFomu));
for (int i = 0; i < 6 - precise; i++)
tpFomu.pop_back();
return tpFomu;
}
}
COREDLL_API vector<string> Generate()
{
vector<string> rawFomu;
vector<fomularNode*> judgedFomu;
vector<string> finalFomu;
rawFomu = geneExp(3*fomuNum);//3是可选参数,保证能选出符合要求个数的表达式
toPostTree(rawFomu);//建树
toJudgeTree();//判断是否符合要求
for (size_t i = 0; i < fomulars.size(); i++)
{
if (okFlag[i] == true)
judgedFomu.push_back(fomulars[i]);//选出合适的树
}
finalFomu=fomusToStr(judgedFomu);//树转表达式,去除多余括号
for (size_t i = 0; i < finalFomu.size(); i++)
{
//cout << finalFomu[i] << '=';
//cout << Calc(finalFomu[i]) << endl;//测试输出
finalRes.push_back(Calc(finalFomu[i]));
}
return finalFomu;
}
COREDLL_API vector<string> getRes()
{
return finalRes;
}
COREDLL_API bool settingXml(string path)
{
//xml方式setting
string tpop;
ReadXml(path, fomuNum, maxopNum, range, tpop, fractionflag, precise);
for (size_t i = 0; i < ops.size(); i++)
{
ops.push_back(tpop[i]);
}
return true;
}
COREDLL_API bool setting(int foN,int maxopN,int MaxR,string op,bool fraction,int preci)
{
//非xml方式的setting
fomuNum = foN;
maxopNum = maxopN;
range = MaxR;
ops.clear();
for (size_t i = 0; i < op.size(); i++)
{
ops.push_back(op[i]);
}
fractionflag = fraction;
precise = preci;
return true;
}
从bug收获的
变量的取名真的很重要!!检查发现是判断二叉树是否相同的子函数的变量名写错了,左子树 lch 和右子树 rch 使用错误,再次阅读时也难以发现,说明一个好的变量名的重要性。
界面模块,测试模块和核心模块的松耦合
本次课程UI组和Core组的划分,让我对MVC原则理解更深刻了。具体来说,Core组主要负责程序核心(Model),UI 组的工作主要是用户交互(Controller)和数据显示(View)。当然,Core和UI组的功能划分也没有这么详细,具体情况具体分析,主要还是看编程者的思路。 具体对接的时候,有的UI组做的东西多,其实把core的一部分也做进来了,所以对接的时候还是很麻烦的。
感觉xml真心好用
这次xml读写部分是我写的,感觉xml读写方式真心好用,虽然最后貌似没有UI组用xml,大家都是通过函数接口直接通信的。但是涉及到不同的语言的交互,xml就能发挥它的威力了,比如我用C#写一个UI,用matlab写一个core,这个时候用xml就很方便了。而且相较于txt,xml应该不会受到编码方式以及其他各种格式的影响。
我认为个人作业和结对编程的区别
这次作业最大的收获就是明白了结对编程的意义,有一个好的队友真地很重要。在和队友结对编程的过程中,能看到对方的优点,同时看到自己的缺点,队伍里两个人由此都能得到巨大的进步。从开发层次来说,编写的代码同时经过2个人的眼睛,设计质量和代码质量都有了很大的提高,同时代码也更加规范了,因为谁也用不惯对方的代码风格,最后更多地偏向于书上提供的规范。同时,在这次结对编程中,认为马同学解决问题的能力很强,在难以描述的bug面前,总能在很短时间内解决问题,给我们的结对编程工作带来更大的信心以及更高的满足感。同时我们不断地编程过程,就是不断的复审过程,因为我们的一举一动都在对方的视线之内,在这种督促的压力下,我们的工作显得更加认真,并且我们要提高自己能力不让对方小看。结对编程使这次作业开发流程显得更加规范。
走上工作岗位之后,是否会采用结对编程?
走上工作岗位之后,很大概率不会采用结对编程。当然,培训新人的时候可能会考虑。因为结对编程特别适合于知识的分享和传递,特别适合于帮助开发者快速熟悉自己所不熟悉的领域。更多情况下是两个人水平相似,因此结对编程有浪费时间的嫌疑。另外结对编程的双方如果性格不合(这点概率是很大的),可能陷入很多的争吵,而导致进度停滞不前。而且结对要求结对的双方都要保证有充沛的精力,因为很容易疲劳。最后,结对编程不能完全替代代码评审,虽然结对编程对代码的审核程度比代码评审更加细致,但两个结对的人有一定的思维趋同性,从而忽略同样的问题。
结对编程,教会我什么
这两次作业给以后开发流程有很多可以借鉴的地方,其中个人感觉最重要的一点是要跑在时间前面,因为我(陈灿)第一次个人作业是在截至时间前一分钟提交的。把事情留在最后,的确能提高时间的效率,但是这是在拿自己project的质量冒险,因为在软件开发的过程中,可能会有各种各样的bug跳出来,而且在太大压力下编写的程序很大概率会有一些潜在的bug。在接下工程的那天开始,就得有一个详细的计划,规定XX天之前必须完成什么阶段。而且还要有足够的时间余量,来应对突发情况,这才是一个合格的程序员应该具备的素养。