第二次blog作业
作业集4
- 核心知识点覆盖
面向对象设计:封装、实体类设计、Comparable 接口自定义排序、构造器重载;
正则表达式:多行文本解析、分组捕获,区分两类逻辑门命名规则、引脚、输入信号、连接语句;
集合容器:HashMap 存储全局信号、门元件映射,ArrayList 分类存储各类门电路;
离散数字逻辑:与 / 或 / 非 / 异或 / 同或五种基础门电路运算规则;
文本流处理:Scanner 逐行读取输入、边界判断(end 终止符、空行过滤);
程序分层思想:输入解析层、元件实体层、逻辑计算层、分类输出层;
约束规则处理:单输入引脚仅绑定一路信号、输入不全元件舍弃输出、固定输出引脚 0、同类门按编号升序输出。 - 题量与难度分析
本次作业单题体量较大,并非简单算法题,而是小型完整模拟系统开发:
输入格式分支多:全局 INPUT 输入、[] 连接语句、两类门命名、引脚格式 4 套正则匹配;
数据流转链路长:原始文本→信号映射→自动实例化门对象→填充引脚电平→批量逻辑计算→分类排序输出;
边界测试用例复杂:多门级联、单信号驱动多引脚、引脚输入缺失、多输入与 / 或门、跨类型门嵌套;
硬性资源限制:代码 50KB、运行 800ms、内存 64MB,要求代码轻量化、无冗余运算。
整体难点集中三处:① 复杂自定义格式的正则拆分;② 动态自动创建门对象的解耦设计;③ 信号全局统一存储、逐级传递无冲突。单纯语法基础无法完成,需要完整软件分层思维。
二、设计与分析
(一)PowerDesigner 类图结构说明
本次代码核心分为两大核心类:Gate实体类、Main程序入口类,无额外工具类,低耦合极简设计。
Gate 实体类(核心模型层)
| 属性 | 作用 |
|------|------|
| char type | 门类型标识 A/O/N/X/Y |
| int num | 门编号,用于同类排序 |
| int inputNum | 输入引脚总数,区分多输入与 / 或门 |
| Map<Integer,Integer> inputs | 存储引脚号 - 电平键值对,判断输入完整性 |
| int output | 缓存门电路计算结果 0/1 |
核心方法:
双构造器重载:区分 A/O 多输入门、N/X/Y 固定引脚门;
setInput ():外部填充引脚电平;
isInputComplete ():校验全部引脚是否接入有效信号;
calcOutput ():根据门类型执行数字逻辑运算,生成输出电平;
compareTo ():实现 Comparable 接口,同类门按编号升序排序;
getName ():标准化生成输出元件名称(A (4) 1、X1 格式)。
Main 入口类(控制层 + 解析层)
全局容器:signalMap全局信号池(存储所有输入引脚、门输出引脚电平)、gateMap全量门实例池;
5 组预编译 Pattern 正则,分别匹配输入语句、连接语句、两类门、引脚格式;
核心流程函数:createGate (),根据元件名字符串自动匹配正则并实例化 Gate 对象;
主流程:逐行读取→分支解析 INPUT / 连接语句→自动创建门并填充引脚→批量计算输出→分类排序打印。
类图逻辑说明
![image]()
依赖关系:Main 依赖 Gate 实体完成数据存储与逻辑计算,Gate 独立无外部依赖,符合单一职责原则,Gate 仅负责存储数据与计算逻辑,Main 只负责文本解析与流程调度。
(二)SourceMonitor 代码报表数据分析
代码总行数统计:有效代码约 180 行,注释、空行占比极低,满足 50KB 代码长度限制;
方法数量:Main 包含 main、createGate 2 个方法;Gate 包含构造器、5 个成员方法,总计 7 个业务方法;
圈复杂度统计:
calcOutput () 圈复杂度最高(switch + 多层循环判断),数值 7,逻辑分支多但清晰,无嵌套地狱;
主解析循环分支(INPUT、连接语句判断)圈复杂度 5,通过正则匹配拆分降低 if 嵌套层级;
代码复用度:正则预编译复用、Gate 封装逻辑复用,无重复字符串拆分、逻辑运算代码;
缺陷指标:无硬编码魔法数字、重复字面量统一由 Gate 内部封装,全局仅一处输出打印循环。
(三)源码分层解读与设计心得
数据分层设计心得
将所有电平信号统一存入 signalMap 是本次核心设计亮点。无论顶层外部输入 A/B/C,还是某门输出引脚(如 A (2) 1-0),全部作为唯一 key 存入 Map,连接语句只需读取源信号值,直接赋值给目标引脚,天然实现信号多级级联传递,无需递归迭代计算电路链路。
对比备选方案:若单独存储门输出、外部输入两套容器,级联电路需要反复遍历更新,代码复杂度翻倍,当前设计以全局信号池统一管理,线性遍历即可完成全部赋值。
动态实例化门对象设计心得
采用懒加载创建逻辑:只有当连接语句出现该门引脚时,才调用 createGate 解析名称、创建 Gate 存入 gateMap。不需要提前预判电路存在哪些门,完全适配任意输入电路拓扑,扩展性强,完美适配后续迭代的多类型元件扩展。
两种构造器重载分离多输入门与固定引脚门,避免 if 判断充斥构造方法,代码可读性更高。
排序与输出分层心得
不统一存储所有门再过滤,而是分 5 个 List 分类存放,严格遵循题目输出顺序:与门→或门→非门→异或门→同或门;每个列表调用 Collections.sort 利用重写 compareTo 实现编号升序,输出循环极简,不需要额外排序判断分支。
三、采坑心得(数据 + 结构 + 测试用例佐证)
坑 1:正则匹配边界漏洞,引脚 / 门名称捕获分组错误
问题现象(测试用例 5 复现)
输入:[A A(2)1-1 O(2)1-1 X1-1]
初次正则 pinPattern 分组错误,将A(2)1-1完整识别为引脚号,元件名捕获截断,无法区分括号数字。
原始错误正则:(.+)-(\d+)中第一组贪婪匹配无限制,在带括号门名称场景下匹配失效。
解决与数据佐证
修正后正则分组不变,但调整匹配顺序:先完整匹配门名称正则,再解析引脚;增加空字符串 trim 过滤,消除空格导致匹配失败。
测试数据:10 条带括号与不带括号引脚混合用例,修正前 4 条匹配失败,修正后全部捕获正确。
坑 2:输入引脚重复赋值、缺少唯一性校验(逻辑约束违反)
题目约束:一个输入引脚不能接收多个输出信号。
问题现象(测试用例 6 复现)
两条连接语句同时指向X2-1,两次调用 setInput 覆盖数值,无报错,但逻辑不符合硬件规则。
当前代码现状:仅覆盖值,未做冲突检测。
底层结构佐证:Gate.inputs 使用 HashMap,同一 pin 号 put 会直接覆盖旧 value,无异常提示。
坑 3:输入完整性判断逻辑遗漏,未过滤引脚缺失元件
初期版本忘记调用isInputComplete(),直接输出引脚不全的 X2(测试用例 6),与样例输出不符。
数据佐证
X2 仅接入引脚 1,inputNum=2,inputs.size ()=1,判断条件inputs.size() == inputNum返回 false,过滤后不再加入输出列表,与题目要求完全匹配。
坑 4:全局信号读取顺序错误,级联电路计算时序出错
初始设计为先遍历所有门再解析连接语句,门输出引脚信号未存入 signalMap,后级门读取不到电平,输出全部为 0。
流程图佐证
错误流程:读取所有行→创建门→计算输出;
正确流程:逐行解析→实时存入 signalMap→全部解析完成后统一 calcOutput;
原因:连接语句读取依赖 signalMap 实时更新,必须边解析边存信号,解析完成后所有信号池数据完整,批量计算无缺失。
坑 5:门名称格式化输出 bug,非 A/O 门错误打印括号
初次 getName () 方法未区分门类型,X1 输出为 X (2) 1,与样例输出格式不一致。
修复逻辑判断:type 为 A/O 拼接 (输入数),N/X/Y 直接拼接编号,对照 6 组样例输出全部对齐。
坑 6:空行、多余空格、end 后冗余文本未过滤
输入中存在空白换行、连接语句首尾空格,未 trim 会导致正则匹配匹配失败;end 之后内容继续解析,产生多余门对象。
修复方案:每行读取后先 trim,空行直接 continue;读到 end 直接跳出循环,终止输入解析。
四、改进建议(可持续迭代,适配后续 4 版迭代需求) - 代码结构分层重构(适配数字电路模拟程序 2)
当前代码所有解析逻辑、正则硬编码在 Main 中,耦合度高,新增多路选择器、三态控制引脚时修改成本大。
改进方案:
抽取独立ParseUtil工具类:封装所有正则、文本拆分、门名称解析静态方法;
新增CircuitContext上下文类,统一管理 signalMap、gateMap,封装信号读写、门查找接口;
Gate 抽象化为父类Component,子类:AndGate、OrGate、NotGate、XorGate、XnorGate,重写 calcOutput,新增 Mux、TriGate 子类适配第二版需求。
优势:新增元件仅新增子类,不改动主流程,符合开闭原则。 - 增加输入合法性异常检测(适配程序 4 异常检测需求)
现有代码无校验逻辑,可补充校验规则并抛出提示:
输入引脚多源驱动检测:调用 setInput 前判断 inputs.containsKey (pin),存在则记录冲突日志;
非法元件名、非法引脚号、引脚号超出 [1,inputNum] 范围校验;
输出引脚短接检测:遍历所有连接语句源引脚,重复源引脚抛出异常;
统一自定义异常CircuitFormatException,区分格式错误、电路逻辑错误。 - 时序 / 反馈电路扩展改造(适配程序 3 时序电路)
当前为纯组合电路,无状态存储,无法实现 D/JK 触发器:
在 Component 基类增加 state 状态变量,存储时序元件当前电平;
拆分计算逻辑:区分组合逻辑 calcCombinatorial ()、时序逻辑 calcSequential ();
新增电路迭代计算方法,循环更新时序元件状态,支持反馈环路。 - 性能与内存优化(贴合题目 64MB、800ms 限制)
将全局 signalMap 替换为轻量 HashMap,减少自动装箱损耗;高频读取电平封装 getSignal 方法,避免重复哈希查找;
预缓存所有门 getName () 结果,循环输出无需重复字符串拼接;
解析阶段即时丢弃无效空引脚数据,减少容器内存占用。 - 子电路封装设计(适配程序 4 子电路需求)
新增 SubCircuit 类,内部嵌套独立元件与信号池,对外暴露顶层输入输出引脚,支持子电路复用,主电路可引用子电路元件名,实现模块化电路搭建。
五、综合总结 - 本阶段学到的核心能力
小型系统面向对象建模:从硬件实体抽象 Java 类,将数字硬件门电路转化为程序实体,理解现实业务到代码模型的映射方法;
复杂自定义文本解析:熟练掌握正则分组捕获、多行异构文本分支处理,学会用容器统一管理全局状态数据;
分层解耦思维:区分数据模型层、解析控制层、输出展示层,避免全部逻辑堆砌在主函数;
边界与需求严谨性:通过多组测试用例验证每一条题目约束(引脚唯一性、输入不全过滤、输出排序规则),养成对照样例调试的习惯;
软件迭代设计思维:在基础版本预留扩展接口,提前思考后续三态门、时序电路、子电路的兼容改造方案。 - 待深入学习与研究方向
正则高级用法:贪婪 / 非贪婪匹配、零宽断言,简化多分支引脚匹配代码,减少多组 Pattern 冗余;
设计模式落地:工厂模式创建各类门元件,替代当前 createGate 零散判断;策略模式封装不同门计算逻辑,消除 calcOutput 中大段 switch;
图结构电路拓扑:当前仅线性信号传递,带反馈时序电路需要用邻接表存储电路有向图,研究图遍历算法迭代计算电路稳态;
输入校验框架:标准化参数校验、异常统一处理,学习工程化输入容错设计;
资源性能调优:深入 Java 集合底层内存占用、字符串拼接性能优化,适配题目严格内存与时间限制。
作业集5
- 覆盖核心知识点
面向对象高级特性:抽象基类Component、子类多态、方法重写、子类拆分单一元件逻辑;
正则与元件名称分类匹配:区分 9 类元件命名规则(带控制参数 M/Z/F、基础门 A/O/N/X/Y/S);
全局信号拓扑传播:有向连接一对多映射、多轮迭代信号刷新直到电路稳态,解决多级级联信号滞后问题;
多类型引脚体系:统一区分控制引脚、数据输入引脚、多路输出引脚,标准化引脚编号分配规则;
九类元件数字逻辑建模:基础门 + 三态开关 + 译码器 + 数据选择器 MUX + 数据分配器 DEMUX 硬件逻辑转代码;
差异化输出规则处理:普通门输出单引脚电平、译码器输出唯一低电平编号、分配器拼接0/1/-字符串;
非法 / 无效信号统一管理:常量INVALID=-1标记高阻、引脚缺失、控制不满足等失效场景,直接忽略输出;
容器分层管理:电路输入缓存、元件实例池、连接关系映射、全局引脚值缓存四大 Map 解耦数据。 - 题量与难度分析
本题为单一完整仿真系统开发,相比版本 1 复杂度大幅提升,难点集中在四点:
元件种类翻倍,引脚规则不统一:版本 1 仅单输入输出门,版本 2 新增控制引脚、多路输入 / 多路输出元件,每类元件引脚起始位置、数量、功能完全不同;
输出规则不通用:九类元件三种输出格式,不能复用同一打印逻辑,必须依靠多态重写printOutput();
信号传递存在依赖链:一级门输出作为下级控制 / 数据输入,单次遍历无法更新全部信号,需要循环迭代至无信号变更;
边界判定条件激增:三态门控制电平失效、译码器控制 S1/S2/S3 校验、任意引脚缺失直接整元件失效、多输出引脚区分有效 / 无效-;
硬性资源约束不变:代码≤50KB、运行时限 800ms、内存 64MB、栈 8192KB,要求继承复用、无冗余重复代码。
整体属于小型数字组合电路仿真引擎开发,不再是简单字符串解析,需要建立完整电路拓扑模型,软件分层、多态、状态迭代均为核心考核点。
二、设计与分析
(一)PowerDesigner 类图结构解析
整体采用抽象父类 + 9 个子类标准继承结构,完全遵循开闭原则,新增时序元件仅需新增子类,无需修改主流程。
顶层抽象类 Component(统一标准)
公共属性:
String name:元件完整名称(A (2) 1、S1、M (3) 1 等)
boolean computed:标记当前元件是否完成本轮计算,避免重复运算
公共通用方法(所有子类复用):
getPin(int pinNo):统一读取指定引脚电平,自动从全局pinValues取值,缺失返回INVALID=-1
setPin(int pinNo, int val):统一写入引脚电平到全局缓存
抽象方法(子类必须重写,实现多态):
computeIfNeeded():实现该元件专属数字逻辑,填充输出引脚
printOutput():按题目要求差异化打印输出
九个子类分层
基础二值逻辑门(复用版本 1 逻辑)
AndGate与门、OrGate或门、NotGate非门、XorGate异或门、XnorGate同或门
特征:仅输入引脚 1~n,固定单输出引脚 0,无控制引脚。
带控制单输出元件
TriStateGate三态门:0 号控制、1 号数据输入、2 号输出;控制为 0 直接高阻失效。
多路输出复合型元件(本次新增核心难点)
Decoder译码器 M (n):3 个固定控制引脚、n 路数据输入、2ⁿ路输出;仅控制满足 S1=1、S2=0、S3=0 才正常译码,输出只打印唯一 0 引脚编号;
MUX数据选择器 Z (n):n 路控制引脚、2ⁿ路数据输入、单输出引脚;控制码选中对应数据输出;
DEMUX数据分配器 F (n):n 路控制引脚、1 路总输入、2ⁿ路多路输出;仅选中通道输出数据,其余全部标记无效-。
顶层 Main 控制层依赖关系
plaintext
Main主程序
├─ 四大全局容器
│ circuitInputs:顶层外部输入缓存
│ components:所有元件实例池 key=元件名
│ connections:拓扑连接关系 源引脚→目标引脚列表(一对多)
│ pinValues:全局统一引脚电平仓库(所有输入、元件输入输出统一存放)
├─ 解析模块 parseAll()
│ parseInput() 解析INPUT全局输入
│ parseConnection() 解析[]连接语句,自动扫描引脚创建元件
│ tryRegisterFromPin() createComponent() 根据元件名称匹配正则实例化对应子类
├─ 计算模块 computeAll()
│ 循环迭代:先传播所有连线信号 → 全部元件重新计算逻辑 → 直到无电平变更
│ resolvePin() 统一读取引脚有效值
└─ 输出模块 printAll()
getOrderedComponents() 按规定9类顺序、同类编号升序排序元件
调用多态方法 comp.printOutput() 自动适配各类元件输出格式
设计心得
抽象基类抽离公共引脚读写逻辑,消除 9 个子类重复哈希读写代码,代码精简、易维护;
多态完美解决差异化输出问题,主输出循环无需 if/instanceof 分支判断,新增元件仅重写两个抽象方法;
全局统一pinValues是核心设计:无论外部输入、元件输入、元件输出全部作为唯一 key 存储,多级级联无需递归遍历电路,直接映射传递信号。
(二)SourceMonitor 报表数据分析
代码总行数:有效代码约 460 行,继承复用大量公共逻辑,无重复逻辑门运算代码,满足 50KB 长度限制;
类与方法统计:1 个抽象父类 + 9 个子类,主类包含解析、计算、排序工具方法共 8 个,全部方法职责单一;
圈复杂度分布:
各子类computeIfNeeded()逻辑独立拆分,单个方法圈复杂度最高 6,无超长 switch / 多重 if;
computeAll()迭代循环圈复杂度 4,采用双层分离(信号传播→元件计算)降低嵌套;
createComponent()元件名称正则匹配分支多,但每条匹配独立判断,可读性高;
代码复用指标:引脚读写、元件排序、编号提取全部抽取公共方法,复用率 70% 以上;
缺陷指标:无魔法数字,失效电平统一常量INVALID=-1;引脚编号规则全部封装在子类内部,主流程无硬编码引脚号。
(三)源码分层解读核心亮点
懒加载元件创建机制
解析连接语句时扫描所有目标引脚,截取元件名称,不存在则自动匹配正则生成对应子类存入components,无需提前预判电路存在哪些元件,完全适配任意拓扑,天然兼容后续时序元件扩展。
多轮稳态迭代信号传播
版本 1 单次遍历即可完成计算,版本 2 存在多级元件级联(A 输出→三态控制端→译码器输入),单次循环无法同步更新所有引脚。采用循环刷新机制,每轮先同步所有连线的信号映射,再重算全部元件,连续两轮无电平修改则判定电路达到稳态,停止迭代,完美解决信号传递滞后问题。
标准化引脚分配隔离
每类元件引脚功能、起始编号完全封装在子类内部,主程序无需关心引脚结构。例如译码器固定 0/1/2 为控制,3 开始为输入;分配器 0~n-1 为控制,n 为总输入,子类内部硬编码对应规则,上层代码完全解耦硬件引脚定义。
统一失效处理标准
所有引脚缺失、控制条件不满足、输入不全场景统一赋值INVALID=-1;打印时判断引脚值是否有效,无效直接跳过该元件输出,统一实现题目 “输入不全忽略元件” 要求。
三、采坑心得(结合测试用例、数据、流程图佐证)
坑 1:单次遍历无法传递多级级联信号,译码器 / 三态门输出全部失效
问题复现(测试用例 8)
顶层输入接入译码器控制引脚,译码器输出作为分配器控制信号;仅单次循环计算时,译码器输出引脚值未写入pinValues,分配器读取控制引脚全部为 INVALID,无输出。
原始错误流程
读取全部连接→传播一次信号→一次性计算所有元件;下级元件无法读取上一轮刚生成的元件输出电平。
修复流程图(稳态迭代)
初始化电路输入到 pinValues
循环(最大 200 轮防死循环)
遍历全部连接映射,同步源引脚值到所有目标引脚,记录电平是否发生修改
重置所有元件 computed 标记
全部元件重新执行 computeIfNeeded,更新输出引脚
无任何引脚电平变更,跳出循环
数据佐证
迭代 1:仅顶层外部输入同步,基础门计算完成;
迭代 2:基础门输出同步到下级控制引脚,三态、译码器完成计算;
迭代 3:复合型元件输出同步到末端分配器,电路全部稳定;
固定最大 200 次迭代,规避反馈电路死循环风险。
坑 2:元件排序提取编号逻辑漏洞,带括号元件截取编号失败
问题现象
M (3) 1、Z (2) 2 提取编号时,直接截取末尾数字,初次实现未过滤括号,读取到3)1数字解析报错;同类元件无法按编号升序输出,样例输出顺序混乱。
修复逻辑
工具方法extractNumber():先匹配最后一个),从括号后截取纯数字;无括号(S1、X1)直接过滤非数字字符,统一返回整型编号。
测试数据:9 类元件名称共 15 组测试用例,修复前 6 组匹配失败,修复后全部正确排序。
坑 3:译码器控制条件校验缺失,控制引脚不满足时仍输出有效编号
题目约束:仅 S1=1、S2=0、S3=0 译码器才工作,其余状态全部输出无效忽略。
错误代码
未增加三控制引脚联合判断,仅读取输入编码直接计算输出,测试用例 8 修改 E=1(S3=1)后仍输出 0,与样例要求不符。
修复数据校验
在 Decoder.computeIfNeeded () 优先判断s1!=1 || s2!=0 || s3!=0,条件不满足所有输出引脚置 INVALID,打印时直接跳过该元件。
坑 4:数据分配器输出拼接逻辑颠倒,有效位与无效-顺序错乱
测试用例 10 F (3) 1(3 位控制,8 路输出),初次循环从高位到低位拼接,输出字符串顺序与引脚编号 0~7 相反;
修复:循环 i 从 0 到 outCount-1,依次拼接 W0、W1…W7 对应字符,有效值填数字,无效填充-,输出-1------与样例完全匹配。
坑 5:三态门引脚编号混淆,误将输出设为引脚 0,输出打印格式错误
版本 1 门输出统一引脚 0,三态门硬件规范输出为引脚 2,初期复用旧逻辑输出S1-0,与样例 7S1-2:1格式不符;
修复:TriStateGate 打印固定读取 pin2,子类内部独立定义引脚分配,不与基础门共用输出引脚号。
坑 6:连接关系重复覆盖目标引脚,多源输入冲突无校验(题目约束违反)
题目规定一个输入引脚不能绑定多个输出信号,当前代码仅后覆盖前值,无冲突检测。
底层结构佐证:connections存储一对多映射,但未做反向校验;同一 dest 引脚被多条连接作为目标时,后序信号直接覆盖,逻辑硬件违规,属于遗留缺陷。
坑 7:正则匹配元件名称边界缺失,部分混合字符名称创建元件失败
初期 createComponent 仅用substring截取括号数字,未使用完整正则匹配;如M(3)12误读取输入引脚数 312,正则完整匹配M(\d+)\d+后分组捕获数字,彻底规避截取越界问题。
四、改进建议(适配迭代 3 时序电路、迭代 4 异常检测需求) - 架构分层解耦,抽取独立工具类(长期可持续扩展)
当前解析、编号提取、引脚读取工具方法全部耦合在 Main 类,代码臃肿,新增时序触发器会大幅增加 Main 体积。
改进拆分三类工具类:
ParseUtil:封装所有正则匹配、元件名称解析、输入文本拆分、编号提取静态方法;
CircuitContext上下文类:统一封装四大全局容器,提供 get/set 引脚值、注册元件、添加连接接口,主程序不再直接操作 Map;
CircuitSolver:独立电路稳态迭代计算模块,隔离拓扑传播逻辑。
优势:新增 D/JK 触发器仅新增 Component 子类,完全不改动解析、计算、主流程代码,严格遵循开闭原则。 - 完善输入逻辑异常检测(适配程序 4 异常校验需求)
补充完整校验规则,自定义CircuitException统一抛出格式 / 逻辑错误:
多源输入冲突检测:建立反向映射pin→源引脚,同一输入引脚绑定多个源时抛出冲突;
非法元件名称、控制引脚数为 0、输入引脚超出编号范围校验;
输出引脚短接检测:多条连接同一源输出引脚属于硬件违规,记录警告;
空连接语句、INPUT 格式不规范、非法电平值(非 0/1)拦截。 - 时序电路扩展改造(适配程序 3 带反馈触发器)
当前 Component 仅支持组合逻辑,无状态存储,无法实现 D、JK 触发器:
抽象基类增加int state状态属性,用于存储时序元件当前锁存电平;
拆分计算方法:calcCombination()组合逻辑、calcSequential()时序状态更新;
迭代计算区分时钟周期,增加时钟输入引脚识别,每轮迭代模拟一次时钟触发;
增加环路检测,识别反馈电路避免无限迭代。 - 性能与内存优化(贴合 64MB 内存、800ms 时间限制)
预缓存所有元件 getName 字符串,打印循环无需重复拼接,减少字符串对象创建;
将LinkedHashMap替换为基础 HashMap,降低链表额外内存开销;
迭代稳态提前退出优化:仅变更引脚关联的元件重新计算,无需全部元件重复运算;
解析阶段过滤无效空白引脚、临时字符串及时释放,减少内存占用。 - 子电路模块化封装(适配程序 4 子电路需求)
新增SubCircuit继承 Component,内部独立拥有私有引脚值缓存、内部元件、局部连接;对外暴露顶层输入输出引脚,支持主电路直接引用子电路名称作为元件,实现电路复用、分层搭建,降低复杂电路拓扑维护成本。 - 输出逻辑统一封装
当前九类元件打印逻辑分散在子类,新增格式化工具PrintUtil,提供译码器编号打印、分配器字符串拼接、普通门单引脚输出静态方法,子类仅传入参数调用,减少重复字符串拼接代码。
五、综合总结 - 本阶段核心收获
面向对象继承与多态工程落地:掌握抽象基类拆分公共能力、子类差异化实现业务逻辑的标准开发范式,理解多态消除大量分支判断的优势;
拓扑仿真建模思维:学会将硬件电路转化为程序有向图模型,通过全局状态缓存 + 迭代稳态仿真模拟硬件信号同步传播;
复杂多规格业务需求拆分:面对九类元件、三种输出格式、多套引脚规则,学会分层拆解、按实体分类封装,避免代码全部堆砌在主函数;
边界与硬件规则严谨性训练:通过译码器控制条件、三态门高阻、多路分配器无效标记等大量边界用例,养成逐条对照题目硬件约束调试的习惯;
软件迭代前置设计思想:开发基础版本时预留抽象扩展接口,而非一次性写死流程,提前兼容后续时序电路、子电路、异常检测的迭代需求。 - 待深入学习与研究方向
设计模式落地:使用工厂模式统一创建九类元件,消除 createComponent 中大量正则 if 判断;策略模式封装每类元件计算逻辑,进一步拆分子类职责;
图论算法优化电路仿真:当前暴力多轮迭代效率较低,学习拓扑排序,按信号依赖顺序单向计算,单次遍历即可达到稳态,大幅提升运行速度;
统一异常处理框架:学习工程化全局异常捕获、错误信息格式化输出,区分用户输入格式错误、电路逻辑错误;
内存与 JVM 底层优化:研究 HashMap 哈希冲突、字符串常量池、循环内对象复用,适配题目严苛 64MB 内存限制;
时序电路状态机仿真:学习触发器、寄存器状态流转逻辑,掌握带反馈环路的时序电路迭代仿真方案。
作业集6
一、前言
本次作业 7-1 NCHUD - 数字电路模拟程序 4,满分 100、难度困难,是整个数字电路迭代系列最终基础增强版本,在版本 1 纯基础逻辑门之上,两大核心新增功能:子电路模块化、全链路异常输入校验,同时保留 A/O/N/X/Y 五类基础门电路逻辑,是面向对象组合模式、语法词法校验、分层电路嵌套仿真综合实训大题。
- 覆盖核心知识点
面向对象组合设计模式:抽象电路单元 Component,分为叶子节点(基础门 Gate)、组合节点(子电路 SubCircuit),统一对外接口,实现电路嵌套复用;
多层级文本解析:先全局预解析所有前置子电路定义(Cx: ~ endc),再解析主电路 INPUT、连接语句,区分子电路局部作用域、主电路全局作用域;
子电路端口映射机制:子电路自定义 IN/OUT 端口(如 C2-A、C2-C),建立主电路引脚与子电路内部信号的双向映射;
多优先级异常校验体系:5 类电路语法 / 逻辑异常,固定报错优先级,多条连接异常仅输出第一条最高优先级错误;
作用域隔离:子电路内部元件编号独立,与主电路、其他子电路编号允许重复,输出时携带Cx-前缀区分归属;
全局信号冲突检测:反向映射记录每个输入引脚的信号源,检测多源驱动冲突;
标准化电路仿真逻辑:信号逐级传递、门电路逻辑计算、元件分类排序输出、无效引脚元件过滤;
输入分段解析状态机:区分「子电路定义段」「主电路运行段」两种解析上下文,endc/end 作为分段终止符。 - 题量与难度分析
本题为完整模块化电路仿真系统,相比 1/2/3 版本新增两大重量级模块,难点分为四块:
组合模式架构改造:原有单一 Gate 实体无法承载嵌套子电路,必须重构顶层抽象,统一叶子与组合单元接口,保证遍历、计算、输出逻辑通用;
子电路隔离与端口映射:每个子电路拥有独立局部信号池、局部元件集合、本地输入输出端口,主电路通过虚拟端口跨层级传递信号,多层嵌套映射易出现键名冲突;
多层异常校验规则复杂:5 类异常存在固定优先级,单条连接同时触发多条错误仅输出优先级最高项,多条连接报错只取第一条报错连接;区分连接语句中 “系统输出端”“系统输入端” 语义,极易混淆引脚角色;
输出格式分层区分:主电路元件直接输出名称,子电路内部元件统一拼接子电路编号-元件名-引脚前缀,同类元件全局统一升序排序;
硬性资源约束:代码上限 64KB、运行时限 400ms、内存 64MB,栈 8192KB,要求架构复用、无冗余重复校验代码。
整体不再是单层电路解析,而是可嵌套分层电路编译器 + 仿真引擎,考核设计模式、状态机解析、分层作用域、规则校验多重工程能力。
二、设计与分析
(一)PowerDesigner 组合模式类图完整结构
完全遵循题目推荐组合模式,顶层统一抽象组件,分叶子、组合两类实现:
plaintext
┌───────────────────────────────┐
│ abstract CircuitComponent │ 抽象顶层组件(统一接口)
├───────────────────────────────┤
│ # String compId │ 唯一标识(门名称 / Cx子电路编号)
│ # Map<String, Integer> signals │ 本地信号池(子电路私有/主电路全局)
│ # Listchild │ 组合单元持有子元件,叶子节点为空
│ + calcSignal() │ 统一信号计算接口
│ + collectOutput(List) │ 收集有效输出元件,用于全局排序打印
│ + getPortValue(String port) │ 获取对外端口电平
└───────────────┬───────────────┘
│
┌────────┴────────┐
▼ ▼
┌───────────────┐ ┌───────────────────────┐
│ Gate(叶子节点) │ │ SubCircuit(组合节点) │
├───────────────┤ ├───────────────────────┤
│ char type │ │ int subId │ 子电路编号
│ int num │ │ ListinPorts │ 子电路输入端口列表
│ int inputCnt │ │ ListoutPorts │ 子电路输出端口列表
│ Map<Integer,Integer> pinInputs │ Map<String,String> portMapping
│ int outputVal │ │ 主电路虚拟端口 ↔ 内部信号映射
├───────────────┤ ├───────────────────────┤
│ 实现全部抽象方法 │ 持有多个Gate子元件,递归计算内部电路
│ 仅代表单一逻辑门 │ 对外暴露虚拟端口,作为复合元件嵌入上层电路
└───────────────┘ └───────────────────────┘
顶层 Main 控制层结构:
Map<Integer, SubCircuit> subCircuitPool:全局预存储所有前置定义子电路(C1、C2...),解析主电路时直接引用;
Map<String, String> destPinSourceMap:反向映射,记录每个输入引脚对应的信号源,用于冲突检测;
ListerrorLogs:顺序存储所有连接语句异常,解析完成仅输出第一条最高优先级错误;
解析状态机:
状态 1:读取子电路定义,匹配Cx:开头,持续读取至endc,存入子电路池;
状态 2:读取主电路,匹配 INPUT、[] 连接语句,逐行语法校验、信号绑定、冲突检测;
流程分层:预解析子电路 → 主电路语法异常校验(按优先级判定)→ 无异常则全局信号迭代计算 → 递归收集所有主 / 子电路有效元件 → 按规则分类排序输出。
(二)SourceMonitor 报表数据分析
代码规模:有效代码约 520 行,组合模式复用公共接口,Gate 五类门逻辑复用版本 1 代码,无重复运算逻辑,满足 64KB 代码限制;
类与方法统计:1 个抽象父类、1 个叶子 Gate 子类、1 个组合 SubCircuit 子类,Main 包含解析、校验、映射、排序工具方法 9 个;
圈复杂度分布:
异常校验工具方法checkConnError()圈复杂度 7,按固定优先级顺序依次判断 5 类错误,逻辑线性无深层嵌套;
SubCircuit 内部信号递归计算圈复杂度 4,封装独立内部仿真,与主电路解耦;
元件名称正则匹配、子电路端口匹配独立拆分,单分支复杂度低;
代码复用指标:
引脚读取、门电路计算、元件排序、编号提取全部封装公共静态工具;
组合模式统一 calcSignal、collectOutput,主电路 / 子电路遍历逻辑完全复用;
缺陷指标:异常优先级使用有序枚举常量定义,无魔法数字;作用域信号池隔离,子电路局部信号不会污染主全局信号。
(三)源码分层解读与设计心得 - 组合模式核心设计心得
题目明确推荐组合模式,该架构完美解决子电路嵌套需求:
单一逻辑门(Gate)、封装电路块(SubCircuit)都属于CircuitComponent,主电路无需区分是基础门还是子电路,统一调用计算、收集输出;
子电路内部可以包含任意数量基础门,未来迭代 3 新增时序触发器,仅新增 Gate 子类,无需修改子电路、主电路遍历逻辑;
输出收集采用递归遍历:SubCircuit.collectOutput () 会遍历内部所有 Gate,自动拼接Cx-前缀,统一输出格式,无需单独区分打印逻辑。 - 子电路作用域隔离设计心得
子电路拥有独立私有signals信号 Map,内部元件编号仅在子电路内生效,主电路、其他子电路允许重名,解决编号冲突;
通过portMapping映射表实现跨层级信号传递:
例:子电路 C2 输入端口 A、输出端口 C,主电路引脚C2-A自动映射至子电路本地信号 A,C2-C读取子电路内部输出信号,对外屏蔽内部复杂引脚结构,实现模块化封装。 - 优先级异常校验设计心得
将 5 类异常按题目给定优先级顺序依次判断,一旦匹配当前连接的某类错误,直接返回该错误信息,不再判断后续低优先级异常;
解析时按行顺序存储所有连接异常,解析完全部输入后,若 errorLogs 非空,仅打印第一条异常并终止程序,不再执行电路仿真,符合题目要求。 - 分层输出排序设计心得
全局收集所有有效元件时,同时记录元件归属(主电路 / 子电路编号);统一按照「与门→或门→非门→异或门→同或门」大类划分,每一类内部按元件编号升序,子电路元件自动携带子电路编号前缀,输出格式统一对齐样例。
三、采坑心得(结合测试用例、数据、流程图佐证)
坑 1:子电路未做作用域隔离,内部元件编号与主电路冲突,输出混淆
复现用例:样例 2(C1 含 N1、C2 含 N2)
初期所有元件存入全局统一 Map,N1、N2 无归属标记,输出无法区分属于哪个子电路,打印丢失 C1/C2 前缀。
修复方案
每个 SubCircuit 内置独立子元件集合,收集输出时递归遍历子电路内部 Gate,拼接子编号-元件名作为输出标识;主电路元件无前缀,从根源区分归属。
数据佐证
修复前样例 2 输出N1-0:1 N2-0:0;修复后输出C1-N1-0:1 C2-N2-0:0,与标准样例完全匹配。
坑 2:连接语句引脚角色语义混淆,分不清 “系统输出端”“系统输入端”
题目核心易错定义:[] 中第一个元素是信号源(系统输出),后面所有元素是接收引脚(系统输入);元件输入引脚写在首位、元件输出引脚写在第二位都会触发异常。
复现用例:样例 6 [A(2)1-1 A(2)1-0]
初期仅判断引脚类型,未区分语句前后位置,未识别输入输出顺序颠倒;修复后先固定首位为源,其余为目标,顺序颠倒直接命中第 4 优先级错误。
坑 3:单条连接同时触发多条异常,低优先级错误覆盖高优先级
复现用例 8 [A(2)1-0 O(2)1-0]
同时触发「多输出源」(优先级 1)、「无输入目标」(优先级 2);初期并行判断,随机输出错误。
修复校验流程图
定义优先级枚举顺序:
多个输出源(连接首位超过 1 个)
无输入目标(无后置引脚)
无输出源(仅单个元素)
输入输出顺序颠倒
输入引脚多源冲突
遍历判断时,命中任意一级直接返回错误,终止后续判断,保证高优先级优先输出。
坑 4:输入引脚多源冲突检测缺失,同一引脚绑定两路信号无报错
底层结构佐证:新增destPinSourceMap反向映射,key = 目标引脚,value = 信号源引脚;每一条连接遍历目标引脚前先查询,若已存在源则直接记录冲突错误,停止解析当前连接。
测试用例 7 两条连接指向 A (2) 1-1,修复后正常输出冲突 ERROR。
坑 5:解析顺序颠倒,先解析主电路再解析子电路,引用 Cx 端口时找不到子电路
题目强制所有子电路定义在主电路之前,修复后解析流程固定:
逐行读取全部原始输入,先提取所有 Cx: ~ endc 子电路块存入 subCircuitPool;
剩余文本作为主电路输入,再解析 INPUT 与连接语句;
规避主电路提前引用未定义子电路的空指针问题。
坑 6:子电路端口映射双向绑定缺失,只能写入不能读取输出信号
初期仅实现主电路向子电路输入端口传值,子电路输出端口无法反向回传电平至主电路;新增 portMapping 双向读写方法,读取C2-C时自动查询子电路本地 OUT 端口电平,完成跨层级信号传递。
坑 7:异常处理逻辑未提前终止仿真,报错后仍打印元件输出
题目要求出现异常仅输出 ERROR,不输出任何元件电平;修复逻辑:解析完成后判断 errorLogs 非空,打印第一条异常直接 return,跳过 calcSignal 与 printOutput 流程。
四、改进建议(适配长期迭代与工程规范) - 子电路嵌套多层扩展优化(兼容迭代 3 时序电路)
当前仅支持主电路引用单层子电路,可扩展 SubCircuit 内部允许嵌套其他 SubCircuit;
优化改造:
SubCircuit 的 child 列表同时支持 Gate 与 SubCircuit,递归计算、递归收集输出;
输出前缀递归拼接C1-C2-A(2)1-0,多层嵌套清晰区分层级;
端口映射增加多层转发逻辑,自动传递多层子电路输入输出信号。 - 异常校验模块化抽取,构建统一校验工具类
当前校验逻辑耦合在主解析流程,抽取独立ConnValidator工具类:
封装 5 类异常判定静态方法,按优先级有序执行;
支持自定义错误信息格式化,统一输出 ERROR 标准字符串;
新增异常类型枚举,便于后续迭代 4 扩展更多非法输入校验(非法元件名、非法电平、引脚越界)。 - 性能优化适配 400ms 时间限制
预缓存所有子电路端口映射关系,解析主电路时直接查表,避免重复字符串拆分;
全局信号计算采用拓扑排序,替代暴力多轮迭代,单次遍历完成稳态计算,降低循环耗时;
收集输出元件时使用预分配 List,减少集合扩容开销;
正则 Pattern 全局静态预编译,不重复创建匹配器。 - 异常输入完整检测扩充(迭代 4 完整容错需求)
补充题目迭代 4 要求的全量非法输入检测:
非法元件名称、与 / 或门输入引脚数≤0;
电平值非 0/1、INPUT 格式不规范;
子电路重复定义相同编号、Cx 编号非法;
引用不存在的子电路端口、不存在的子电路编号;
输出引脚短接(多条连接同一源输出引脚),新增对应 ERROR 提示。 - 分层日志与调试框架
新增调试开关,可打印:
所有子电路内部端口映射关系;
每条连接语句的源、目标引脚;
全局信号池所有引脚实时电平;
便于复杂嵌套电路调试,快速定位端口映射、信号传递 bug。 - 统一资源回收机制
解析完成后主动清空所有 Map、List 缓存,释放内存,严格贴合 64MB 内存上限,避免多层子电路嵌套导致容器堆积内存溢出。
五、综合总结 - 本阶段核心学习收获
设计模式工程落地:彻底掌握组合模式适用场景,理解统一抽象接口拆分叶子 / 组合单元,实现可嵌套分层组件开发;
分层编译解析思维:学会分段状态机解析混合多层定义的输入文本,区分局部 / 全局作用域,隔离子电路私有数据;
规则驱动校验体系搭建:针对多优先级业务规则,设计有序判定逻辑,保证报错输出符合需求;
模块化硬件仿真思想:将电路封装为可复用子模块,通过虚拟端口对外暴露接口,屏蔽内部复杂实现,贴近真实硬件模块化设计;
边界与异常规范训练:完整覆盖 5 类电路语法、逻辑异常,建立 “先校验、后计算” 的标准程序开发流程;
迭代兼容设计思路:基于抽象顶层组件开发,新增元件、多层嵌套无需重构主流程,满足系列题目持续迭代需求。 - 待深入学习与研究方向
编译原理基础:词法分析、语法分析状态机,实现更健壮的输入解析,替代逐行字符串拆分;
拓扑图仿真优化:有向图邻接表存储电路信号依赖,拓扑排序单次计算电路稳态,提升仿真运行效率;
异常处理工程化:自定义分层异常类,区分语法异常、电路逻辑异常、资源异常,统一全局捕获输出;
组合模式拓展应用:学习装饰器、工厂模式搭配组合模式,实现子电路参数配置、元件自动创建;
多层级缓存优化:多级信号读写缓存,减少哈希表重复查询,适配超大规模嵌套电路仿真。 - 整体学习感悟
数字电路模拟程序 4 是整套迭代题集的集大成版本,融合了前面 1/2 版本的基础门电路仿真逻辑,新增组合模式模块化、完整异常校验两大核心工程能力。开发过程中最大难点在于理清子电路局部作用域与主电路全局信号的映射关系、区分连接语句中引脚的语义角色、严格遵循异常优先级输出规则,多次因层级隔离不足、语义混淆踩坑。
通过本次开发深刻体会到两点:
面对可嵌套、可复用的分层系统,提前使用设计模式抽象顶层接口,远比后期重构成本更低;
业务规则(尤其是异常、硬件约束)必须逐条拆解、按优先级固化代码逻辑,不能依靠模糊判断实现。
整套数字电路迭代项目从单层基础门、带控制引脚组合元件、时序电路预留、模块化子电路、全量异常校验完整走完,建立了小型仿真软件从需求拆解、架构设计、编码实现、边界校验、迭代扩展的完整开发思维,对后续大型面向对象综合项目开发具备极强参考意义。

浙公网安备 33010602011771号