前言
本阶段的学习主要围绕两次题目集和一次课堂测验展开,其中核心内容是数字电路模拟程序的设计与实现。题目集四和五呈现了从简单到复杂的渐进式设计过程,考察了面向对象编程、算法设计、将实体抽象为数据类型的能力以及代码编写能力。
知识点分析:
-
题目集四:涉及基本的门电路(与门、或门、非门、异或门、同或门)、输入输出处理、对应的算法设计,根据单一职责原则,开闭原则等进行程序设计,将系统分为三个核心模块,Pin类,Gate抽象类及子类和CircuitContext类。新增门电路时,仅需继承Gate类并实现calculateOutput()方法,无需修改原有代码。例如新增 “与非门”,只需创建NandGate子类,原有AndGate、OrGate等代码无需改动。我觉得难点主要在于如何将分散的门电路按逻辑连接,并保证信号按顺序传递。
-
题目集五:在题目集四的基础上,新增三态门、译码器、数据选择器、数据分配器。需要注意的点是,如何实现复杂原件,一开始的思路是,使用基础元件进行组合得到复杂原件,但是这样写效率很低,于是换成了模拟的方法。
-
课堂测验:侧重Java基础概念,包括类与对象、继承与多态、异常处理、集合框架等核心知识点。
设计与分析
题目集四设计与分析
题目集四要求编程实现包含与门、或门、非门、异或门、同或门的数字电路模拟程序,根据输入的元件信息、引脚连接及初始信号,计算并按规则输出各元件的输出电平。
- 输出顺序:按与门→或门→非门→异或门→同或门的顺序输出。
- 同类排序:同种元件按编号从小到大排序。
- 输出内容:各元件的输出引脚电平,无有效输入的元件不输出。
应该按真实电路的样子分层设计,分成五层来写,这样结构清晰,改bug也方便。我的设计思路:电路里有引脚类,有各种门类,还有个电路系统进行计算。引脚管信号传递,门管逻辑运算,系统管整体调度,各司其职,互相配合。
具体实现细节
1. 引脚类(Pin)
这个类我花了不少心思。引脚在真实电路里就是接线的点,有输入有输出。我在Pin类里记录了引脚名字、是输入还是输出、当前信号值。最关键的规则是:只有输出引脚才能给输入引脚传信号,这个必须严格遵守,不然就乱套了。另外,如果引脚没连好或者信号无效,那就标记为无效状态,不能参与计算。
2. 门电路类(Gate)
我写了一个Gate抽象类作为所有门的基类。这样做的好处是,所有门共通的东西都在这里写了,比如管理输入引脚、检查输入是否有效这些。然后各种具体的门(与门、或门、非门、异或门、同或门)都继承这个基类,每个子类只需要实现自己的计算逻辑就行。
比如与门,就是所有输入都为1时才输出1,否则输出0;或门是只要有一个输入为1就输出1。这些都是按照真值表来写的,一个门一个门地对照着真值表实现,虽然有点枯燥,但不容易出错。
3. 电路系统类(CircuitContext)
这个类相当于电路的总控中心。它要管理所有的门和引脚,还要处理用户输入的连接关系。最头疼的是计算顺序问题:A门的输出是B门的输入,必须先算A才能算B。我试了好几种方法,最后用了迭代计算:每次循环只算那些输入都准备好的门,直到所有能算的门都算完。
4. 工具类(GateNameParser)
解析元件名这个功能单独抽出来了。因为用户输入的名字像“A(8)1”这种,需要拆出类型、引脚数、编号等信息。单独写个解析类,代码看起来清爽多了,而且如果要改解析规则,只需要改这一个地方。
5. 主类(Main)
这个类比较简单,就是读用户输入,然后调用其他类干活,最后输出结果。把交互和业务逻辑分开,以后如果想改输入方式或者加图形界面,只需要改这里,不用动核心代码。
流程图:

下面👇是本次作业的类图。

SourceMonitor分析报告

先看整体表现,6个类平均每个有7.67个方法,方法平均语句数5.04,这个粒度还算合理,不会出现“巨型方法”。平均复杂度1.96,说明大部分代码逻辑清晰,读起来不费劲。不过有几个指标敲响了警钟——注释率仅5.1%,以后自己回头改或者组队协作,怕是要对着代码“猜谜”。
复杂度方面,Main.createGateFromName()真是“重灾区”,复杂度6、13行语句,还是全文件最复杂的方法。翻到322行一看,果然是条件判断嵌套多了,打算拆成几个小方法,比如把不同门电路的创建逻辑单独提取出来。另外Main.createGateIfNeeded()的块深度达5,嵌套太深也得优化,用提前return简化结构。
块深度分布里,深度5的语句只有1行,说明极端复杂的代码不多,主要是局部优化问题。方法调用和分支语句比例正常,但注释率必须提上来,计划给核心方法加JavaDoc,复杂逻辑旁补上行内注释,目标把注释率提到15%以上。
题目集五设计与分析
相较于题目集四,题目集五新增的几个复杂的逻辑门电路,三态门,数据选择器等。正如前面所说,这里采用模拟的方法解题。



- 程序在原有基础门电路模拟框架上,遵循 “开闭原则” 完成复杂元件扩展,整体仍保持抽象分层设计,核心层级(基础层、元件层、系统层、工具层、入口层)不变,但针对复杂元件特性优化了层内逻辑,实现基础门与复杂门的无缝融合。
- 以 “物理电路功能模拟” 为核心导向,放弃基础门组合实现复杂元件的思路,直接封装复杂元件的输入输出规则,在保证模拟准确性的同时,大幅降低开发复杂度和运行开销。
- 核心优化方向聚焦于引脚分类管理、信号传播逻辑迭代、输出结果差异化适配,解决复杂元件(三态门、译码器、数据选择器 / 分配器)的控制引脚管理、多输出引脚处理、特殊输出格式适配等核心问题。
复杂门电路子类的核心设计:
- 三态门(TriStateGate):
- 引脚定义适配物理特性,设置 0 号控制引脚、1 号输入引脚、2 号输出引脚。
- 计算逻辑封装 “控制引脚为真时输出输入信号,为假时输出高阻态(无效信号)” 的核心规则,仅当控制 + 输入引脚均有效时执行计算,贴合三态门的开关特性;
- 输出结果仅返回有效状态下的 2 号输出引脚信号,格式适配抽象类接口要求。
- 译码器(DecoderGate):
- 动态初始化引脚数量,根据输入引脚数自动计算输出引脚数(2^n),控制引脚固定为 3 个(S1/S2/S3),输入引脚从 3 号开始编号,输出引脚从 6 号开始编号,贴合译码器的引脚分布特性;
- 计算逻辑分三步:先校验控制 + 输入引脚有效性,再判定使能条件(S1=1 且 S2=S3=0),不满足则所有输出置为无效;满足则将输入信号转换为十进制编码,编码对应输出引脚置为 0,其余置为 1;
- 输出结果适配题目要求,仅返回输出为 0 的引脚对应的 “元件名:Y 编号” 格式,过滤无效信号和高电平输出。
- 数据选择器(MuxGate):
- 按控制引脚数动态计算输入引脚数(2^n),控制引脚独立管理,输入引脚紧随其后,输出引脚为最后一位;
- 重写有效性判定逻辑,仅校验控制引脚有效性(输入引脚可按需判定),符合数据选择器 “控制信号优先” 的特性;
- 计算逻辑核心为 “控制信号编码→选中对应输入引脚→输出该引脚信号”,仅当选中输入引脚信号有效时输出有效信号,否则输出无效。
- 数据分配器(DemuxGate):
- 按控制引脚数动态计算输出引脚数(2^n),单个输入引脚接收待分配信号,控制引脚选择输出通道;
- 计算逻辑为 “控制信号编码→选中对应输出引脚→将输入信号赋值给该引脚,其余输出引脚置为无效”,贴合数据分配器的 “一路输入、多路输出” 特性;
- 输出结果格式化拼接所有输出引脚状态,有效信号显示 0/1,无效信号显示 “-”。
类图设计:

SourceMonitor分析报告

代码评价:
代码规模:Main.java共698行代码,含458条语句,注释占比4.3%,注释覆盖度较低。
复杂度分布:方法最大复杂度为10,对应Main.createGateFromName(),该方法通过多分支判断创建不同类型门电路实例,分支逻辑导致复杂度较高;次高复杂度为9,对应DecoderGate.calculateOutput(),其需处理使能条件判定、输入编码转换、输出赋值等多步骤逻辑。
块深度:最大块深度为5,来自Main.createGateIfNeeded()的嵌套逻辑;整体平均块深度1.76,多数代码块嵌套层级合理,仅少数方法存在过深嵌套。
关键问题:createGateFromName()的多分支结构、DecoderGate.calculateOutput()的长逻辑链,是复杂度的主要来源;注释占比不足,代码可维护性待提升。
后续优化方向:拆分高复杂度方法的逻辑、增加注释、简化嵌套结构,降低代码复杂度与维护成本。
将系统拆分为三个核心模块,每个模块仅承担单一职责:
Pin类:仅负责引脚的信号存储、有效性判定及信号传递,不参与任何逻辑计算;
Gate抽象类及子类(AndGate/OrGate等):Gate抽象类定义门电路的通用行为(如引脚管理、有效性检查),子类仅实现对应门电路的核心计算逻辑,不涉及输入输出解析;
CircuitContext类:仅负责元件管理、连接解析及信号传播调度,不参与具体门电路的逻辑实现。
这种拆分避免了 “一个类包揽所有功能” 的耦合问题,例如修改与门的计算逻辑时,仅需调整AndGate类的calculateOutput()方法,不会影响引脚管理和信号传播逻辑。
以下是我目前没有通过的样例,希望有知道如何解决的小伙伴在评论区给出建议。
1 INPUT: S1-1 S2-0 S3-0 A-1 B-0 D-1
2 [S1 M(2)1-0]
3 [S2 M(2)1-1]
4 [S3 M(2)1-2]
5 [A M(2)1-3]
6 [B M(2)1-4]
7 [M(2)1-6 F(1)1-0]
8 [D F(1)1-1]
9 end
10
11 M(2)1:1
12 F(1)1:1-
课堂测验
这次 Java 面向对象课堂测验,核心考察了 OOP 基础概念,我梳理了考点和自己的掌握情况:
测验聚焦四大核心:一是类库与语法细节,比如java.lang包会自动加载、构造方法可被private修饰(单例模式常用);
二是多态、重写等特性,多态是 “同一行为不同实现” 而非 “同名方法共存”,子类重写父类方法需满足权限不缩小、返回值兼容;
三是抽象类与接口的区别,抽象类不能实例化但可包含普通方法,接口仅存抽象方法,类支持多实现接口但单继承类;
四是访问权限,如protected仅同包或子类可见。
我出错的点集中在接口与抽象类的细节:比如误判 “接口可包含方法体”,忽略了老版本 Java 中接口仅能定义抽象方法;多选时混淆了 “抽象类 / 接口无构造函数” 的正确结论。
整体看基础概念掌握还存在许多漏洞,抽象类、接口的边界规则容易混淆,后续需多加练习。
- 问题描述:与门 / 或门的命名格式是
A(8)1/O(4)2,需要解析出输入引脚数和编号;而非门 / 异或门 / 同或门是N1/X8/Y4,仅需解析编号。最初解析时未考虑括号的边界处理(如括号前后无多余字符),且简单门解析时误将非数字字符全部替换(比如误处理X(2)3这种非法输入,但题目保证输入合法)。
- 解决方案:编写专用解析工具类
GateNameParser,拆分与门 / 或门的解析逻辑(先找(和)的索引,再截取中间的输入引脚数、右侧的编号);简单门通过正则替换非数字字符后转整数。
- 问题描述:
① 输入引脚不能被多个输出引脚驱动,但最初代码未校验该约束(题目保证输入合法,因此无需校验,但需确保信号设置逻辑不覆盖)
② 输出引脚设置信号后,需立即同步到所有关联的输入引脚,最初遗漏了 “信号传播” 步骤,导致输入引脚信号未更新。
- 解决方案:
① Pin类的setSignal方法中,区分输入 / 输出引脚:输入引脚直接设置(不合并),输出引脚设置后遍历关联的输入引脚并同步信号;
② 在CircuitContext中增加propagateInputSources方法,显式触发输入源信号向所有关联引脚的传播。
如在测试以下样例时:
1 INPUT: A-1 B-0 2 [A A(2)1-1] 3 [B A(2)1-2] 4 end
输入源 A、B 被创建为输出引脚,调用setSignal(1)和setSignal(0)时,仅自身signal被设置,未同步到关联的与门输入引脚;
与门 A (2) 1 的 1 号、2 号输入引脚signal始终为null,hasAllValidInputs()返回false,无法计算输出,因此无结果。
修改:Pin类的setSignal方法:输出引脚设置信号后,遍历所有关联的输入引脚,同步信号;CircuitContext增加propagateInputSources方法,确保输入源信号主动传播到关联引脚。之后成功通过样例。
改进建议
- 明确 “输入引脚不能连接多个输出引脚” 的校验逻辑,当前代码未做该约束校验,建议题目补充:程序需检测并提示该违规场景。
- 补充引脚编号的合法性约束(如译码器输出引脚编号范围),避免无效引脚连接。
异常处理完善
在引脚编号解析、数值转换处添加 try-catch,防止输入格式错误导致程序崩溃,捕获异常后提示 “无效引脚编号:XXX”。
- 拓扑排序替代迭代计算:用简单的拓扑排序(按元件依赖关系排序)替代当前 “最大迭代次数” 的信号传播方式,减少重复计算,提升效率。
- 信号传播优化:仅在引脚信号变化时传播,而非每次全量迭代,比如给 Pin 类加状态标记,只传播变化的信号。
总结
通过本次数字电路模拟程序的设计与实践,我深刻体会到将理论知识转化为实际代码的过程既充满挑战也富有成就感。从基础门电路到复杂元件的逐步扩展,不仅锻炼了我的面向对象编程能力,更让我学会了如何运用分层设计、单一职责和开闭原则来构建可维护、可扩展的软件系统。
在实现过程中,我遇到了信号传播顺序、引脚连接合法性及复杂元件模拟等多个难题,通过迭代计算、模块化解析和模拟封装等方法逐一解决,这些经验让我认识到细致分析和逐步调试的重要性。同时,我也意识到自己在代码注释、设计模式应用和性能优化方面仍有不足,例如高复杂度方法需拆分、注释覆盖率待提升。
总体而言,这次项目让我不仅掌握了数字电路模拟的核心逻辑,更培养了系统化思考和工程化实现的能力。未来我将继续深入学习设计模式与算法优化,努力编写更清晰、健壮的代码,为后续开发更复杂系统奠定坚实基础。