OO第一单元作业总结

符悦-21231042 学生 2024-03-20 17:24:50

架构总览

img

架构主要分为四个部分

  • TermFactor一同构成表达式的数据结构
  • Mapping和其管理类MapSet管理自定义函数
  • ParserLexer负责解析输入
  • 工具类ToolsIoManager,程序入口Main

表达式

表达式由TermFactor组成,按照递归下降的解析顺序储存为树状结构,其中Expr类既可以作为Term的上级也可以作为因子(和递归下降的解析顺序相同)

simplify

表达式的化简由函数simplify()负责,每一个类中都实现了相应的simplify()方法,这个方法会返回一个Expr类,类的内容为该因子/项化简后储存在一个表达式结构的结果,通过递归地调用这个方法就可以实现对表达式的化简,以下为Exp实现的例子

@Override
    public Expr simplify() {
        Expr ret = new Expr();
        Term term = new Term(1);
        term.setExp(copy());
        ret.getTerms().add(term);

        return ret;
    }

toString

调用toString()方法生成类的字符串表示,这个过程同样是递归的

实现toString()方法在debug的过程中也很有意义

derive

调用derive()可以获得该因子/项的导数,结果同样储存在Expr

map

通过传入mapping类(其中包含了自定义函数相关的参数),可以将因子中包含的函数代换为实际的表达式

copy

深复制

通过实现以上几个方法,就可以扩展Factor,实现新的因子

Mapping和MapSet

为了管理自定义函数,我设计了管理单个函数映射的Mapping和管理多个mappingMapSet

读取自定义函数时,会创建一个新的Mapping对象储存函数名,函数变量和函数表达式,表达式同样以递归下降的方式解析并储存,为了区分自定义函数中的变量和最终表达式中的变量,mapping中储存的函数变量名前会加上$

所有mapping对象由MapSet对象管理,通过调用mapSet.map(Expr),可以获取传入表达式对mapSet中所有mapping映射后的表达式

Parser,Lexer

管理输入解析,没有太多值得讨论的细节,主要采用递归下降的思想解析输入

Tools

作为工具类实现静态的工具函数

架构分析

整个架构充分考虑到了增量开发和扩展的需求,实际上在添加功能的时候效率尚可。然而在实现的过程中,由于因子和项之间复杂的包含关系导致类与类之间关系冗杂,难以维护(主要还是编写代码时不够认真,具体的实现没有做到足够精简),开发过程中经历了几次小规模的重构

  • 重构存储结构,项中存储Factor的数据结构从ArrayList<Factor>转为对每种Factor分别提供存储
  • 添加深复制,将之前代码中用到深复制的部分精简
  • 重写equals方法,提供因子与因子、项与项之间比较的功能,用于化简

对比其他同学分享的架构,我并没有单独实现多项式与单项式的类用于表示化简结果,因为事实上化简结果本身就是一个表达式,利用表达式进行储存绰绰有余

复杂度分析

Complexity metricsWed20 Mar 2024 11:10:22 CST
MethodCogCev(G)iv(G)v(G)
Main.main(String[])1122
expression.Deri.Deri(Expr)0111
expression.Deri.assign(MapSet)0111
expression.Deri.combine(Factor)0111
expression.Deri.copy()0111
expression.Deri.derive()0111
expression.Deri.equals(Object)2313
expression.Deri.getExpr()0111
expression.Deri.getType()0111
expression.Deri.getValue()0111
expression.Deri.map(Mapping)0111
expression.Deri.mulDeris(ArrayList, ArrayList)2133
expression.Deri.setExpr(Expr)0111
expression.Deri.setValue(BigInteger)0111
expression.Deri.simplify()0111
expression.Deri.toString()0111
expression.Deri.varify(ArrayList)0111
expression.Exp.Exp()0111
expression.Exp.Exp(Expr)0111
expression.Exp.Exp(Expr, BigInteger)0111
expression.Exp.assign(MapSet)0111
expression.Exp.combine(Exp, Exp)0111
expression.Exp.combine(Factor)0111
expression.Exp.copy()0111
expression.Exp.derive()0111
expression.Exp.equals(Object)3414
expression.Exp.getExpr()0111
expression.Exp.getType()0111
expression.Exp.getValue()0111
expression.Exp.isEmpty()0111
expression.Exp.map(Mapping)0111
expression.Exp.mul(Exp, Exp)3344
expression.Exp.setExpr(Expr)0111
expression.Exp.setValue(BigInteger)0111
expression.Exp.simplify()0111
expression.Exp.toString()0111
expression.Exp.varify(ArrayList)0111
expression.Expr.Expr()0111
expression.Expr.Expr(ArrayList)0111
expression.Expr.addExpr(Expr, Expr)1122
expression.Expr.addTerm(Term)3323
expression.Expr.assign(MapSet)1122
expression.Expr.combine(Factor)0111
expression.Expr.copy()1122
expression.Expr.derive()1122
expression.Expr.equals(Object)10838
expression.Expr.getTerms()0111
expression.Expr.getType()0111
expression.Expr.getValue()0111
expression.Expr.isEmpty()0111
expression.Expr.map(Mapping)1122
expression.Expr.mulExpr(Expr, Expr)7144
expression.Expr.setValue(BigInteger)0111
expression.Expr.simplify()5144
expression.Expr.toString()115810
expression.Expr.varify(ArrayList)1122
expression.Functor.Functor(String, ArrayList, BigInteger)0111
expression.Functor.combine(Factor)10636
expression.Functor.copy()1122
expression.Functor.derive()1122
expression.Functor.equals(Object)8828
expression.Functor.getName()0111
expression.Functor.getType()0111
expression.Functor.getValue()0111
expression.Functor.getVar(int)0111
expression.Functor.getVars()0111
expression.Functor.map(Mapping)0111
expression.Functor.mulFunctors(ArrayList, ArrayList)21589
expression.Functor.setValue(BigInteger)0111
expression.Functor.simplify()0111
expression.Functor.toString()1122
expression.Number.Number(BigInteger)0111
expression.Number.combine(Factor)1222
expression.Number.copy()0111
expression.Number.equals(Object)2313
expression.Number.getType()0111
expression.Number.getValue()0111
expression.Number.setValue(BigInteger)0111
expression.Number.simplify()1112
expression.Number.toString()0111
expression.Power.Power(BigInteger)0111
expression.Power.Power(BigInteger, String)0111
expression.Power.combine(Factor)3333
expression.Power.copy()0111
expression.Power.derive()0111
expression.Power.equals(Object)4515
expression.Power.getType()0111
expression.Power.getValue()0111
expression.Power.getVarName()0111
expression.Power.mulPowers(HashMap<String, Power>, HashMap<String, Power>)21599
expression.Power.setValue(BigInteger)0111
expression.Power.setVarName(String)0111
expression.Power.simplify()0111
expression.Power.toString()3414
expression.Term.Term(int)0111
expression.Term.addFactor(Factor)93310
expression.Term.assign(MapSet)3133
expression.Term.combine(Term)2223
expression.Term.combineTerms(Expr)18577
expression.Term.copy()4155
expression.Term.derive()9166
expression.Term.equals(Object)41231023
expression.Term.getDeris()0111
expression.Term.getExp()0111
expression.Term.getExprs()0111
expression.Term.getFunctors()0111
expression.Term.getNumber()0111
expression.Term.getPositivity()0111
expression.Term.getPowers()0111
expression.Term.isCombinable(Term)1912715
expression.Term.map(Mapping)3133
expression.Term.mul(Term)2223
expression.Term.setDeris(ArrayList)0111
expression.Term.setExp(Exp)0111
expression.Term.setFunctors(ArrayList)0111
expression.Term.setNumber(Number)0111
expression.Term.setPowers(HashMap<String, Power>)0111
expression.Term.simple()0111
expression.Term.simplify()8177
expression.Term.toString()1841315
expression.Term.valueEquals(Term, Term)0111
expression.Term.varify(ArrayList)3133
func.MapSet.MapSet()0111
func.MapSet.add(Mapping)0111
func.MapSet.assign(Deri)0111
func.MapSet.assign(Expr)0111
func.MapSet.map(Functor)1222
func.MapSet.mapNames()0111
func.Mapping.Mapping(String, ArrayList, Expr)0111
func.Mapping.assign(MapSet)0111
func.Mapping.getName()0111
func.Mapping.getVar(int)0111
func.Mapping.getVarByName(String)3323
func.Mapping.getVars()0111
func.Mapping.map(Functor)1212
func.Mapping.target(String)0111
func.Mapping.varify()0111
util.IoManager.IoManager()0111
util.IoManager.getInt()0111
util.IoManager.getLine()0111
util.Lexer.Lexer(String)0111
util.Lexer.getNumber()4144
util.Lexer.getVar()4144
util.Lexer.next()4234
util.Lexer.peek()0111
util.Parser.Parser()0111
util.Parser.Parser(Lexer)0111
util.Parser.Parser(Lexer, MapSet)0111
util.Parser.parseDeri()0111
util.Parser.parseExp()5144
util.Parser.parseExpr()2133
util.Parser.parseExprFactor()3133
util.Parser.parseFactor()6666
util.Parser.parseFunctor()1122
util.Parser.parseMapping()1122
util.Parser.parseNumber()4134
util.Parser.parsePower()3133
util.Parser.parseTerm()5145
util.Parser.setLexer(Lexer)0111
util.Parser.setMapSet(MapSet)0111
util.Tools.bigInteger2Int(BigInteger)0111
util.Tools.expFilter(String)6777
util.Tools.postProcess(String)2133
util.Tools.preProcess(String)16359
util.Tools.simplifiedString(String)3861011
util.Tools.trim(String)0111
ClassOCavgOCmaxWMC
Main2.0022
expression.Deri1.25320
expression.Exp1.30426
expression.Expr2.53948
expression.FactorTypen/an/a0
expression.Functor2.53938
expression.Number1.44313
expression.Power2.21931
expression.Term4.0023112
func.MapSet1.1727
func.Mapping1.33312
util.IoManager1.0013
util.Lexer2.40412
util.Parser2.33635
util.Tools4.831129
Packagev(G)avgv(G)tot
2.002
expression2.44295
func1.2719
util3.0087
Modulev(G)avgv(G)tot
lab12.43403
Projectv(G)avgv(G)tot
project2.43403

由idea插件计算出的指标来看,Term类的复杂度较高、方法中涉及到多个因子和递归调用的部分复杂度较高

代码量分析

img

由代码量统计也可以看出,Term类中代码行数最多,由于Term需要管理所有的因子,其中的耦合度也会较高,这给后期代码的维护带来了很多问题

架构迭代

从现在的架构出发,如果要加入一个新的因子,实际上比较简单

  • 实现因子的simplify(),derive(),toString(),copy(),map()
  • 重写equals()
  • Term中添加加入该因子的相关方法,修改TermtoString(),derive(),simplify()
  • Parser中添加解析因子的方法

如果要加入一种新的运算function,则

  • 为每一个因子实现Factor.function()
  • Term实现Term.function(),该函数调用所有因子的这个方法,形成运算结果
  • 结果以Expr返回

Bug分析

由于自己的疏忽,本单元的作业产生了大量的bug。

  • hw1时由于疏于测试,导致修复较小bug时导致了较大的bug,最后强测上传了一份带有严重错误的commit
  • hw2中错误使用addAll导致深浅复制相关的bug

在开发过程中,还产生过很多由于增量开发中没有完整改写相关方法导致的bug,这主要还是因为整个架构类与类之间的耦合度太高,方法冗杂导致的。

设计时,为了接口的精简导致了很多方法内部代码相当复杂,很容易在增量开发中因为疏忽没有完整的实现相关操作导致bug的产生。

另外,由于没有开发自动化测试工具,也导致了一些bug没有及时的被测试出来

互测

互测中寻找bug最强大的工具还是自动化测试脚本(然而由于我没有写生成器,所以并没有产生什么有价值的结果)

被测出来的bug除了上述情况,主要还是糟糕的实现导致的性能问题(在多个exp嵌套的情况下会超时)

虽然排序实际上可以有效的降低判断表达式是否相等的时间复杂度,但是由于重写compareTo很有可能导致更严重更隐秘的bug,因此没有做这样的实现

优化

关于性能分的优化,我采取的策略是

  • toString()方法过程中输出最复杂的表达式

  • toString()输出结尾通过后处理的方式,直接用正则表达式匹配输出结果来进行特殊情况的化简,例如在对exp的化简中

    public static String expFilter(String str) {
            Matcher matcher = expPattern1.matcher(str);
            if (matcher.find()) {
                return "exp(" + matcher.group(2) + ")^" + matcher.group(1);
            }
            // ...
            matcher = expPattern6.matcher(str);
            if (matcher.find()) {
                return "exp(" + matcher.group(2) + "^" + matcher.group(3)
                        + ")^" + matcher.group(1);
            }
    
            return str;
        }
    

显然,从性能的角度上这样处理并不好,但是这样处理拥有更好的可扩展性,同时在产生bug时,由于产生的原始字符串是完整形式,更容易进行debug

心得体会和未来展望

这次写的不满意。

  • 架构实现虽然已经是能想到的最优架构了,但是还是很不满意。由于因子与项的包含关系是相互的,导致整个表达式的构成耦合非常严重,产生bug时经常需要到多个地方反复跳转才能定位产生bug的位置。类似的,在增量开发时必须保证每一处需要改写的地方没有缺漏,不然也会造成困难。
  • 性能上,整个代码处理过程中包含大量的递归调用和对多个对象的深复制,性能上也很差
  • 测试中,由于在单元作业开始前在开发一个普适性的文本生成器用于产生测试用例,最后也没有开发数据生成器,间接造成了整个开发过程携带一些难以被发现但是造成很大问题的bug

总而言之,这个单元的作业写的并不好,下个单元要整改

  • 架构上对下个单元的作业会考虑的更细致,要把功能划分的更开,降低整体的耦合度
  • 选择更好的算法和数据结构
  • 多写注释
  • 数据生成器和批量测试脚本开发中...

下个单元见!

...全文
82 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

301

社区成员

发帖
与我相关
我的任务
社区描述
2023年北航面向对象设计与构造
学习 高校
社区管理员
  • YannaZhang
  • CajZella
  • C_ecelia
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧