大一下后三次题目集阶段性总结
前言
oop课程的第二阶段结束了,第二阶段,围绕集合框架的应用,接口隔离原则的实现,以实现程序的优化设计为目的,就数字电路模拟程序为例,进行了三次迭代式作业,现就这三次作业进行总结。
第一次作业
第一次作业需求说明:请编程实现数字电路模拟程序,数字电路是一种处理离散信号的电子电路。与处理连续变化信号(如声音、温度)的模拟电路不同,数字电路只识别和运算两种基本状态:高电平(通常表示为“1”) 和 低电平(通常表示为“0”)。这正好与二进制数制系统相对应,使得数字电路成为所有计算机和数字系统的物理实现基础。
实现方式:
1.解析输入:Recognize.handleInput() 从 INPUT: 行读取外部输入信号(如 A-1),存入 Map<String,Integer>。
2.解析连接:handlePin() 将中括号内的连接字符串(如 [A A(1)1-0])拆分为字符串数组,表示信号源(外部输入或元件输出)和多个目标引脚。
3.动态构建元件:findComp() 根据元件名(如 A(2)1)查找或创建对应的 Comp 子类实例(CompA, CompO, CompN, CompX, CompY),并存入列表。
4.信号传播:connect() 方法将源信号值写入各目标元件的输入引脚,然后循环遍历所有元件,对输入齐全的元件调用 transfer() 计算输出。若输出发生变化,则继续循环,直到所有信号稳定(类似电平迭代)。
5.收集与排序:筛选出所有输入引脚都已赋值的元件,使用 TypeCompSorter 按类型优先级(A < O < N < X < Y)和数字编号排序。
6.输出:按排序顺序打印每个元件的 类型编号-0:输出值。
代码规模分析:
第一次代码规模如下图:

第一次作业的复杂度分析:

总结:
优点方面:
代码具有良好的面向对象设计基础。采用抽象类 Comp 定义统一接口,利用多态性让 CompA、CompX 等子类各自实现门电路逻辑,扩展新门型较为方便。同时运用了工厂模式(CreateComp),将字符串解析与对象创建解耦,符合单一职责原则。排序部分通过 ComperSorter 接口定义了策略模式,为后续扩展不同排序规则预留了空间。在信号传播机制上,采用 do-while 循环进行迭代计算,试图模拟组合逻辑的“固定点”收敛过程,这种思路比简单的单向遍历更严谨。
不足与缺陷(尤为严重):
最核心的缺陷在于 “输入引脚一次性锁定”。在 connect() 方法中,写入引脚时使用了 if (!comp.getInPin().containsKey(pinNum)) 的判断,这意味着每个引脚的值仅在第一次被赋值时写入,后续即使信号发生变化也无法更新。在真实的组合逻辑中,信号值完全可能因传播顺序不同而经历从 0 到 1 的变化,锁定机制将直接导致仿真结果错误,完全违背了组合逻辑的物理特性。同时,connect 中源信号 srcValue 若为 null 便直接 return,这暗示系统无法处理“信号未就绪”的暂存状态,配合锁定机制,极易导致元件因读取到过时或空值而输出错误。
第二个严重缺陷在于解析逻辑的脆弱性。 CreateComp.extractComp() 对“与门(A)”和“或门(O)”的处理存在硬伤:当元件名不带括号(如 A1)时,默认将输入引脚数设为 0。然而 A 和 O 作为可变输入门,理论上必须通过括号指定输入数量(如 A(2)1)。该逻辑缺陷会导致创建出的引脚容量为 0 的无效元件,其 transfer 方法在遍历空集合时会将输出置为 1,产生非预期的逻辑错误。此外,handleComp 方法定义后从未被调用,造成代码冗余。
在传播顺序与效率上,尽管迭代收敛的思路可行,但由于没有基于元件依赖关系进行拓扑排序,每次 connect 都需对所有元件进行全量扫描直到稳定。当电路规模增大时,时间复杂度会急剧上升,且在没有输入更新锁定的前提下,这种全量扫描会引发大量无效重复计算。最后,代码对异常情况(如格式错误、缺失信号)零容忍,缺乏健壮的错误处理机制,一旦输入不符合预期将直接抛出运行时异常。
第二次作业
第二次作业需求说明:
在第一次电路模拟程序的基础上,添加包含多输入输出的组合电路元件如数据选择器元件引脚类型除输入、输出之外,还要增加控制引脚,如三态门。
实现方式:
- 输入解析与元件预创建
主函数首先读取INPUT:行,通过Utils.parseInputLine()将外部信号(如A-1)存入Map<String, Integer>。
然后循环读取所有连接行(以end结束),同时用正则表达式AONXYSMZF?\d+提取行中出现的所有元件名,存入Set集合去重。
遍历元件名集合,调用CreateComp.createComp()工厂方法,根据类型字符和括号中的参数创建对应的Comp子类实例(如CompA、CompM),并将所有实例存入列表及名称到对象的映射表。
- 连接解析与信号源建模
对每条连接行,去除方括号后按空格拆分成字符串数组,第一个元素为信号源,其余为目标引脚。
定义Source接口,实现类ExternalSource(代表外部输入)和CompOutputSource(代表某个元件的某引脚输出)。
根据源字符串格式(如A或A(2)1-0)创建对应的Source对象,然后遍历所有目标引脚字符串,将(目标元件, 引脚号) -> Source的映射存入inputSourcesMap(每个元件维护一个引脚到源的映射)。
- 迭代信号传播
采用while(changed)循环进行固定点迭代。每次遍历所有元件:
对该元件的每个输入引脚,从inputSourcesMap中获取对应的Source,调用getValue()(传入外部输入映射和元件映射)获取当前值。
若任一引脚源缺失或值为null/-1,则该元件本轮不计算。
若所有输入就绪,则将这些值放入pinValues,调用compute()更新输出引脚值。
比较新旧输出值,若有变化则置changed=true,继续下一轮迭代,直至所有输出稳定。
- 元件有效性判断与输出
每个元件重写isValid()方法,依据自身特性判断:基本门要求所有输入有效且输出不为-1;三态门在控制为0时输出高阻(-1),视为无效;译码器在使能无效时输出全-1,亦无效;数据选择器和分配器要求所有输入有效。
迭代结束后,筛选isValid()为true的元件,按类型优先级(A<O<N<X<Y<S<M<Z<F)和编号升序排序。
调用各元件的getOutputDisplay()按题目指定格式输出(基本门输出类型编号-0:值,译码器输出类型编号:选中索引,分配器输出一串二进制值等)。
- 类结构与扩展性
抽象类Comp封装了引脚集合(inputPins/outputPins)、引脚值映射pinValues,并提供compute()和getOutputDisplay()抽象方法,子类实现各自逻辑。
工厂类CreateComp根据类型字符和括号参数动态实例化不同元件,方便扩展新器件。
工具类Utils负责输入解析、元件名提取、排序顺序定义和编号提取,分离了通用辅助功能
代码规模分析:
第二次代码规模如下图:

第二次作业代码的复杂度分析如下图:

总结:
两次迭代过程,代码的核心差异在于信号传播机制与设计架构。
第一段代码采用“动态创建+锁定引脚”方式,致命缺陷是引脚值一旦写入便不可更新,导致依赖传播顺序的信号被固化,不够灵活,结果极大概率错误。其传播循环虽尝试迭代,但因锁定机制而失效。第二段代码彻底重构了仿真引擎:预先解析所有元件,建立引脚到信号源(Source接口)的映射,每次迭代均从源实时获取最新值,再重新计算所有输入就绪的元件,直至输出稳定。这正确模拟了组合逻辑的“固定点”收敛,从根本上解决了信号更新问题。
架构上,第二段具有明显优势:
引入 Source 接口抽象外部输入与元件输出,解耦信号获取逻辑,便于扩展。
明确划分 inputPins 与 outputPins,引脚编号统一为整数,避免了第一段中字符串键带来的混乱。
支持更多器件(三态门、译码器、选择器、分配器),并通过 isValid() 处理高阻态和使能无效情况,功能完备性大幅提升。
工厂类 CreateComp 根据括号参数创建元件,扩展性更强,体现了多态性。
不足方面:
第二段代码因支持多器件而较为冗长,部分逻辑(如 isValid() 在各子类中重复)可进一步抽象;迭代算法虽正确,但未利用拓扑排序优化,全量扫描在大规模电路中效率偏低。第一段代码虽简洁,但以正确性为代价,不可取。而且代码存在健壮性的问题,不足以支持较为复杂情况下的电路模拟。
总结: 第二段代码在正确性、可扩展性和功能丰富度上全面超越第一段,是合格的电路仿真实现。其核心改进——实时从源获取值而非锁定引脚——是解决组合逻辑仿真问题的关键。但是效率低下的问题仍然无法解决。
第三次作业:
第三次作业需求说明:
在前两次的基础上,增加子电路,增加程序异常输入的检测。
实现方式:
- 两级电路结构
MainCircuit 代表顶层主电路,包含外部输入、子电路实例和门级元件。
SonCircuit 代表子电路,拥有独立的输入端口(INPUT:)、输出端口(OUT:)和内部连接。子电路通过 C数字-变量名 的语法在主电路中被引用,作为信号源或目标。
- 元件模型与工厂
抽象类 Comp 封装类型编号、输入引脚数、引脚值映射 pins,并提供 transfer() 抽象方法由子类实现(与门、或门、非门、异或门、同或门)。
CreateComp 工厂类解析元件名称(如 A(2)1),创建对应实例,并支持按 (类型, 编号, 子电路ID) 查找或创建。
- 解析与异常检测
逐行扫描输入:识别 C数字: 切换子电路上下文,endc 结束;INPUT:/OUT: 记录端口;方括号行解析为连接数组存入 messages。
checkSingleConnection 对每条连接进行格式校验:必须有且仅有一个信号源(source)、至少一个目标(sink)、源须在首位、且每个目标引脚只能被赋值一次(防止冲突),若违规则输出错误信息并终止。
- 迭代信号传播
主电路和子电路各自维护连接列表和元件列表。计算采用 do-while 固定点迭代:
遍历所有连接,根据源类型(外部输入、子电路输出、元件输出)获取当前值,写入目标(元件引脚或子电路输入变量)。
然后对所有输入齐全的元件调用 transfer() 更新输出。
重复直到输出稳定(或达到最大迭代次数)。
子电路的 inputParser() 负责将外部传入的输入变量值传播到内部元件,并计算输出变量值。
主电路先传播一次,再依次计算所有子电路,最后再传播一次主电路,以处理子电路输出到主电路的反馈,确保层次化信号的正确收敛。
- 输出与排序
收集主电路和所有子电路中的元件,筛选输入完整且有输出的有效元件。
按类型顺序(AONXY)、编号数字、子电路ID排序,输出格式为 [C子ID-]类型(引脚数)编号-0:值(非门/异或/同或无括号)。
整体上,该实现通过清晰的层次划分、严格的异常检查和迭代传播机制,较为完整地模拟了多级组合逻辑电路的行为,并具备较好的可读性和扩展性。
第三次代码规模分析:
第三次代码规模分析如下图:

第三次代码复杂度分析如下图:

总结:
这段代码实现了支持层次化电路的组合逻辑仿真器,核心亮点包括:引入 MainCircuit 与 SonCircuit 两级结构,支持子电路定义与实例化;添加严格的连接合法性检查(单源、多目标、引脚不冲突、源在首位);采用固定点迭代传播信号,并通过两次主电路传播(子电路计算前后)处理层次化反馈。
优点:
层次化设计使代码更贴近真实电路设计,可复用子电路,扩展性强。
异常检测机制完善,能提前捕获格式错误和信号冲突,提升了鲁棒性。
元件查找与创建逻辑清晰,支持子电路内元件与主电路元件共存,subId 区分不同实例。
引脚值更新时判断是否变化(old != now),减少不必要的迭代,效率优于前两段。
不足:(该段为AI生成总结)
代码较为冗长,部分逻辑重复(如主电路与子电路的传播流程),可进一步抽象共用方法。
迭代算法未利用拓扑排序,全量扫描在复杂电路中效率偏低。
子电路 inputParser 与主电路 processMainCircuit 存在功能重叠,且最大迭代次数硬编码为 10000,缺乏动态收敛检测。
异常检测中 determinePinType 逻辑复杂,可维护性较差;输出排序忽略了 subId 的实际意义(不同实例应独立输出,但排序按 subId 可能影响顺序正确性)。
总体而言,该实现设计思想领先,尤其在层次化和异常处理方面表现出色,但代码结构与性能优化仍有提升空间。若进一步抽象传播引擎并引入拓扑排序,将更为完善。
三次迭代式总结:
第一次实现基本门电路(与或非异或同或),采用动态创建元件和迭代传播,但输入引脚一旦赋值即被锁定,无法响应后续信号变化,导致仿真结果在复杂连接顺序下必然错误,是致命缺陷。
第二次重构信号传播机制,引入 Source 接口抽象信号源,每次迭代实时从外部输入或元件输出获取最新值,彻底解决了锁定问题。同时扩展支持三态门、译码器、数据选择器和分配器,并通过 isValid() 处理高阻态,功能完备性显著提升,正确性得到保障。
第三次进一步引入层次化设计,区分主电路(MainCircuit)和子电路(SonCircuit),支持子电路定义与实例化,并添加连接合法性检查(单源、无冲突等),增强了鲁棒性。但代码冗长,部分逻辑重复,且仍依赖全量扫描迭代,在大规模电路下效率欠佳。
总体而言,三次代码逐步解决了正确性、功能覆盖和层次化问题,设计思想日趋成熟,但在性能优化和代码简洁性上仍有改进空间。

浙公网安备 33010602011771号