2024面向对象设计与构造-第一单元总结

先放张没什么关系的图

写在前面的话

话说在此之前我并没有上过oopre,同时我的之前的java课程是依靠文心一言写的大作业,所以java也不怎么会,导致适应极为艰难。

程序结构分析以及架构设计体验

由于我认为这两个部分都是和HW紧密相关的因此我将这两个部分放在一起讲了。

HW1

首先来讲hw1的一大难点是git的使用,不然提交作业都是不太方便的。
HW1的要求是将表达式给化简,给定的表达式包含了括号,+,-,*,^,)等符号,因子则有幂函数以及数字等,最后要求输出也是一个表达式。
我采用了课程组所推荐的梯度下降算法作为基本架构,通过逐级解析因子,项,表达式来拆分原来的表达式。其中任何一个表达式可以拆分成一堆带符号的项相加减,而任何一个项又可以拆分成一堆带符号的因子相加减,因子又可以分为幂函数,数字,表达式三类,若是表达式因子那么就按照上述的方法进行嵌套就好。这样看来其实就只需要两个类:因子(Factor)类以及项式(Term)类。项中所有的因子视为相乘的形式而表达式因子之中的所有的项视为相加的形式。
在计算以及化简上面我定义了一个基本项,可以表示为a*x^b的形式,它也可以被视为Term的一种,在Term类以及Factor类之中都有一个cal方法,该方法会计算其中所有的因子或者是项,将其化简为一个基本项列表返回,上层的类可以根据这个列表进行相应的计算。
以上就是我的代码的基本框架,该框架的优点是可拓展性极好,之后的迭代几乎都是在此基础之上进行的,迭代拓展十分方便。但是相对来讲,递归的次数相对来说有些太多了,可能结果会导致代码运行的时间复杂度有一些太高了,为后面hw2爆掉埋了一些隐患。

从类的设计上面讲,由于我之前没有怎么接触过java,因此不会接口,抽象类之类的,导致我没有使用这些,因此类其实看着挺简单的,Main之中调用了所有需要的流程,Token是表达式构成的基本单元,Lexer用于解析输入的表达式字符串,Paser用于将这些解析后的符号形成嵌套结构,Factor是因子类,既可以表示表达式因子也可以表示变量、数字因子等,在这里我的解耦做的不太好,Term则是表达式之中的相加或者相减的项,取决于表达式的结构,当调用Factor类以及Term类之中的cal方法时都会返回一个计算化简之后的基本Term数组,在Main的processor之中解析为字符串。
下面是类图:
类图hw1
代码总量500余行
在这里插入图片描述

方法总数共41个,关于其复杂度:

Complexity metrics�ܶ�19 3�� 2024 14:30:24 CST
MethodCogCev(G)iv(G)v(G)
Exp.getTerms()0111
Factor.Cal()20399
“Factor.Factor(int, ArrayList, BigInteger)”0111
“Factor.Factor(int, BigInteger, BigInteger)”0111
Factor.getCon()0111
Factor.getNum()0111
Factor.getPwAll()0111
Factor.getPwN()0111
Factor.getSign()0111
Factor.getTerms()0111
Factor.getType()0111
Factor.setPwAll(BigInteger)0111
Lexer.Lexer(String)1731314
Lexer.getToken()0111
Lexer.getToken(int)0111
Lexer.ned()0111
Lexer.ned(int)0111
Lexer.next()0111
Main.main(String[])2531415
Main.processor(ArrayList)2451011
Parser.Parser(Lexer)0111
Parser.expPer(int)8388
Parser.facPer()10499
Parser.start()0111
Parser.terPer()5155
Term.Cal()10277
“Term.Term(ArrayList, int)”0111
“Term.Term(int, BigInteger, BigInteger)”0111
Term.getNum()0111
Term.getPwN()0111
Term.getSign()0111
Term.getTerms()0111
Term.reverSign()2113
“Token.Token(Type, BigInteger)”0111
“Token.Token(Type, String)”0111
Token.getCont()0111
Token.getNum()0111
Token.getType()0111
“compare(Term, Term)”7n/an/an/a
类复杂度
ClassOCavgOCmaxWMC
Exp1.0011
Factor1.73919
Factor.Typen/an/a0
Lexer2.831217
Main7.671123
Parser3.40717
Term2.00716
Token1.0015
Token.Typen/an/a0
总复杂度
Projectv(G)avgv(G)tot
project2.89110

在该阶段我并不熟悉,内聚不高(比如tostring这种功能的方法居然在main里面!),耦合较高,(Term余Factor都承担了两种不太相同的功能),优点是非常具有可拓展性

bug分析:该次作业唯一的bug是有个地方两个BigInteger比较时有一个toString另一个没有使得.equal()一直返回false,导致只要我的表达式指数为0就会爆炸。bug代码修复后代码行和圈复杂度上的没有差异。
我寻找别人的bug也是通过构造(0)^0这样的情况进行hack,成功了一次,后面构造--------(x) -+x成功了一次。

HW2

HW2新加入了自定义函数类以及exp,这里我使用了字符串替换展开处理自定义函数,为基本项增加了一个成员变量来表示exp内的表达式,添加了equation类处理自定义函数。剩下的与之前hw1基本相同。优化在于增加了一个遍历的方法来为exp之内的表达式因子寻找最大公因数,然后遍历最大公因数的因子比较并得到最短的exp表达字符串。(也正是因为这个原因我的hw2爆了TLE)。
类图如下:
在这里插入图片描述
复杂度飙升的一次作业呀!
首先是提高了内聚,为Factor以及Term都配置了相应的toString方法,嵌套返回字符串,移除了原先的Main里面的将Term转化为字符串的方法。同时拆解了计算方法如加减乘除等,提高了代码复用性。此外添加了equation类进行预先的字符串展开处理,将所有的函数进行替换。其它的类功能基本没有发生变化。
代码量如下:
在这里插入图片描述
现阶段代码总量一共920行左右,主要是由于Term反复调用计算相关函数导致代码量飙升。
下面是方法的复杂度表格,总共60个方法

Complexity metrics�ܶ�19 3�� 2024 14:59:57 CST
MethodCogCev(G)iv(G)v(G)
Equation.Equation(String)0111
Equation.convert(ArrayList)2133
Equation.convotor(String)3133
“Equation.fPaser(String, int)”14577
Factor.Cal()3361616
“Factor.Factor(int, ArrayList, BigInteger)”0111
“Factor.Factor(int, BigInteger, BigInteger)”0111
“Factor.Factor(int, Factor)”0111
Factor.getPwAll()0111
Factor.setPwAll(BigInteger)0111
Factor.toString()0111
Lexer.Lexer(String)1831415
Lexer.getToken()0111
Lexer.getToken(int)0111
Lexer.ned()0111
Lexer.ned(int)0111
Lexer.next()0111
Main.main(String[])1122
Main.preProcessStr(String)2155
Parser.Parser(Lexer)0111
Parser.expPer(int)8388
Parser.facPer()1251111
Parser.start()0111
Parser.terPer()5155
Term.Cal()10277
“Term.Term(ArrayList, int)”0111
“Term.Term(int, ArrayList)”0111
“Term.Term(int, BigInteger, BigInteger)”0111
“Term.Term(int, BigInteger, BigInteger, ArrayList)”0111
“Term.add(Term, Term)”6444
“Term.cao(Term, ArrayList)”6444
Term.checkSimple(ArrayList)75810
“Term.compareExp(Term, Term)”8545
“Term.compareTerm(Term, Term)”11666
Term.dao()11177
Term.expToString(ArrayList)10346
Term.expToStringPlus(ArrayList)2181014
Term.findAllFactors(BigInteger)3133
Term.getBasicNum()0111
Term.getBasicPwN()0111
Term.getBasicTermsInExp()0111
Term.getSign()0111
Term.isMatch(String)1122
Term.listToString(ArrayList)5244
Term.merge(ArrayList)14478
“Term.merge2(ArrayList, ArrayList)”0111
“Term.multiply(Term, BigInteger)”1222
“Term.multiply(Term, Term)”0111
Term.reverseSign()2113
Term.termQsort(ArrayList)0111
Term.toString()2321011
TermKey.TermKey(Term)0111
TermKey.getExpTerms()0111
TermKey.getPwN()0111
“Token.Token(Type, BigInteger)”0111
“Token.Token(Type, String)”0111
Token.getCont()0111
Token.getNum()0111
Token.getType()0111
“compare(Term, Term)”0n/an/an/a
类复杂度
ClassOCavgOCmaxWMC
Equation3.50714
Factor3.001521
Factor.Typen/an/a0
Lexer3.001318
Main2.0024
Parser3.60818
Term3.6114101
TermKey1.0013
Token1.0015
Token.Typen/an/a0
项目总复杂度
Projectv(G)avgv(G)tot
project3.46204

bug分析: 就这次的bug而言主要在于时间性能问题上面,由于我对exp求长短进行了相关优化导致了时间复杂度的上升(在于求系数的共同因数上面),得遍历系数公约数的每一个约数,导致我的代码被hack了三次超时,同时强测也挂掉了一个点,通过回滚版本到未优化版本就直接全对了。然后我通过为优化添加了字符串长度限制进行了判别,若是系数公约数大于10000就放弃精细的求最优解,只是比较提取最大公约数后与原exp字符串长短寻求更优解,这样修改后也避免了超时。
本次没有找到别人的bug。

HW3

增添了求导因子以及自定义函数互相引用,意外发现我之前写的字符串替换已经可以满足这个要求了,所以剩下的任务只有一个求导。为TEerm类写了一个求导方法,按照相乘的求导的规则对基本项进行求导,同时为Factor类写了一个求导方法,按照相加的求导规则返回该类中因子求导的结果即可。
请添加图片描述
在HW3最终版本之中一共有7个class(与HW2相同),其中Term类和Factor类各自有6个属性,lexer有3个属性,equation有4个属性,Paser有1个属性,Token有1个属性。
方法数量则是Term类最多,有19种其余的如token有4种,paser有5种,equation有4种,Factor有7种,lexer有6种。
代码总量有900余行,其中Term类的代码总量最高有400余行
请添加图片描述
对于方法复杂度如下:

Complexity metrics��һ18 3�� 2024 20:38:18 CST
MethodCogCev(G)iv(G)v(G)
Equation.Equation(String)0111
Equation.convert(ArrayList)2133
Equation.convotor(String)3133
“Equation.fPaser(String, int)”14577
Factor.Cal()3361616
“Factor.Factor(int, ArrayList, BigInteger)”0111
“Factor.Factor(int, BigInteger, BigInteger)”0111
“Factor.Factor(int, Factor)”0111
Factor.KaiDao()0111
Factor.getPwAll()0111
Factor.setPwAll(BigInteger)0111
Factor.toString()0111
Lexer.Lexer(String)1931516
Lexer.getToken()0111
Lexer.getToken(int)0111
Lexer.ned()0111
Lexer.ned(int)0111
Lexer.next()0111
Main.main(String[])1122
Main.preProcessStr(String)2155
Parser.Parser(Lexer)0111
Parser.expPer(int)8388
Parser.facPer()1461313
Parser.start()0111
Parser.terPer()5155
Term.Cal()10277
“Term.Term(ArrayList, int)”0111
“Term.Term(int, ArrayList)”0111
“Term.Term(int, BigInteger, BigInteger)”0111
“Term.Term(int, BigInteger, BigInteger, ArrayList)”0111
“Term.add(Term, Term)”6444
“Term.cao(Term, ArrayList)”6444
Term.checkSimple(ArrayList)75810
“Term.compareExp(Term, Term)”8545
“Term.compareTerm(Term, Term)”11666
Term.dao()11177
Term.expToString(ArrayList)10346
Term.expToStringPlus(ArrayList)2181014
Term.findAllFactors(BigInteger)3133
Term.getBasicNum()0111
Term.getBasicPwN()0111
Term.getBasicTermsInExp()0111
Term.getSign()0111
Term.isMatch(String)1122
Term.listToString(ArrayList)5244
Term.merge(ArrayList)14478
“Term.merge2(ArrayList, ArrayList)”0111
“Term.multiply(Term, BigInteger)”1222
“Term.multiply(Term, Term)”0111
Term.reverseSign()2113
Term.termQsort(ArrayList)0111
Term.toString()2321011
TermKey.TermKey(Term)0111
TermKey.getExpTerms()0111
TermKey.getPwN()0111
“Token.Token(Type, BigInteger)”0111
“Token.Token(Type, String)”0111
Token.getCont()0111
Token.getNum()0111
Token.getType()0111
“compare(Term, Term)”0n/an/an/a
类总复杂度如下
ClassOCavgOCmaxWMC
Equation3.50714
Factor2.751522
Factor.Typen/an/a0
Lexer3.171419
Main2.0024
Parser3.80919
Term3.6114101
TermKey1.0013
Token1.0015
Token.Typen/an/a0
项目总复杂度如下
Projectv(G)avgv(G)tot
project3.47208

bug分析:本次没有bug也没有找到别人的bug。

新的迭代场景预设

预设迭代为添加cos以及sin等三角函数。
在基本项之中加入cos列表以及sin列表即可,在求导时仍然使用相关求导法则,最后改写cal方法即可。

心得体会

oo,爽!
主要感觉熟悉了java语言,对于面向对象思想有了一定的了解,还挺有收获的。

未来方向

其实我觉得这一单元做的不错,难点主要在hw2上面,讲求了一个循序渐进,可能未来不需要太大修改,只需要把互测平台交互做好一些就行(格式检查错误类型反馈),另外可以稍微放宽互测条件限制如增加字符串最长长度等。

不正经的总结

本次lab实在是太简单了,hw1缺乏思维难度,所有的问题按照课上讲的就完了,hw2优化只需要求个公因数,缺乏对于思维挑战性也没有太多代码增量,hw3更是简单的离谱,代码量以及思维量都很少,建议给下一届把sin,cos加回来达到适中难度🥵🥵🥵(bushi
Ciallo~(∠・ω< )⌒★

  • 20
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值