oop作业集4~6总结

oop作业集4~6总结

前言

本次的三次作业主要围绕数字电路模拟展开,模拟电路信号在数字电路的传递的过程。三次作业层层递进,作业4考察的是基础的门电路设计,涉及到与门、或门、非门、异或门、同或门五种元件,这五种电路元件的作用,在题集的题目当中有详细的介绍,这里就不做过多的赘述了,重点在于如何实现电路的传递,计算与输出这一系列的过程。作业5在作业4的基础上又增加一些电路元件,主要增加了三态门、译码器、数据选择器、数据分配器,本题整体的模拟思路与作业4相似,但是相较于作业4,作业5的新增的元件又增加了控制引脚,并且输出引脚也由原来的一个并且固定为编号0到增加了多个输出引脚。作业6在作业5的基础上又增加了子电路和五种异常输入信息的校验,在这里我们可以采取组合模式,将子电路当成一种特殊的电路元件,这个电路元件需要输入,同时也有输出,但是这个电路元件由多个基础的电路元件组合而成。

以上这些就是对每个题目集的大致的一个介绍,下面我想争对这些题目集来具体分析一下我的一些设计与分析。

知识点总结

1.面向对象编程和面向对象设计最基础,最核心的原则SOLID原则。
2.充分考虑系统的可复用性、可维护性与可扩展性。
3.基础的电路知识。
4.基础的图论知识,如邻接表和拓扑排序等
5.对组合设计模式的理解和使用。
6.复杂字符串的处理和正则表达式的使用。
7.状态机与语法分析。
8.异常处理与优先级逻辑。
9.测试点的等价类划分,边界值分析,独立性和原子性。

题量

ps:在题量这一部分我的分析指标主要是在没有大量冗余代码的前提下完成这一题目集所需要的实际代码数量和所需要的模块数量以及每个类的复杂度。

通过分析我的三次作业成功提交且通过的代码可以发现(虽然最后一次没有完全通过,但是应该大致接近):
三次作业的难度呈现逐渐递增的增长趋势。

1.在oop4中完成题目所需要的功能,大概需要花费400多行代码,包括入口类在内一共需要12个类。
2.在oop5中完成题目所需要的功能,大概需要花费1000多行代码,包括入口类在内一共需要20个类。
3.在oop6中完成题目所需要的功能,大概需要花费1500多行代码,包括入口类在内一共需要24个类。

三次作业的难度等级依次是偏难,困难,困难。

难度

难度基于个人的主观做题感受

这次的三个作业题的难度感觉有点大,主要难在这个数字电路模拟系统比较抽象,之前也没怎么接触过,其次这次的题集考到了一些图论相关的知识,比如在信号传递和计算的过程需要用到拓扑排序,还有这道题的边界条件非常多需要仔细考虑清楚。

设计与分析

基于本人三次作业集中题目的提交源码进行分析,参考SourceMontor的生成报表内容以及PowerDesigner的相应类图

数字电路模拟程序-1

1.设计思路
为了完成这个基础逻辑门元件构成的电路,针对数据的存储,我设计了五个类:AndGate,OrGate,NotGate,XorGate,NxorGate,分别对应数字电路系统中的五个元件,与门,或门,非门,异或门,同或门,使用一个Circuit来记录整个电路的信息。
为了完成复杂的电路信息解析工作,我设计了CirCuitParser专门用于输入的电路信息的解析并完成相应的电路的构建工作。
针对信息传递的过程,我设计了DestPin和Stimulator,前者起到的作用是正确的传递信息,这个DestPin相当于元器件上的输入引脚其记录了针脚编号,其内部拥有所连元器件类的引用,在针脚接收到数据后,可以调用对应元器件内部的方法将信息传递给元器件。后者使用队列的方式通过将具备计算条件可以输出信号的元件压入队列,然后再出队,将该元器件的信号传递给它所连的元器件的输入引脚,从而完成整个信号传递的过程。
最后按照题目给定的输出条件,使用一个单独的OutputResult完成对各个元件信号的输出。

2.类图分析(解释和心得)
alt text
在这个图中可以看到我设计思路当中所提到的一些类,其中AndGate,OrGate,NotGate,XorGate,NorGate都是门类,所以可以设计一个父类Gate类,抽取一些共性,如id,name,type这些基本的属性以及input,inputCount等等用于具体的计算逻辑,父类提供了一个模板,五个门类继承父类,但是由于不同的子类计算信号的逻辑不同,所以需要将计算方法做成抽象类,交由子类具体实现,这在一定程度上实现了多态。
Gate作为Circuit一个组成部分,构成了Circuit类,所以将Gate与Circuit之间构成组合关系。
唯一让我感觉到这个设计的不足之处在于,DestPin作为Gate的一个组成部分,与Gate之间构成组合关系,这个层级似乎有点颠倒,虽然当前的设计可以保证基本功能的实现,但是从语言和逻辑层面似乎还有缺陷,所以需要后期的进一步修正。

3.代码复杂度分析
alt text
【method复杂度分析】:
经 MetricsReloaded 分析, 本系统 29 个方法中,28 个方法的 CogC ≤ 8,平均 CogC 仅为 1.84, 表明整体设计高度模块化。唯一异常点为 Simulator.run(Circuit)(CogC=29, v(G)=13, iv(G)=12, ev(G)=3),其高复杂度源于四层嵌套控制流与非结构化循环。通过提取 SimulationEngine 调度器,可将该方法复杂度降至安全阈值内,同时提升系统可测试性与可维护性。

alt text
【class复杂度分析】:
1.系统整体复杂度分析:
平均OCavg = 2.05表示整体类方法的平均复杂度较低(建议阈值≤3),说明系统大部分类设计简洁,符合单一职责原则。
平均OCmax = 3.50表示类内最复杂方法的平均复杂度可控,但存在个别类偏离整体趋势。
总WMC = 78表示系统整体复杂度规模适中,未出现大规模复杂类堆积。

2.需要重点关注的类:
(1)Simulator(OCavg=12.00,OCmax=12,WMC=12)
分析:OCavg高达12,远超阈值(建议≤3),且OCmax与OCavg相等说明类中仅1个方法,但该方法复杂度极高。

原因:循环嵌套过深,Simulator的run方法中三层循环导致逻辑难以追踪,状态耦合程度过大,deliver队列和gates状态紧密耦合,修改一处就可能影响多处。

优化方案:采取职责分离的方式,将Simulator拆分成三个类,分别负责初始化,信号传递和输出计算。

(2)CircuitParser(OCavg=4.40,OCmax=8,WMC=22)
分析:OCavg=4.40超阈值,OCmax=8类内存在复杂方法,WMC=22方法数量过多。

原因:解析逻辑太复杂,这个类内的parser()方法同时处理输入解析、门解析、边解析、输出解析这四种逻辑违反单一职责原则。

优化方案:将CircuitParser拆分成四个独立的类,每个类负责一种解析逻辑。原来的CircuitParser类的parser方法变成一个调度类,负责调用上面拆分出的四种方法。

数字电路模拟程序-2

1.设计思路
本题的设计思路整体上基于第一次数字电路模拟程序进行,但是由于本题增加了新的要求一个是包含多输入输出的组合电路元件如数据选择器另一个是元件引脚类型除输入、输出之外,增加控制引脚,如三态门。在第一次题目的基础上增加一些元器件,并且元器件的结构也有所变化,增加了除输入引脚和输出引脚外的控制引脚,所以储存数据的类也应该相应增加,增加TriStateGate,Decoder,Multiplexer,Demultiplexer,由于增加的元件的输出引脚的编号不再默认为0,而是不同的输出引脚编号根据元件具体的类型来确定,但是相较于第一次模拟程序的编号不连续的问题,本次模拟程序新增的四个元器件的编号都是连续的,因此还需要增加一个专门管理输出源的类,因为系统的输出源不带引脚,而本次新增加的输出源带引脚编号,所以我采取的设计模式是分开对待。

2.类图分析(解释和心得)
alt text
通过本次的类图分析可以看到,本次系统的定义了所有元器件的父类CircuitComponent,这样做的原因是在Circuit方便数据的存储以及在Simulator的过程中方便数据的初始化,传递和计算,在OutputResult中不同的类型的元器件能够正常排序和输出结果。对于本次新增的四种电路元器件,由于它们都有控制引脚,所以我可以抽象出一个共同的父类ControlledComponent,由于本次新增的输出引脚有多个,所以不再采取同第一个程序的Gate的设计方案一样使用一个固定的int属性来存输出引脚的信号,而是采取Map来进行存储(虽然用List也可以,因为这道题无论是控制引脚,输入引脚还是输出引脚编号都是连续的,但是如果用List就会导致要做编号的映射不然会有一定的存储空间空着,虽然这题的引脚数量应该不会太多),同时我需要对之前第一个系统中定义的Gate做一定的修改保证Gate和ControlledComponent类的行为一致。

3.代码复杂度分析
alt text
【method复杂度分析】:
经 MetricsReloaded 分析,本系统101个方法中,CogC建议 < 5,超过10表示方法过于复杂,而当前平均值平均值2.43,属于中等水平。v(G)建议 < 10,超过15表示需要重构,而当前平均值1.35-2.40,相对较低。iv(G)建议 < 5,超过10表示数据流过于复杂,而当前平均值1.83,相对较低。
但是复杂度较高的方法集中于Simulator.run()(CogC = 65,ev(G) = 3,iv(G) = 24),CircuitParser.parseEdge()(CogC = 57,ev(G) = 1,iv(G) = 17),OutPutResult.output()(CogC = 36,ev(G) = 3,iv(G) = 13),CircuitParser.parseGate()(CogC = 5,ev(G) = 3,iv(G) = 2),CircuitParser.parseControlledComponent()(CogC = 5,ev(G) = 3,iv(G) = 2)
(1)Simulator.run()方法职责过多,包含信号传递、计算逻辑、状态管理。
(2)CircuitParser.parseEdge方法,复杂的条件判断和重复逻辑。
(3)OutPutResult.output方法,复杂的输出格式处理逻辑
应该对以上有问题的方法进行合理的重构和设计模式的应用。

alt text
【class复杂度分析】:
1.系统整体复杂度分析:
当前代码库整体复杂度可控
平均OCavg = 2.15表示整体类方法的平均复杂度较低(建议阈值≤3),说明系统大部分类设4.60计简洁,符合单一职责原则。
平均OCmax = 4.60表示类内最复杂方法的平均复杂度可控,但存在个别类偏离整体趋势。
总WMC = 217表示系统整体复杂度规模适偏大。

2.需要重点关注的类:
(1)Simulator(OCavg=20.00,OCmax=20,WMC=20)
分析:类中方法的平均圈复杂度极高(20.00),说明类中所有方法逻辑极其复杂。最大圈复杂度等于平均圈复杂度(20),表明类中所有方法复杂度一致且极高。WMC=20,类整体复杂度较高,违反单一职责原则。

原因:run() 方法承担了信号传递、组件计算、状态管理等全部逻辑,方法内嵌套多层循环和复杂条件判断。类职责过于集中,未拆分信号传递、计算逻辑等独立功能。

优化方案:需要将Simulator重构为职责分离的类,如将信号传递逻辑拆为SignalDeliverySystem,将组件计算逻辑拆为ComponentCalculator。

(2)CircuitParser(OCavg=6.67,OCmax=16,WMC=40)
分析:平均圈复杂度(6.67)和最大圈复杂度(16)均远超平均值,说明类中方法逻辑复杂。WMC=40,类整体复杂度极高,类职责过重

原因:parser()、parseEdge()、parseGate() 等方法承担了电路解析的全部逻辑,包含多层条件判断和重复代码。类中方法职责不清晰,未按单一职责原则拆分解析功能。

优化方案:将CircuitParser拆为独立的解析器类,具体重构方法在数字电路模拟程序-1已经说明这里就不再赘述。

(3)OutPutResult(OCavg=4.75,OCmax=16,WMC=19)
分析:平均圈复杂度(4.75)较高,最大圈复杂度(16)严重超标。WMC=19,类整体复杂度较高,输出逻辑过于集中。

原因:output() 方法包含所有组件的输出逻辑,需处理Gate、ControlledComponent等不同类型,条件判断复杂。类未按组件类型拆分输出逻辑,违反开闭原则。

优化方案:引入策略模式处理不同组件输出,引入公共的接口interface OutputStrategy,将门输出逻辑变成GateOutputStrategy,继承自OutputStrategy,将控组件输出逻辑变成ControlledComponentOutputStrategy,也继承自OutputStrategy,根据输出组件的类型来决定输出的具体策略。

数字电路模拟程序-4

1.设计思路
本次的模拟程序设计应该还是基于数字模拟程序-1,因为本题没有提到数字电路模拟程序-2中的三态门,译码器,数据选择器和数据分配器,在数字电路模拟程序-1的基础上增加子电路和程序异常输入的检测。这里难处理的是子电路,但是受到老师发的组合模式的类图的启发,在这里采取组合模式来设计本次新增加的子电路,具体就是将子电路看成一个带有输入引脚和输出引脚的有其他的几种基础的门电路组成的电路元器件组成的电路元器件。在程序异常输入检测方面,我根据异常的先后顺序采取优先级模式,其中前四种异常无关上下关即不依赖其他行的连接信息,所以在这一行的连接信息解析完后就可以进行相应的判断,但是最后一个异常检测与上下文有关,需要所有引脚的连接状态都确定完毕之后才能开始确定,另外由于这题子电路和主电路的解析非常的相似,所以这里采取了状态机的方式来进行子电路和主电路之间解析的切换并且映入了状态枚举类State来表示和区分当前所处的解析状态。
2.类图分析(解释和心得)
alt text
通过本次的类图分析可以看到本次在数字电路模拟程序的增加了CircuitComponent的子类SubCicuit,以及在的用于异常校验的类ConnectionValidator,SignalConflictDetector以及PinDirection,其中第一个异常检测类用于校验前四种异常,第二个异常校验类用于单独校验最后一种异常,最后一个PinDirection为一个枚举类用于标定某个接口属于该电路元器件的输入输入针脚还是输出针脚(虽然有些系统输入源和没有针脚这一概念但是可以类似地理解)。

3.代码复杂度分析
alt text
【method复杂度分析】:
经 MetricsReloaded 分析,系统整体方法复杂度处于中等水平,但存在少数高复杂度方法,对代码可维护性构成潜在风险。CogC总值245,平均值2.43(总值245,平均值2.43),ev(G)总值136(平均1.35),v(G)总值242(平均2.40),(建议阈值<10)。iv(G)总值185,平均值1.83(建议阈值<5)。
(1)Simulator.run()在数字电路模拟系统-2中已分析过,这里不再赘述。
(2)CircuitParser.parseEdge()(CogC=57,v(G)=17,iv(G)=17)
CogC = 57:认知复杂度极高,远超平均值;v(G) = 17:圈复杂度高,存在大量条件判断(如parts[0].contains("-")、circuit.getComponentEdges().containsKey(name));iv(G) = 17:信息流复杂,涉及map、ComponentSource、DestPin等多个对象的交互。
(3)OutPutResult.output()在数字电路模拟程序-2中已分析过,这里不再赘述。
alt text
【class复杂度分析】:
1.系统整体复杂度分析:

2.需要重点关注的类:
系统整体复杂度处于中等水平,但部分类存在高复杂度风险。OCavg = 2.37(建议阈值<5);OCmax = 4.83(建议阈值<10);WMC = 12.04(建议阈值<20)大部分类的OCavg、OCmax、WMC均在合理范围内(如AndGate、Circuit、ComponentSource等),说明系统整体设计相对清晰。少数类(如Simulator、OutPutResult、CircuitParser)的复杂度指标远高于平均值,存在职责过重、逻辑复杂、重复代码等问题,需优先重构。
(1)ConnectionValidator(OCavg=4.33,OCmax=11,WMC=13)
分析:OCavg=4.33:略高于平均值(2.37),说明类中方法的平均逻辑复杂度较高;
OCmax=11:类中单个方法的最高圈复杂度为11,高于平均值(4.83);
WMC=13:类中所有方法的圈复杂度之和为13,说明类中方法数量较少但每个方法复杂度较高。
原因:职责过重:validate方法包含输入数量验证、输出顺序验证、信号冲突检测等多个逻辑,条件判断多;条件判断冗余:验证逻辑(如inputCount > 1、outputCount == 0)重复,增加复杂度;
错误处理集中:exitWithError方法集中处理错误,导致方法逻辑复杂。
优化策略:将不同的验证规则拆分成独立的方法:如validateInputCount、validateOutputCount、validateSequence等,使用策略模式:为每种验证规则定义策略(如InputCountValidator、OutputCountValidator、SequenceValidator),每个策略处理一种验证逻辑。

采坑心得

数字电路模拟程序-1

1.踩坑点1
alt text
原因:在解析系统输入的输入源的时候for循环的i起始值不当,在对Input分隔的时候没有"-"后续的e字符数组也就只有一个元素,而我使用了下标1来获取数组中的元素,导致数组下标越界。
修改措施:
alt text

2.踩坑点2
alt text
原因:正则表达式的解析写错,导致后面将字符转换成数字时出错。
修改措施:alt text

3.踩坑点3
alt text
原因:在解析每一行的驱动源的时候使用subString来截取子字符串但是end设置的不对,导致subString截取越界。
修改措施:alt text

4.踩坑点4
alt text
原因:在输入应该解析的字符串的时候没有把电路连接信息题目给的格式给去除,导致字符解析出现问题。
修改措施:alt text

5.踩坑点5
alt text
原因:在解析的时候条件判断有误,导致在解析电路的连接信息解析到了最后一个OUT行导致出错,在解析OUT行的时候未能正确截取字符串。
修改措施:alt text

6.踩坑点6
alt text
原因:未对不同类型的门元件采取不同的解析策略,因为,不同类型的门元件的格式是不一样的,按照同一固定格式进行解析会出错。
修改措施:alt text

7.踩坑点7
alt text
原因::在Gate的构造函数中,未对属性id和type初始化,因为后续的输出要经过排序,而排序的依据就是编号和元器件的类型,这就导致排序超时。
修改措施:alt text

8.踩坑点8
alt text
输出的结果和标准答案(A(2)1-0:1)不一样。
原因:计算逻辑处理错误,判断计算逻辑时未考虑清楚,导致所有元器件都无法正常计算。
修改措施:alt text

数字电路模拟程序-2

1.踩坑点1
alt text
原因:没有考虑到value可能为空的情况,因为第二次电路模拟系统可能没有任何新添加的第二类元器件。
修改措施:alt text

2.踩坑点2
alt text
原因:没有判断controlledComponent可能为null的情况。
修改措施:alt text

3.踩坑点3
alt text
标准答案输出为Z(1)1-3:1,而我的程序运行之后什么都没有输出。
原因:在解析控制元件并创建控制元件的时候,类的创建出错并且输出函数也有问题,输出格式与题目要求的格式不一致。
修改措施:alt text

4.踩坑点4
alt text
原因:在map里面根据编号获取对应的控制引脚时没有考虑到其为null的情况,也就是说没有考虑到想要获取的控制引脚对应编号的信号不存在。
修改措施:alt text

5.踩坑点5
alt text
标准输出为S1-2:0
M(2)1:2
原因:在电路模拟时,对元件的信号进行传递,没有考虑到两种不同的电路元件即门电路元件和控制元件的输出格式不一样,使用同一格式输出,导致信号无法正常传递
修改措施:alt text

数字电路模拟程序-4

1.踩坑点1
alt text
标准输出为S:C1-A(2)1-0:0
原因:提取电路元器件时字符串解析错误
修改措施:alt text

2.踩坑点2
alt text
标准输出为ERROR: [A(2)1-1] include none input
原因:判断输入引脚的数量和输出引脚的数量的关系时,判断逻辑错误。
修改措施:alt text

3.踩坑点3
alt text
标准输出为ERROR: [A(2)1-1 A(2)1-0] input and output sequence error
原因:第四种输出异常的判断逻辑错误,将输入标签和输出标签弄反。
修改措施:alt text

4.踩坑点4
alt text
标准输出为C1-A(2)1-0:0,而我的系统则表示输入异常
原因:在给不同的引脚贴标签时,系统输出源的标签错误,系统输入源本应该作为输出引脚,而我在写的时候却把它变成了输入引脚。
修改措施:alt text

改进建议

整个三次题目的编码虽然能够完成题目的要求,但是代码的质量并不高,可以看到我的三次数字电路模拟程序出现了大量重复的代码而且有些方法和类的复杂度太高,不便于理解,也不便于后续的维护和更新,所以我可能需要对在复杂度分析中提到有问题的方法和类进行重构,减低复杂度,使得代码的结构更加健康。

总结

所学:
1.面向对象编程和面向对象设计最基础,最核心的原则SOLID原则。
2.充分考虑系统的可复用性、可维护性与可扩展性。
3.基础的电路知识。
4.基础的图论知识,如邻接表和拓扑排序等
5.对组合设计模式的理解和使用。
6.复杂字符串的处理和正则表达式的使用。
7.状态机与语法分析。
8.异常处理与优先级逻辑。
9.测试点的等价类划分,边界值分析,独立性和原子性。
10.常见的设计模式,例如策略模式,组合模式,工厂模式和状态模式。
11.学会合理的拆分类。

针对本次题目我觉得我需要再进一步学习如何降低方法和类的复杂度,时刻关注metrics指标,遇到复杂度异常的方法和类要及时,不然如果后续代码需要在前面代码的基础上扩展时,问题会逐渐积攒,等到积攒的问题超过了系统的承载上限时,整个系统便会崩溃,难以运作。

posted @ 2026-06-23 11:10  CodeMaster孤独如我  阅读(3)  评论(0)    收藏  举报