BUAA_OO_Unit1

度量分析

HW1

UML图

在这里插入图片描述

在这里插入图片描述

各个类方法,分支和行数

ClassMethodbranchline(695)LCOM方法内聚缺乏度FANIN类的扇入FANOUT类的扇出
Term1610290.33333333300
Simplify28541070.85714285700
Power148150.500
Parser142652000
Num148160.500
MainClass13223-100
Lexer163431000
Expr1614370.33333333300

根据这个表可以看到Simplify方法的内聚缺乏度过高,该类的内聚性不高。现在回想起来,当时我设计时就出现了问题,这个类的设计初衷是把一个去掉多余括号的表达式,重新拆分并化简。这个想法使我的代码对hw2新增因子exp出现了很强的不适应性,而且也没有使用拆分复杂度到细小的类的思想。这个架构设计我下文会讲述。

每个方法

TypeMethodNameLOC(代码行数)CC(圈复杂度)PC(方法中传参个数)
ExprExpr510
ExpraddIndex311
ExpraddKind311
ExpraddTerm311
ExprtoString2960
Exprsimplify1432
FactoraddIndex011
LexerLexer411
LexergetNumber820
LexergetIndex1640
LexermeetX310
Lexernext1750
Lexerpeek310
MainClassmain1711
MainClassexprDelete1021
NumNum512
NumaddIndex311
Numsimplify1540
NumtoString820
ParserParser311
ParserparseExpr1740
ParserparseTerm1231
ParserparseFactor4570
PowerPower311
PoweraddIndex311
PoweraddKind311
PowertoString2050
SimplifySimplify311
Simplifyoperate4370
SimplifymulOperate4891
SimplifymulPrintf2362
SimplifyaddPrintf3661
Simplifycompare312
SimplifygetIndex1021
TermTerm310
TermaddFactor311
TermaddKind311
TermgetKind310
TermtoString2140
TermExprMult1432

圈复杂度较高的是Expr的toString()方法,和Parser类的parseFactor方法,和Simplify的绝大部分方法。久其原因是我在该toString方法内进行了部分化简和合并运算,没有另起一个方法或类。而Simplify类究其原因是我的设计动机:把一个表达式字符串重新拆分并化简,出现了问题。我没有在Term类和Expr类提供专门的化简方法。这是因为我认为Expr和Term类可以调用Simplify方法,进行化简。实时证明Simplify的化简方法不专一而且不适配Expr类内部的输入,后续的迭代证实了这个错误。

HW3的基本情况和HW1类似。而且因为HW2和HW3相比架构变化不大,没有列出它的UML图和度量分析。

HW3

UML类图

在这里插入图片描述
在这里插入图片描述

HW3可以看到,本来Factor不是Term类的接口,但是参考实验代码,这样设计可以方便求导,求导后返回值是Term类型,更容易承载求导后的结构。对我来说,这是一种架构的改变,也是一种思想的改变。当然当时我没有意识到这一点。

各个类方法,分支和行数

ClassMethodbranchline(695)LCOM方法内聚缺乏度FANIN类的扇入FANOUT类的扇出
Term110481020.400
Simplify210921860.88888888900
Power168200.33333333300
Parser164692000
Num168200.500
MainClass14639-100
Lexer164643000
FuncStatic133000
Func1710450.28571428600
Expr110521260.300
Exp15110.400
Derive1580.400

每个方法

TypeMethodNameLOC(代码行数)CC(圈复杂度)PC(方法中传参个数)
TypeMethodNameLOC(代码行数)CC(圈复杂度)PC(方法中传参个数)
-----------------------------------------------------------
DeriveDerive311
DerivetoString410
DeriveaddIndex211
Derivederive410
Deriveclone310
ExpExp311
ExpaddIndex211
ExptoString410
Expderive610
Expclone410
ExprExpr510
ExprmergeExpr1232
ExpraddIndex411
ExpraddKind311
ExpraddTerm311
ExprtoString3360
Exprsimplify2452
ExprdeleteExp50113
Exprderive3050
Exprclone820
FactoraddIndex011
Factorderive010
Factorclone010
FuncFunc511
FuncaddIndex211
FuncaddFactor311
FunccreatExpr2730
FunctoString1020
Funcderive820
Funcclone720
FuncStaticaddAll311
LexerLexer411
LexergetNumber820
LexergetIndex1640
LexermeetX310
Lexernext3390
Lexerpeek310
MainClassmain2721
MainClassexprDelete1021
MainClasssort921
NumNum512
NumaddIndex311
Numsimplify1540
NumtoString820
Numderive510
Numclone310
ParserParser411
ParserparseExpr1740
ParserparseTerm1231
ParserparseFactor64130
ParserfactorExpr1720
ParsermakeFunc2240
PowerPower311
PoweraddIndex411
PoweraddKind211
PowertoString1330
Powerderive1530
Powerclone310
SimplifySimplify311
Simplifyoperate5790
Simplifychange1032
SimplifymulOperate61111
SimplifymulPrintf3984
SimplifyaddPrintf3761
Simplifycompare312
SimplifygetIndex1021
SimplifydeleteExp50113
TermTerm310
TermaddFactor311
TermaddKind311
TermgetKind310
TermtoString2860
TermExprMult2552
TermdeleteExp50113
Termderive2250
TermaddIndex211
Termclone820

架构设计体验

HW1架构可以用这张类图表示
在这里插入图片描述

MainClass是程序入口,内部使用Lexer类遍历输入的表达式,Parser通过使用Lexer,逐渐按表达式(Expr),项(Term),因子(Factor)的层次结构递归构造,最后返回一个Expr类的因子,最后调用Simplify类,化简toString()后的Expr。

    Simplify simplify = new Simplify(result);
    result = simplify.operate();

这个架构参考了课程组提供的代码。但是我也有思考不周的地方,对于带指数的表达式因子,我是把指数存入表达式因子内部,把化简因子的任务交给了表达式类,破坏了Expr类的简洁性,提高了求导迭代的难度。同时,我对Simplify类的定义:处理一个经过处理的不带括号的表达式,影响了我之后的迭代,最终使Simplify类不堪重负,而且也未被了递归下降法和层次分析。

HW3和HW1相比,多出的几个因子,只是在HW1的基础上新建几个类,在使用Factor接口。比较重要的实现是Exp即指数函数,主要的变化是Simplify类,里面的部分方法被重写,方法之间的调用关系也被改变。HW2新添的指数函数因子对我的整个代码设计是一个很大的打击。因为exp在化简后还留有括号,括号内出现的+和这个因子是一体的。这是一场灾难。
例如:
for (String string : expr.split(“\+”))
这样的代码划分表达式是很脆弱的,而且和解析表达式的递归下降法有很强的割裂感,也可以看成是不成熟的正则表达式。严格说这是因为自己偷懒,没有为Expr和Term等类设计专门的化简方法,使得本应承担合并同类项工作的Simplify类做了一些额外的工作。
同时,对新增指数函数exp的不适配的主要原因也可能是自己第一次作业架构设计,没有把Factor作为Term类的接口,处理项内的表达式乘法的时候是利用字符串拼接,而不是重新构造部分层次关系,再统一使用toString()方法。
Term的toString()方法部分内容:
String termOne = sb.toString();
String termTwo = iter.next().toString();

sb = ExprMult(termOne,termTwo);
最后,关于我怎么处理指数函数exp。使用字符串的replace方法,把exp替换成形如a1a的标识语句,建立HashMap的映射机制,化简处理后再替换回去。这种字符串处理,使第二次作业出现CPU运行时间过长。望各位引以为戒。
以上是我的架构设计和不足之处。悲观的说,自己的架构不适用任何一种处理后会出现新的带括号因子的扩展。

程序bug

HW1,自己的主要bug是对Term类的toString()方法处理失误。简单来说,如果整个项有表达式而且为负,我只会简单的在表达式前加一个‘-’号,显然这是错的。当时设计时,我虽然考虑到了带负号的情况,但是没考虑到Term内表达式因子带负号,仅仅简单处理了负号。这种设计情况的缺失,好的方法是利用自动化评测或者尽可能构造所有情况的样例。
HW2,自己的bug是CPU运行时间过长,原因是Expr类toString()方法的表达式乘表达式即指数运算,产生了一个十万级的字符串,这时使用字符串构造方法及其耗费时间。这个问题出在架构设计和实现思路上,这只能通过经验或者好的架构设计来解决。

TypeMethodNameLOC(代码行数)CC(圈复杂度)
ExprtoString336
TermtoString214
很明显,出现bug的方法,代码行过长,圈复杂度过高,其他方法圈复杂基本不超过2,证明这些方法职责划分不清晰,功能过多,设计不合理。可以把方法内不同功能的代码划分出去创建新的单一功能的方法。

发现别人程序bug策略

手动构造一些特殊样例,但是基本无效,没有阅读代码进行白盒测试。
放到自动评测机上跑。

自己的优化

hw1优化有认真写
数字之间进行运算
同类项合并不包括指数函数
不同项排序,避免负项在首位
相同指数函数合并没有公因数提取,鸵鸟战术
通过字符串处理进行优化,但是在某种程度上老的处理影响了自己的重构改进。
我的优化没有保证自己程序的简洁性,比如自己只想到总的Simplify类,没有想到一些分开的处理方法,比如exprSimplify,termSimplify。相信这样处理不会让某个类过于庞大,也容易理清自己的思路。 正确性得到了一定保障。优化过后本来不能过的测试点通过了。没有数据。好奇怪

心得体会

首先,我第一次使用递归下降法解析字符串,同时也体会到层次化设计的优越之处。第一次看课程组的代码感到醍醐灌顶,发人深思。这种设计架构使复杂的结构层次化,简单化。学到了新知识。其次,单元一可能是我大学以来第一次认真使用编辑器自带的调试工具。之前用printf法解决程序bug,很舒适。但是这个单元使用了递归的方法处理内容,使用调试工具打断点,一步一步看还是很有必要的。最后,自己的第一单元还是有点遗憾的。没有实现搭一个自己评测机的想法。HW1自己的开头不是很好,限制了自己的思想。指数等细节处理不到位。最后的化简没有认真做,有1*出现等。现在回想自己为了实现HW2的功能改HW1的代码也花费了很多时间和心态,效果也不理想,期望自己之后的学习实践中可以提升能力、收获经验。

未来方向

可以优化一下oolens公众号的内容。

  • 41
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值