4~6次作业集总结

一、前言
这三次作业集是数字电路模拟程序,难度都非常大,不仅题目的量大,类图也没有,如果开始类设计的不好的话,后面迭代想要拓展的话也会非常麻烦,所有这几次作业给出了后面迭代的内容。这个程序主要可以分成三个部分:解析输入、设计实体类、处理电子元件之间的信号传递。这三个部分都很不好做,解析输入最好用正则表达式,设计实体类要考虑原来的程序能不能兼容后面加的元件,处理电子元件之间的信号传递也是一个麻烦的问题。我开始的类设计的就很不好,导致后面迭代的时候每次都改了又改。

二、第四次作业集

  1. 题目要求:
    设计一个数字电路模拟程序,题目会给出对应的输入表示连接和输入信号,电子元件只有基础的逻辑门元件,输出所有元件的输出信号
  2. 设计与分析:

屏幕截图 2026-06-24 144705``

屏幕截图 2026-06-24 145235

屏幕截图 2026-06-24 145240

Pin、NotGate、AndGate、OrGate、XorGate、XnorGate 所有方法调用次数、圈复杂度全部为 0~3, 无红色高风险标记。 Main类是全项目唯一性能/复杂度瓶颈(红色标记集中区)。calAndSpreadSigns() 圈复杂度 23,全项目第一,是信号仿真主循环,嵌套两层集合遍历,每轮全量扫描所有连线、所有逻辑门,调用频次极高。createAllGates() 复杂度 21,负责遍历全部引脚、解析元件、if-else 分支创建门,大量字符串解析 + 循环遍历,开销巨大。parseInput(7):解析输入信号,字符串分割、数字转换高频执行;printSorted(6):门排序 + 打印,内嵌自定义比较器,每次输出都要类型判断、提取编号;getOutputPinValue(6):反复判断引脚类型、截取元件名,信号查询核心工具;parseConnection(5)、setInputPinValue(5):连线解析、引脚赋值高频调用,重复字符串截取。全部业务逻辑、工具方法、解析、仿真、打印全部堆在 Main 静态类,所有高复杂度方法集中于此,耦合严重;底层实体轻量化,顶层调度层臃肿低效,仿真循环无增量优化,全量遍历造成大量无效计算;字符串截取、正则、门类型判断分散在 Main 各个方法,重复执行同类逻辑,拉高整体复杂度。实体层分层完全符合面向对象设计:引脚、基础门抽象、具体门实现三层隔离,计算逻辑内聚在对应门类,后续扩展新门仅需新增子类,底层结构无需重构。

  1. 踩坑心得:
    超长 if-else 分支区分门类型,新增元件必须修改该方法,违反开闭原则;遍历全部引脚集合去重创建门,重复解析同一个门名称,集合遍历开销大;所有创建逻辑放在 Main 中,和输入解析、仿真逻辑耦合在一起,修改一处牵动全部。getInputPinCountByName每次调用都新建 Pattern,高频仿真循环反复创建正则对象,损耗性能;getComponentName、getPinNumber每次调用都重复执行 lastIndexOf、substring,无缓存。门类型排序数字硬编码(1/2/3/4/5),类型判断逻辑同时存在getTypeOrder和打印比较器两处,新增门需要同步改两处;排序逻辑内嵌打印方法,排序、输出耦合,无法单独复用门排序逻辑。
  2. 改进建议:
    架构分层重构(解决 Main 上帝类、高复杂度问题),InputParser:封装输入、连线解析,统一缓存所有正则;GateFactory:门工厂类,用注册模式替代 if-else 分支;SignalSimulator:抽离calAndSpreadSigns 仿真循环,统一管理信号分发;GatePrinter:单独封装门排序、控制台输出逻辑;将 calAndSpreadSigns 拆分为「信号分发」「门输出计算」两个子方法,一个方法只做一件事。缓存重复计算数据:LogicGate 缓存 typeOrder、元件编号,排序时不用重复正则提取数字、判断门类型;全局静态缓存所有正则 Pattern,避免重复创建。引脚分隔符、OUT 标识、门类型排序值、最大迭代轮数统一设为 static final 常量;抽离独立比较器 GateComparator,把类型排序、编号升序逻辑单独封装,解耦打印代码。LogicGate 预留扩展抽象方法,支持多输出、控制引脚,后续多路选择器、触发器无需修改现有门子类;GateFactory 采用注册映射模式,新增时序元件仅需注册,无需修改分支判断。

三、第五次作业集

  1. 题目要求:
    在第四次的基础上加了三台门、译码器、数据选择器、数据分配器等元件
  2. 设计与分析:

屏幕截图 2026-06-24 152637

屏幕截图 2026-06-24 153025

屏幕截图 2026-06-24 153032

屏幕截图 2026-06-24 153045

底层实体(Pin、Component 抽象、基础逻辑门 And/Or/Not/Xor/Xnor)所有方法圈复杂度 0~3,无红色标记,封装规范:Pin 统一管理引脚赋值状态,Component 顶层抽象统一所有元件接口,LogicGate 封装基础门公共逻辑,单一职责清晰,无性能与耦合缺陷。新增扩展元件(TriStateGate、Decoder、Multiplexer、Demultiplexer)内部方法复杂度集中在 4~6,属于多路选择/译码器的正常业务复杂度,无极端高风险;每个元件独立封装引脚存储、输出计算、打印逻辑,相比第一版仅基础门的设计,扩展分层完成。calculateAndSpreadSignals() 圈复杂度 23(全项目最高),仿真主循环嵌套两层全局遍历:先遍历全部元件计算输出,再遍历全部连线分发电平,每轮迭代全量扫描所有数据,无效循环极多,是程序性能瓶颈。ComponentFactory.createComponent() 圈复杂度 19,超长 if-else 分支匹配元件前缀(A/O/N/X/Y/S/M/Z/F),每新增一类元件必须追加分支,违反开闭原则,高频调用拉高复杂度。getTypeOrder(Component) 圈复杂度 10,9 层 if-else 区分元件排序优先级,新增元件需要同步修改此方法 + 工厂类两处代码,维护成本翻倍createAllComponents(10)、parseInput(7)、printSorted(5)、parseConnection(5)、getOutputPinValue(4)、setInputPinValue(3),均为高频字符串截取、正则、分支判断逻辑,重复执行同类解析逻辑。抽离ComponentFactory工厂类,将门创建逻辑从 Main 拆分,解决了第一版createAllGates(21)超高复杂度问题,职责拆分初见成效;统一顶层抽象Component,逻辑门、三态门、译码器、多路选择器全部实现同一套接口,新增元件无需修改基础门代码,扩展性大幅提升;将输出打印逻辑下沉到各元件printResult(),Main 仅做统一排序遍历,降低打印方法耦合度。上帝类问题未根治:输入解析、仿真调度、字符串工具、排序逻辑仍全部堆在 Main 静态类,所有热点方法集中于此;仿真循环无增量优化,全量遍历机制和第一版完全一致,高复杂度根源未解决;工厂、类型排序依赖超长 if-else 分支,新增元件需要修改多处代码,扩展不友好;字符串解析工具无缓存,正则、截取逻辑重复创建执行,持续拉高工具方法调用开销。

  1. 踩坑心得:
    if-else 分支硬编码元件前缀,新增元件必须修改工厂方法,违反开闭原则;元件标识与构造逻辑耦合在分支内,无法动态注册元件,后续迭代子电路、时序元件时维护成本极高。元件排序优先级数字硬编码,与工厂类的元件判断分支完全独立;新增元件时,必须同步修改工厂、getTypeOrder两处代码,极易漏改产生 bug;类型判断逻辑重复,排序时反复执行instanceof判断,无缓存元件类型序号。正则还是没有全局静态缓存。虽然抽离了 ComponentFactory,但工厂仍依赖 Main 的静态工具方法getPinCountByName,双向耦合;所有字符串工具、输入解析、仿真调度全部静态写在 Main,无法单独单元测试。
  2. 改进建议:
    改进和上一次基本一样,虽然加了工厂类,但是这个工厂类还要改进,采用注册映射模式替代 if-else 分支,解耦 Main 静态工具依赖。在 ComponentFactory 中维护Map<String, Supplier>,元件前缀与构造器映射,新增元件仅需注册一行,无需修改分支。新建枚举ComponentType,统一存储元件前缀、排序优先级、引脚解析规则,工厂、排序器共用同一套枚举,一处修改全局生效,消除两处同步修改的 bug。

四、第六次作业集

  1. 题目要求:
    在第一次的基础上加入异常输入检测
  2. 设计与分析:

屏幕截图 2026-06-24 175622

屏幕截图 2026-06-24 175720

屏幕截图 2026-06-24 175725

子电路模块 SubCircuit,内部方法复杂度集中在 5~12,属于子电路内部仿真、端口映射的正常业务复杂度,无极端风险。innerPropagate()(12):子电路内部独立信号传播循环,实现内外电路仿真隔离,是本版本核心优化点;子电路独立维护内部门、端口信号、连线列表,完成题目 “子电路” 扩展需求。checkAllConnError() 圈复杂度 23(全项目最高)承担全部输入异常校验,多层嵌套 if 分支:校验连线输出源数量、引脚顺序、输入引脚多驱动冲突、非法端口格式,大量条件判断嵌套,所有异常校验逻辑全部堆在 Main 单一方法,可读性与维护性极差。propagateAllSignals() 圈复杂度 21(仿真性能瓶颈)全局信号仿真主循环,三层嵌套遍历:顶层连线分发→所有子电路内部传播→所有门输出计算,每轮迭代全量扫描全部顶层、子电路连线,存在大量无效遍历。printAllValidGateOutput(14):遍历顶层 + 所有子电路门,内嵌门类型排序、编号提取逻辑;getSignalValue(14):跨全局输入、顶层门、子电路端口三层引脚取值,多层正则匹配分支;splitSubCirAndMain(11):循环切割文本、拆分主电路与子电路代码块;setPinSignal(9)、buildSingleGate(9):高频引脚赋值、门实例创建工具方法。核心缺点上帝类还是未根治。

  1. 踩坑心得:
    校验逻辑全部内联在单一方法,多层 if 嵌套,拆分粒度极差,新增任何约束规则都会进一步拉高圈复杂度;校验过程重复调用isValidOutputSource,重复执行正则匹配引脚,产生大量重复计算;双向静态耦合;SubCircuit 依赖 Main 静态取值方法,Main 持有全部子电路集合,两者无法独立拆分测试;门类型排序getGateOrder使用长 if-else,新增元件必须同步修改排序方法 + 门创建方法两处代码,极易漏改产生 bug;打印、排序逻辑耦合在printAllValidGateOutput,收集门、排序、输出三职责混杂,无法单独复用排序逻辑。

  2. 改进建议:
    抽离splitSubCirAndMain,负责切割主电路、子电路文本块;剩下的基本还是之前说的那些,这里不赘述了。

五、总结
最开始只需要实现与/或/非/异或/同或五种基础门,优先保证输入输出样例能跑通,只完成了底层Pin、LogicGate的分层抽象,这部分设计是全程最稳定、复用性最高的代码。但为了省事,所有输入解析、信号仿真、门创建、打印排序逻辑全部堆在Main静态类,形成 “上帝类”;仿真直接全量遍历所有连线,没有任何性能优化;区分门类型、元件排序全靠零散if-else,完全没有考虑后续扩展,写完代码后复杂度报表里Main多个方法标红,当时只觉得功能没问题就行,没意识到后期扩展会难以维护。
版本要求新增译码器、多路选择器、三态门等多引脚、带控制端的元件,我意识到不能再把门创建逻辑写死在Main里,于是抽离了ComponentFactory工厂类,统一顶层Component抽象接口,让所有元件共用一套引脚、计算、打印规范,扩展性得到提升。但前期的历史遗留问题没有彻底解决:仿真循环依旧全量遍历、Main还是包揽所有业务、元件排序判断和工厂分支两处重复写instanceof判断,新增元件需要同步修改两处代码,修改时出现过漏改排序逻辑导致输出顺序错乱的 bug,这也是复杂度报表里getTypeOrder、工厂方法圈复杂度居高不下的核心原因。
最后一次迭代新增两大难点:子电路嵌套、全输入异常校验。我独立封装了SubCircuit类隔离子电路内部连线、端口、门,实现内外电路仿真隔离,同时新增完整的连线错误检测逻辑。但也彻底暴露了前期偷懒带来的缺陷:所有异常校验逻辑挤在同一个checkAllConnError方法,多层 if 嵌套导致圈复杂度达到 23;子电路为了取值直接调用Main静态工具方法,形成双向耦合,没法单独调试子电路;仿真全量遍历的性能瓶颈依旧存在,报表中propagateAllSignals21 的高复杂度就是长期遗留问题。
对比前三版复杂度平均值逐步上升(3.05→2.87→4.32),也直观体现出:前期架构不规范,后续迭代新增需求只会不断堆高代码复杂度。本次分三阶段迭代开发数字电路模拟器,完整走完了 “基础功能实现→扩展元件开发→复杂子电路 + 异常校验” 的全过程,通过圈复杂度报表直观看到不良架构带来的维护、性能缺陷,也亲手落地了分层、工厂模式、增量仿真、解耦模块等优化方案。不再只是纸上谈兵学习面向对象和软件设计规范,而是通过真实迭代项目,理解了规范设计对项目长期维护、扩展的重要性。

posted @ 2026-06-24 20:53  llq-007  阅读(2)  评论(0)    收藏  举报