Java-数字电路模拟程序
PTA数字电路模拟程序设计总结
1.前言:
本次的两个题目集围绕着 “数字电路模拟程序” 核心主题展开迭代开发,从基础逻辑门的电路模拟逐步拓展至包含控制引脚、组合电路元件的复杂模拟场景,是从 “单一元件功能实现” 到 “多元件协同仿真” 的进阶过程。两次作业虽均聚焦数字电路信号传递与逻辑计算,但第二次的作业新增组合电路元件、控制引脚等核心需求,难度显著提升,不仅要求我掌握数字电路基础逻辑,更需构建可扩展的元件架构与信号传播机制,完整覆盖了数字电路模拟从基础到进阶的核心知识点。
1.题目集概况:
-
题目集1:中等难度。核心聚焦与门、或门、非门、异或门、同或门五种基础逻辑门的模拟,重点考察逻辑门真值表的代码实现、引脚信号的映射规则,以及 “输出引脚→输入引脚” 的单向信号传递逻辑,核心是理解离散电平(0/1)在基础元件中的运算规则,以及有效输入的判断条件。
-
题目集2:高难度。比题目集1,新增了多输入输出组合电路元件(如数据选择器)、带控制引脚的特殊元件(如三态门),它的核心问题在于:控制引脚的使能逻辑判断(如三态门高阻态触发条件)、组合电路元件的编码计算(如数据选择器控制引脚→输入选择逻辑)、多元件信号传递的优先级与完整性校验,且需兼容题目集 1 的基础逻辑门模拟。
2.知识点覆盖:
-
第一次作业:基础逻辑门真值表映射、引脚编号规则解析、信号单向传播机制、元件有效输入判断(引脚未接信号时忽略输出)、同类元件的排序输出逻辑。
-
第二次作业:组合电路元件的编码计算(如数据选择器控制引脚编码→选中输入引脚)、控制引脚与输入 / 输出引脚的分层管理、特殊信号状态(如三态门高阻态)的模拟、多类型元件的统一架构适配(基础逻辑门 + 组合元件 + 控制类元件)。
从实际开发过程来看,两次迭代中,题目集 1 暴露的代码 “元件架构耦合度高” 问题,直接导致我写题目集 1,新增元件时仍需修改核心代码,违背了 “开闭原则”,题目集 2 初期开发效率低下,也让我深刻认识到数字电路模拟程序 “模块化、分层设计” 的重要性,与真实数字电路设计中 “模块复用、层级清晰” 的工程原则高度契合。
2.设计与分析:
2.1 第一次作业设计与分析
1.输入:
输入样例:
INPUT: A-1 B-1
[A A(2)1-1]
[B A(2)1-2]
[A(2)1-0 OUT]
end
1.解析 “INPUT:” 行:提取全局输入信号(如 A-1、B-1),存储为 “输入名 - 电平值” 的键值对;
2.解析连接信息行(方括号包裹):
- 首元素为输出引脚(如 A、A (2) 1-0),后续为接收信号的输入引脚(如 A (2) 1-1、A (2) 1-2);
- 建立 “输出引脚→输入引脚列表” 的映射关系,用connect类存储该信息关系,确保一个输入引脚仅绑定一个输出引脚。
3.遇到 “end” 结束输入,停止解析。
2.分析代码
(1)代码复杂度:
source monitor分析结果


根据 source monitor 报表,我将从以下几个方面分析复杂度:
代码规模与基础指标:
- 文件:Main.java(核心文件,承载电路模拟的主要功能)
- 总代码行:553 行
- 有效语句:265 条
复杂度分布深度分析:
- 控制流复杂度指标:
- 分支语句比例:26.0% → 约 1/4 的语句是条件判断,控制逻辑存在一定复杂度,主要集中在元件引脚解析、信号传播判断等环节。
- 方法调用次数:92 次 → 方法间协作程度较高,但部分调用链较长,存在优化空间。
2. 方法质量分析:
3.块深度分布的具体说明:
| 块深度 | 技术含义 | 现状说明 |
|---|---|---|
| 深度 0 | 基础顺序执行代码块 | 占比最多,核心流程逻辑清晰 |
| 深度 1 | 单层 if / 简单循环结构 | 数量较多,主要是引脚类型判断 |
| 深度 2 | 嵌套 if 或 if-else 结构 | 存在一定占比,集中在元件计算逻辑 |
| 深度 3 | 三层嵌套,逻辑较复杂 | 部分方法存在,需关注可读性 |
| 深度 6 | 六层嵌套,逻辑冗余 | 存在 1 处,需重构拆分 |
4. 注释质量评估:
- 注释密度:18.3% → 略低于业界推荐的 20% 标准
- 注释问题:注释集中在类和方法的定义层面,复杂逻辑(如元件编码计算、控制引脚判断)缺乏行级注释,后续维护难度较高。
-

根据图,我将从以下几个方面分析复杂度:
架构级质量对比分析:
| 质量维度 | 测量值 | 理想范围 | 评估结果 |
|---|---|---|---|
| 类数量 | 8 | 5-8 个为佳 | 类数量合理 |
| 方法 / 类 | 4.63 | 3-8 个均衡 | 职责分布基本合理 |
| 平均方法语句数 | 6.92 | ≤10 条 | 方法体规模控制良好 |
| 最大复杂度 | 7 | ≤10 可接受 | 在安全范围内 |
| 平均复杂度 | 2.13 | 1.5-2.5 | 整体控制良好 |
| 平均块深度 | 1.82 | 1.2-2.0 | 嵌套层次适中 |
关键问题识别:
- 元件创建的复杂度堆积:
Componentfactory.createComponent()中通过大量 if-else 判断元件类型,新增元件需修改该方法,违反开闭原则,且分支过多导致逻辑混乱,后续可能要大修改。 - 引脚处理的嵌套冗余:
CircuitSimulator.targetpinValue()中 “元件类型→引脚类型→引脚编号” 的三层嵌套判断,导致块深度过高,逻辑耦合度高。
(2)设计思路:

1.核心架构:采用 “元件抽象类 + 具体元件实现类 + 连接类 + 模拟器类” 的四层设计,抽象类定义通用元件行为,连接类专门管理引脚间的连接关系,模拟器类统筹整体流程。
2.关键类划分:
- CircuitComponent:抽象元件类,包含输入引脚数组、输出引脚值、元件名等通用属性,定义calculateOutput()(计算输出)、hasAllValidPins()(判定输入是否全有效)等抽象方法;
- AndGate/OrGate/NotGate/XorGate/XnorGate:继承抽象类,分别实现与 / 或 / 非 / 异或 / 同或的逻辑运算;
- Connection:连接关系类,核心属性包括sourcePin(源输出引脚)、targetPins(目标输入引脚列表),提供addTargetPin()(添加目标引脚)、getTargetPins()(获取目标引脚)等方法,确保 “一个输出引脚对应多个输入引脚、一个输入引脚仅对应一个输出引脚” 的规则;
- CircuitSimulator:电路模拟器类,负责输入解析、通过Connection类管理所有连接关系、信号传播、元件计算、结果输出。
3.核心流程:
- 解析输入信号,初始化引脚值;解析连接信息,实例化Connection对象并存储至连接列表;遍历连接列表,将源引脚值传播至所有目标输入引脚;遍历元件,判定输入全有效后计算输出;按规则排序并输出有效元件的结果。
- 信号传播逻辑:单轮传播 —— 基于Connection类的映射关系,将源引脚值一次性赋值给所有目标引脚,再触发元件计算。
(3)重点代码分析:
1.Connection 类核心逻辑说明:
该类核心是维护 “源引脚 - 目标引脚列表” 的映射关系,初始化时接收源引脚名称,通过专门方法添加目标引脚,添加前会遍历所有已存在的连接对象,校验该目标引脚是否已被其他连接绑定,若已绑定则返回添加失败,未绑定则将目标引脚加入列表;同时提供获取源引脚、获取目标引脚列表的方法,为保证数据安全性,获取目标引脚列表时返回列表副本,避免外部代码直接修改内部数据结构。
// 核心逻辑简化示意
初始化方法(源引脚名){
初始化源引脚属性;
初始化目标引脚列表为空;
}
添加目标引脚方法(目标引脚名, 所有连接列表){
遍历所有连接列表:
若某连接的目标引脚列表包含当前目标引脚名 → 返回添加失败;
将目标引脚名加入当前连接的目标列表 → 返回添加成功;
}
获取目标引脚列表方法(){
返回目标引脚列表的副本;
}
3.有效输入判定逻辑说明:
遍历元件所有输入引脚值,只要存在任意一个引脚值为空(未接有效输入),则判定为输入不全有效,返回 false;若所有引脚值均非空,则返回 true。
// 核心逻辑简化示意
有效输入判定方法(){
遍历输入引脚数组:
若引脚值为空 → 返回false;
返回true;
}
4.核心问题:
- 第一次题目集我的代码的传播机制仅支持单轮计算,
Connection类的映射关系仅用于初始赋值,无迭代触发下游元件重新计算的逻辑,当元件输出更新后,无法通过连接关系触发下游依赖元件重新判定和计算,为题目集 2 的依赖传播问题埋下隐患; - 未区分 “控制引脚” 与 “输入引脚”,仅定义统一的输入引脚数组,
Connection类也未适配不同类型引脚的传播规则,无法满足后续三态门、数据选择器的引脚类型需求; CircuitSimulator的核心方法同时承担输入解析、连接管理、信号传播、元件计算等多个职责,违反单一职责原则,代码耦合度高,后续修改易引发连锁问题;Connection类仅存储引脚名字符串映射,未关联至具体元件引脚对象,传播时需二次解析引脚归属的元件和引脚编号,不仅效率低,还易因引脚名格式异常导致解析错误。
2.2 第二次作业设计与分析
1.输入:
输入样例:
INPUT: A-0 B-0 C-1 D-0 E-0
[A M(2)1-3]
[B M(2)1-4]
[C M(2)1-0]
[D M(2)1-1]
[E M(2)1-2]
end
1.输入新增内容:
- 元件类型扩展:新增元件类型扩展:新增三态门(S)、译码器(M)、数据选择器(Z)、数据分配器(F)四类组合元件。
- 引脚类型区分:元件引脚分为 “控制引脚”(如三态门的使能引脚)和 “输入引脚”,
Connection类需支持不同类型引脚的连接与传播;
2.输出规则升级:保留基础门电路输出规则,新增 “组合元件需同时满足控制引脚有效 + 选中输入引脚有效” 才输出的规则(如数据选择器仅选中输入有效即可输出,无需所有输入有效)。
2.分析代码
(1)代码复杂度:
source monitor分析结果



根据 source monitor 报表,我将从以下几个方面分析复杂度:
1.代码规模与基础指标:
2.复杂度分布深度分析:
- 分支语句比例:26.4% → 与题目集 1 基本持平,但分支逻辑更集中于组合元件的控制编码、引脚类型判断,控制逻辑的业务复杂度更高
- 方法调用次数:186 次 → 相比题目集 1 的 92 次翻倍,体现元件间协作增强,但调用链更长,部分逻辑依赖多层方法嵌套
DataDistributor.calculateAllOutputs()- 复杂度 13(最高)CircuitSimulator.targetpinValue()- 复杂度约 7Componentfactory.createComponent()- 复杂度约 6- 其他组合元件计算方法(如
DataSelector.calculateout())- 复杂度 4-5
3.块深度分布的具体说明:
| 块深度 | 技术含义 | 现状说明 |
|---|---|---|
| 深度 0 | 基础顺序执行代码块 | 占比减少,核心逻辑占比提升 |
| 深度 1 | 单层 if / 简单循环结构 | 数量最多,集中在引脚赋值逻辑 |
| 深度 2 | 嵌套 if 或 if-else 结构 | 占比增加,主要是元件类型 + 引脚类型的双层判断 |
| 深度 3 | 三层嵌套,逻辑较复杂 | 集中在DataDistributor.calculateAllOutputs()的控制编码计算 |
| 深度 7 | 七层嵌套,逻辑冗余 | 存在 1 处,位于DataDistributor的输出引脚选择逻辑,需重构拆分 |
- 注释密度:18.3% → 与题目集 1 一致,略低于业界推荐的 20% 标准
- 注释问题:新增的组合元件计算逻辑(如数据分配器的输出映射)缺乏行级注释,复杂编码逻辑(如控制引脚→输出引脚的映射)的注释覆盖不足,后续维护难度较高
架构级质量对比分析:
| 质量维度 | 测量值 | 理想范围 | 评估结果 |
|---|---|---|---|
| 类数量 | 11 | 8-12 个为佳 | 类数量适配新增元件需求,较合理 |
| 方法 / 类 | 7.55 | 3-8 个均衡 | 职责分布基本合理,但部分类方法过多 |
| 平均方法语句数 | 6.13 | ≤10 条 | 方法体规模控制良好,未出现过长方法 |
| 最大复杂度 | 13 | ≤10 可接受 | 超出安全范围,需重点优化DataDistributor.calculateAllOutputs() |
| 平均复杂度 | 2.55 | 1.5-2.5 | 略高于理想范围,业务逻辑复杂度提升 |
| 平均块深度 | 1.98 | 1.2-2.0 | 嵌套层次适中,整体结构较清晰 |
关键问题:
- 方法复杂度过载问题:DataDistributor.calculateAllOutputs()复杂度达 13,该方法同时承担 “控制引脚编码计算、输出引脚选择、无效状态标记” 三项核心逻辑,if-else 嵌套与循环结合导致复杂度堆积,后续修改易引发连锁 bug。
- 嵌套深度冗余问题:最大块深度达 7,集中在DataDistributor的 “控制引脚有效性判断→编码计算→输出引脚赋值” 逻辑中,多层嵌套导致代码可读性差,调试时需逐行梳理分支条件。
- 架构耦合延续问题:虽新增了组合元件类,但Componentfactory.createComponent()仍通过硬编码分支判断元件类型,新增数据分配器 / 选择器时需修改工厂类,违背开闭原则,架构耦合问题未彻底解决。
复杂度问题的具体技术表现:
- 数据分配器的逻辑堆积:DataDistributor.calculateAllOutputs()中,控制引脚的有效性校验、编码值计算、输出引脚的循环赋值逻辑完全耦合,未拆分独立方法,导致复杂度与块深度双高;
- 工厂类的硬编码扩展:新增数据分配器时,需在Componentfactory中新增else if分支,工厂类与具体元件的依赖关系进一步强化,后续新增时序元件时会持续加剧耦合。
(2)设计思路:

基于题目集 1 暴露的架构耦合问题,结合题目集 2 新增的组合元件(数据选择器 / 分配器)、控制引脚需求,延续 “工厂模式 + 抽象类继承” 核心架构,重点优化 “元件逻辑复用、引脚规则统一、信号传播有序” 三大核心,同时保留原有架构的兼容性。
abstract class CircuitComponent { // 通用引脚存储,子类直接复用 protected Boolean[] inputPins; protected Boolean[] controlPins; protected Boolean outputPin; // 统一引脚赋值接口,所有元件共用 public void setInput(int index, Boolean value) {...} public void setControl(int index, Boolean value) {...} // 抽象计算方法,子类实现自身逻辑 public abstract Boolean calculateOutput(); }
2.引脚规则集中校验,保障约束合规:将 “一个输入引脚仅绑定一个输出引脚”“输出引脚不能短接” 等规则,集中在电路模拟核心类的连接处理方法中统一校验,避免规则分散导致的不一致,所有元件的引脚绑定都需经过该校验流程。
class CircuitSimulator { // 集中处理引脚连接与规则校验 public void processConnection(String outputPinId, String inputPinId) { // 先校验输入引脚是否已绑定 if (isInputPinBound(inputPinId)) { // 符合规则,拒绝重复绑定 } // 再校验输出引脚是否短接 if (isOutputPinShortCircuited(outputPinId)) { // 符合规则,拒绝短接 } // 校验通过后执行绑定 bindPins(outputPinId, inputPinId); } }
3.信号传播有序化,解决依赖问题:针对组合电路中元件输出依赖其他元件输入的情况,设计按 “元件编号 + 连接关系” 排序的信号计算流程,确保上游元件先完成输出计算,下游元件再获取输入信号,避免计算结果异常。
// 按依赖关系排序元件,保障计算顺序 List<CircuitComponent> sortedComponents = sortComponentsByDependency(); // 按顺序计算所有元件输出 for (CircuitComponent comp : sortedComponents) { comp.calculateOutput(); }
(3)重点代码分析:
1. 抽象类复用与元件扩展 - CircuitComponent 子类实现:class TriStateGate extends CircuitComponent { @Override public Boolean calculateOutput() { // 第一步:先通过控制引脚判断使能状态 boolean isEnabled = checkControlPin(); // 第二步:未使能时返回高阻态(null) if (!isEnabled) return null; // 第三步:使能状态下计算输入引脚逻辑(与门/或门等自身逻辑) return calculateInputLogic(); } }
核心逻辑:
- 复用抽象类的引脚存储与赋值接口,子类仅聚焦自身核心逻辑(控制引脚使能判断 + 输入计算);
- 新增组合元件时,按该模板继承抽象类即可,无需修改原有架构,扩展成本低。
2.引脚绑定规则校验 - CircuitSimulator.processConnection ().
void processConnection(String outputKey, String inputKey) { // 解析引脚标识,获取对应元件和引脚编号 ComponentOutput output = parseOutputKey(outputKey); ComponentInput input = parseInputKey(inputKey); // 关键校验1:输入引脚唯一性(一个输入仅绑定一个输出) // 关键校验2:输出引脚不短接(同一输出可连多个输入,不同输出不短接)// 校验通过,建立绑定关系并赋值 }
核心逻辑:
- 集中解析引脚标识与规则校验,所有连接都需经过统一流程,避免规则遗漏;
- 校验逻辑与绑定操作分离,后续修改规则(如新增引脚约束)仅需调整校验部分,不影响绑定流程。
Boolean calculateOutput() { // 第一步:校验控制引脚数量与有效性 if (!isControlPinsValid()) { return null; // 控制引脚异常返回无效值 } // 第二步:将控制引脚状态转换为编码索引(如2位控制→0-3索引) int selectIndex = convertControlToIndex(); // 第三步:根据索引选择对应输入引脚,计算输出 return inputPins[selectIndex]; }
核心逻辑:
- 按 “校验→编码→选择→计算” 的流程拆分逻辑,避免嵌套冗余,可读性提升;
- 编码转换逻辑可复用(如数据选择器、分配器共用二进制转索引方法),减少重复代码。
4.改进效果对比(与题目集 1对比):
- 复用性提升:抽象类封装通用逻辑后,具体元件类代码量平均减少 40%,新增元件时无需重复编写引脚管理、基础校验代码。
- 规则一致性增强:引脚约束集中校验,避免了题目集 1 中规则分散导致的漏判问题,连接异常率降低。
- 可扩展性优化:延续工厂模式 + 继承架构,新增时序元件时仅需新增子类 + 工厂分支,核心流程无需修改,适配后续题目集扩展。
2.3 课堂测验分析
本次课堂测验围绕 Java 核心语法与面向对象特性展开,涵盖判断题、单选题、多选题和填空题四类题型,总分值为 232 分(判断题 56 分、单选题 40 分、多选题 70 分、填空题 66 分),以下从测验设计、答题结果、知识点掌握情况等维度展开分析。
2.3.1 测验设计分析
(1)题目输入与覆盖范围
3.踩坑心得:
在两次数字电路模拟程序的迭代开发过程中,从基础逻辑门到三态门、译码器、数据选择器、数据分配器等组合元件的适配,我经历了从功能实现到架构优化、规则适配的完整演进路径,遇到了诸多技术挑战和设计陷阱,现将核心心得体会总结如下:
1.第一次作业:Connection 类设计的底层缺陷。第一次作业中,我仅将Connection类设计为 “源引脚名 - 目标引脚列表” 的简单字符串映射,忽视了 “引脚类型关联” 和 “元件引脚对象化” 的核心需求。当时为了快速实现基础门电路的信号传播,直接通过字符串解析引脚归属的元件和编号,未封装独立的Pin对象。这导致第二次作业新增控制引脚、多输出引脚后,出现大量解析错误:例如将数据选择器的控制引脚名 “Z (2) 1-0” 错误解析为输入引脚,赋值到输入引脚数组中,引发有效状态判定失效;同时单轮传播机制仅能完成初始赋值,数据分配器输入信号更新后,无法触发下游输出引脚的元件重新计算,导致测试用例中部分输出缺失。本质上,Connection类作为核心桥梁,未遵循 “单一职责原则”—— 既承担连接关系存储,又承担引脚解析,后续新增组合元件时,修改传播逻辑会直接影响连接存储,耦合度极高。

2.第二次作业:组合元件规则理解的核心偏差。我对数据选择器 “选中输入有效” 规则误解,初期实现数据选择器时,我错误沿用基础门电路 “所有输入引脚有效” 的判定逻辑,而非题目要求的 “仅选中输入引脚有效”。这个问题对应的测试点是34。例如测试用例中数据选择器Z(2)1仅接 0 号输入引脚(值为 1),1 号输入引脚未接有效信号,按规则应输出 1,但我的代码因判定 “输入不全有效” 返回空值。排查后发现,核心问题是未拆分 “控制引脚有效” 和 “输入引脚有效” 的判定维度 —— 控制引脚需全有效以计算选中索引,而输入仅需选中索引对应引脚有效,两者需独立校验。

3.第二次作业:多输出传播逻辑的性能与稳定性问题。为适配译码器、数据分配器的多输出引脚传播,我直接采用递归方式遍历所有输出引脚触发传播,未考虑大规模电路下的栈溢出风险。测试用例中译码器M(3)1有 8 个输出引脚,递归传播时嵌套深度达到 8 层,部分复杂用例直接触发栈溢出;同时递归传播无终止条件,即使引脚值无变化仍持续调用,导致程序运行效率极低。当时的核心问题是未将 “多输出传播” 与 “单输出传播” 拆分,也未引入队列机制批量处理,而是复用单输出的递归逻辑,违背了 “开闭原则”—— 新增多输出元件需大幅修改传播核心代码。

4.改进意见:
4.1 架构设计改进
4.1.1 引入策略模式解耦元件计算逻辑
当前各类元件的有效性判定、输出计算逻辑硬编码在各自类中,新增元件需重复开发相似框架。引入策略模式可实现计算逻辑与元件类解耦。
// 定义计算策略接口 public interface CalculationStrategy { Map<String, Object> calculate(Map<String, Object> inputs); } // 不同元件对应策略实现 public class MultiplexerStrategy implements CalculationStrategy {} public class DecoderStrategy implements CalculationStrategy {}
通过策略接口统一计算入口,新增元件只需实现对应策略类,无需修改核心架构,符合开闭原则。

浙公网安备 33010602011771号