NCHU_数字电路模拟程序
NCHU_BLOG_数字电路模拟程序
一、前言:
本次作业相较于上一次,复杂度明显上升。设计思路也完全不同,需要考虑的情况更是多如牛毛。各种电路元器件简直把大脑都要燃烧起来,果不其然,对于我这种小菜鸡来说,拿分也是一件奢侈的事情。最终也至是勉强通过一点点测试点,拿到一点点幸苦分。希望我的设计可以给到大家一点点微不足道的启发,接下来就是我的设计和分析过程了。
二、设计与分析
(一)1.1第一次数字电路模拟程序的设计与分析
1.1.1题目要求:
数字电路是一种处理离散信号的电子电路。与处理连续变化信号(如声音、温度)的模拟电路不同,数字电路只识别和运算两种基本状态:高电平(通常表示为“1”) 和 低电平(通常表示为“0”)。这正好与二进制数制系统相对应,使得数字电路成为所有计算机和数字系统的物理实现基础。
请编程实现数字电路模拟程序,
1、电路元件
电路中包含与门、或门、非门、异或门、同或门五种元件。元件特征如下:
与门:包含两个或多个输入引脚和一个输出引脚。所有输入引脚必须都是高电平,输出引脚才是高电平,只要有一个输入引脚为低电平,输出引脚输出低电平。
或门:包含两个或多个输入引脚和一个输出引脚。所有输入引脚必须都是低电平,输出引脚才是低电平,只要有一个输入引脚为高电平,输出引脚输出高电平。
非门:包含一个输入引脚和一个输出引脚。输出引脚的电平与输入引脚的电平相反,如输入为低电平,输出则为高电平。
异或门:包含两个输入引脚和一个输出引脚。当两个输入引脚电平不一致时输出引脚输出高电平,否则输出低电平。
同或门:包含两个输入引脚和一个输出引脚。当两个输入引脚电平一致时输出引脚输出高电平,否则输出低电平。
2、程序输入
1)元件信息:
用A、O、N、X、Y 分别用作与门、或门、非门、异或门、同或门五种元件的元件标识符。
电路中的每个与门、或门用“标识符(输入引脚数)+编号”作为其元件名。
例如:A(8)1表示一个8输入引脚的与门,O(4)2代表一个4输入引脚的或门。
电路中的每个非门、异或门、同或门用“标识符+编号”作为其元件名。
例如:X8表示一个异或门,Y4代表一个同或门,N1代表一个非门。
约束条件:
不同元件的编号可以相同,如X4、Y4。
同一电路中同种元件的编号不可重复,可以不连续
2)引脚信息:
引脚信息由“元件名-引脚号”构成,。
例如:A(8)1-2代表与门A(8)1的2号引脚。
3)电路的输入信息:
电路的输入格式:
INPUT:英文空格+输入1+”-”+输入信号1+英文空格+输入2+....+输入n+”-”+输入信号n
例如:
“INPUT: A-0 B-1 C-0”代表整个电路包括3个输入:A、B、C 分别输入0,1,0信号。
4)连接信息
引脚的连接信息格式:
[+输出引脚+英文空格+输入引脚1+。。。。+英文空格+输入引脚+]
例如:
[A A(8)1-1 A(8)1-3 X5-2]
代表信号从引脚A发送给与门A(8)1的1、3两个引脚,以及异或门X5的2号引脚。
[Y8-0 N1-1 O(4)2-3 Y2-1]
代表信号从引脚Y8-0发送给非门N1的1号引脚、或门O(4)2的3号引脚、同或门Y2的1号引脚。
约束条件:
一个输出引脚可以连接多个输入引脚,即将输出引脚的信号传给每一个输入引脚。但一个输入引脚不能连接多个输出引脚。
输出引脚不能短接在一起。
5)输入结束信息
所有输入以end为结束标志,end之后出现的内容忽略不计
3、程序输出
按照与门、或门、非门、异或门、同或门的顺序依次输出所有元件的输出引脚电平。同类元件按编号从小到大的顺序排序。
如果某个元件的引脚没有接有效输入,元件输出无法计算,程序输出结果忽略该元件
4、测试输入默认满足以下条件:
1)每个元件的输入引脚连续编号。假设元件有n个输入引脚,则其编号取值范围为[1,n],且引脚号不重复。
2)本题涉及的五种元件都只有一个输出引脚,输出引脚号默认为0。
本次题目没有提供类图的设计,全是本人一点点的小思路,类图的设计如下:

1.1.2类图的设计与分析:
ComponentType 枚举:
定义了各种逻辑门类型及其属性
Pin 类:
表示电路中的引脚
Component 抽象类:
所有逻辑门元件的基类
具体逻辑门类:
AndGate、OrGate、NotGate、XorGate、XnorGate
CircuitSimulator 类:
电路仿真主控类,负责解析输入、管理连接和执行仿真
Main 类:
程序入口点
1.1.3心得与分析:
Source Monitor分析结果:

由上图中分析结果不难得出代码的优劣:
优点:
- 复杂度相关指标
最大复杂度 (Maximum Complexity):13 - 非常高
平均复杂度 (Average Complexity):2.82 - 还算合理
最复杂方法:CircuitSimulator.parseConnection() (第204行)
问题分析:
parseConnection()方法的复杂度13过高,建议拆分为多个小方法
通常复杂度超过10的方法就需要重构 - 代码结构指标
类/接口数量:6 - 对于这个项目规模还算合适
平均方法数/类:5.67 - 分布比较均匀
平均语句数/方法:4.91 - 方法长度适中 - 可读性指标
注释行比例:5.9% - 严重不足
一般认为应该达到15-25%
缺乏足够注释会影响代码可维护性 - 控制流指标
最大块深度:6 - 有些嵌套过深
平均块深度:1.84 - 还算正常
分支语句比例:16.2% - 控制流不算过于复杂 - 方法调用
方法调用语句数:81 - 在179个语句中占比较大
说明代码比较模块化,方法职责划分较好
主要问题:
注释严重不足 - 需要大量增加注释
parseConnection()方法过于复杂 - 需要重构
部分嵌套过深 - 存在6层的块深度
改进措施如下:
1. 增加注释:
2. 重构parseConnection()方法:
3. 减少嵌套深度:
使用卫语句(guard clauses)提前返回
提取深度嵌套的代码块为独立方法
(二)1.2第二次数字电路模拟程序的设计与分析
1.2.1题目要求:
数字电路是一种处理离散信号的电子电路。与处理连续变化信号(如声音、温度)的模拟电路不同,
数字电路只识别和运算两种基本状态:高电平(通常表示为“1”) 和 低电平(通常表示为“0”)。
这正好与二进制数制系统相对应,使得数字电路成为所有计算机和数字系统的物理实现基础。
请编程实现数字电路模拟程序。
以下内容中,首行用#号标注的为本次新增的题目要求,其余内容与“数字电路模拟程序-1”相同。
1、电路元件
电路中包含与门、或门、非门、异或门、同或门、三态门、译码器、数据选择器、数据分配器九种元件。元件特征如下:
与门:包含两个或多个输入引脚和一个输出引脚。所有输入引脚必须都是高电平,输出引脚才是高电平,只要有一个输入引脚为低电平,输出引脚输出低电平。
或门:包含两个或多个输入引脚和一个输出引脚。所有输入引脚必须都是低电平,输出引脚才是低电平,只要有一个输入引脚为高电平,输出引脚输出高电平。
非门:包含一个输入引脚和一个输出引脚。输出引脚的电平与输入引脚的电平相反,如输入为低电平,输出则为高电平。
异或门:包含两个输入引脚和一个输出引脚。当两个输入引脚电平不一致时输出引脚输出高电平,否则输出低电平。
同或门:包含两个输入引脚和一个输出引脚。当两个输入引脚电平一致时输出引脚输出高电平,否则输出低电平。
三态门:三态门的作用类似于电路中的开关。包含一个输入引脚、一个输入控制引脚、一个输出引脚。当控制引脚为高电平时,三态门输入输出之间导通,输出电平等于输入电平;当控制引脚为低电平时,三态门输入输出之间呈现高阻态(类似开关断开),输出为无效状态。
译码器:译码器的作用是讲输入的编码转换为一路有效信号。一个译码器包含两个或多个输入引脚(如图中的A2\A1\A0)、三个控制引脚(如图中的S3\S2\S1)、4个或多个输出引脚(如图中的Y7~Y0)。根据输入输出的数量有2-4线译码器、3-8线译码器等。
当控制引脚当S1 =1,S2 +S3 =0时,译码器正常工作,输出引脚只有一个输出信号0,其余输出为1;哪个引脚输出0由输入引脚的编码决定,例如:图中的3-8线译码器三个输入引脚信号的编码与输出引脚的编码对应,A2\A1\A0输入000时,Y0输出0,其余输出1;A2\A1\A0输入001时,Y1输出0,其余输出1;依次类推。控制引脚不满足S1 =1,S2 +S3 =0时,译码器处于无效状态,所有输出为无效值。
数据选择器:数据选择器的作用是从多路输入信号中选择一个,并将其信号直接送往唯一的输出端,选择哪一路输入信号由控制端决定。如图所示控制端有两个则输入端有4个,S1\S0是两个控制端,D3D0是输入端,S1\S0的4种信号组合00、01、10、11分别选择D3D0其中一路输入。如S1S0=00,则Y=D0;S1S0=01,则Y=D1;S1S0=10,则Y=D2;S1S0=11,则Y=D3根据输入引脚数量的不同有二选一数据选择器(1个控制端)、四选一数据选择器(2个控制端)、八选一数据选择器(3个控制端)等
数据分配器:数据分配器的作用与数据选择器正好相反,是将唯一的一路输入信号输出到多路输出引脚的其中之一,选择哪一路输出引脚输出由控制端决定。如图所示控制端有两个AB,输出端有4个W0\W1\W2\W3,D是输入端,AB的4种信号组合00、01、10、11分别选择W3~W0其中一路输出,其他三路输出为无效状态。如AB=00,则W0=D;AB=01,则W1=D;AB=10,则W2=D;AB=11,则W3=D。根据输出引脚数量的不同有二路数据分配器(1个控制端)、四路数据分配器(2个控制端)、八路数据分配器(3个控制端)等
2、程序输入
1)#元件信息:
用A、O、N、X、Y、S 、M、Z、F分别用作
与门、或门、非门、异或门、同或门、
三态门、译码器、数据选择器、数据分配器九种元件的元件标识符。
电路中的每个与门、或门用“标识符(输入引脚数)+编号”作为其元件名。
例如:A(8)1表示一个8输入引脚的与门,O(4)2代表一个4输入引脚的或门。
电路中的每个非门、异或门、同或门用“标识符+编号”作为其元件名。
例如:X8表示一个异或门,Y4代表一个同或门,N1代表一个非门。
电路中的数据选择器、数据分配器用“标识符(控制引脚数)+编号”作为其元件名。
例如:Z(2)2代表一个四选一数据选择器,F(3)2代表一个8路数据分配器。
译码器用“标识符(输入引脚数)+编号”作为其元件名。
例如:M(3)1表示一个3-8线译码器。
约条件:
不同元件的编号可以相同,如X4、Y4。
同一电路中同种元件的编号不可重复,可以不连续
2)引脚信息:
引脚信息由“元件名-引脚号”构成。
例如:A(8)1-2代表与门A(8)1的2号引脚。
含控制引脚的元件如本次添加的所有元件,按控制-输入-输出的顺序排序,
每种类型的引脚按编号从小到大的顺序排序,
例如3-8线译码器M(3)1包含3个输入引脚、3个控制引脚、8个输出引脚,
M(3)1-0/1/2对应控制引脚S1/S2/S3,
M(3)1-3/4/5对应输入引脚A0/A1/A2,
M(3)1-6/7/8/9/10/11/12/13对应输出引脚Y0~Y7。
又如三态门的三个引脚,0号引脚为控制端、1号引脚为输入端、2号引脚为输出端。
3)电路的输入信息:
电路的输入格式:
INPUT:英文空格+输入1+”-”+输入信号1+英文空格+输入2+....+输入n+”-”+输入信号n
例如:
“INPUT: A-0 B-1 C-0”
代表整个电路包括3个输入:A、B、C 分别输入0,1,0信号。
4)连接信息
引脚的连接信息格式:
[输出引脚+英文空格+输入引脚1+。。。。+英文空格+输入引脚]
例如:
[A A(8)1-1 A(8)1-3 X5-2]
代表信号从引脚A发送给与门A(8)1的1、3两个引脚,以及异或门X5的2号引脚。
[Y8-0 N1-1 O(4)2-3 Y2-1]
代表信号从引脚Y8-0发送给非门N1的1号引脚、或门O(4)2的3号引脚、同或门Y2的1号引脚。
约束条件:
一个输出引脚可以连接多个输入引脚,即将输出引脚的信号传给每一个输入引脚。但一个输入引脚不能连接多个输出引脚。
输出引脚不能短接在一起。
5)输入结束信息
所有输入以end为结束标志,end之后出现的内容忽略不计
3、程序输出
按照与门、或门、非门、异或门、同或门、三态门、译码器、数据选择器、数据分配器的顺序依次输出所有元件的输出引脚电平。
同类元件按编号从小到大的顺序排序。
如果某个元件的引脚没有接有效输入、输入输出之间断开(如三态门)或控制引脚输入无效,元件输出无效,程序输出忽略该元件。
译码器不输出引脚电平,输出其输出为0的引脚的编号。如“M(3)1:3”代表译码器M3的输出引脚Y3输出0,其他引脚输出1。
数据分配器按引脚编号从小到大的顺序输出所有输出引脚的信号,无效状态引脚输出“-”。
如“F(2)1:--0-”代表分配器F1的输出引脚W2输出0信号,其他三个引脚为无效状态。
4、测试输入默认满足以下条件:
1)每个元件的输入引脚连续编号。假设元件有n个输入引脚,则其编号取值范围为[1,n],且引脚号不重复。
2)本题涉及的五种元件都只有一个输出引脚,输出引脚号默认为0。
1.2.2类图的设计与分析:
第一次的类图与第二次并无太多不同,仅仅是增加了几个元器件,具体增加的元器件在题目中也给出了原理和触发过程,需要注意的就是一些触发条件和约束条件。
因此这里就不给出具体的类图设计(毕竟自己设计完类图,然后代码只拿了一点点分,感觉很丢脸)
1.2.3心得与分析:
Source Monitor分析结果:

由图可知,此次的代码设计简直是天灾
- 复杂度严重恶化
最大复杂度:20 → 从13增加到20(增长53.8%)
平均复杂度:3.50 → 从2.82增加到3.50(增长24.1%)
最复杂方法仍然是parseConnection()(第492行)
严重问题:parseConnection()方法复杂度达到20,这在软件工程中是极其危险的信号,表明该方法承担了过多职责。 - 规模大幅扩张
总行数:845 → 从371行增加到845行(增长127.8%)
语句数:346 → 从179增加到346(增长93.3%)
类/接口数:5 → 从6减少到5,但代码量翻倍 - 方法负担加重
平均方法数/类:9.80 → 从5.67增加到9.80(增长72.8%)
平均语句数/方法:6.94 → 从4.91增加到6.94(增长41.3%) - 代码质量有所改善的方面
注释比例:9.0% → 从5.9%提升到9.0%(仍有改进空间)
平均块深度:1.38 → 从1.84降低到1.38(控制流更扁平化)
最大块深度:4 → 从6降低到4(嵌套减少)
严重问题识别:
parseConnection()方法(复杂度20)的问题:
这个方法现在需要处理:
各种类型的元件(9种)
每种元件的不同引脚分配逻辑
控制引脚与输入引脚的区分
错误处理
这明显违反了单一职责原则。
本次代码设计完全没有任何优点,只能看到全是问题,在这种大型题目的设计中,我还是没有做到将问题拆开了计算,而是想找一种统一的方法来解析每一种结果,这种做法的答案很明显,不仅无法做到面面俱到,反而是四处漏风,这也是我这次代码拿不到分的主要原因之一。
(三)课堂测验分析
本次课堂测验上,只获得了70%的分数,在看错误的题目时,发现自己在基础知识上,还有很多的漏洞。以下举一些具体的例子




这几个题目较为明显的反应出我对基础知识的不扎实,容易搞混知识点。在一些迷惑性选项面前,很容易走进圈套,在编写代码时,我也经常出现一些低级错误,但是指导这次的测试,我才明白,那些错误如果没有编译环境的提醒,我可能只有在运行的时候才知道错误,我需要加强我的编码能力,争取在小错误上不犯错,在大错误上少犯错。这样才能快速的写出好的代码。
三、踩坑心得
(一)第一次的代码设计完全就是每个元器件的设计,然后加上总体设计,并不容易出现原理的混乱,而是容易出现莫名的错误,归根结底还是自己在设计解决方法时,一股脑的写在了一个方法中,导致及其容易出现错误。
(二)向别人学习是一个很不错的方法,这次在写代码的时候,全程自己再写,闷着头,写断手也没什么成效,有时候不如借鉴一下他人的思路,或许和答案之间就隔着一面墙,他人的思路或许就是让你转个弯。
四、改进建议
(一)在类图的设计时,将不同的类进行明确的分开,但是在需要整合到一起时,想清楚不同的类整合起来,区别是什么?相似点是什么?而不是单纯的合并。
(二)码力不足,在碰到稍微复杂一点的算法时,只会使用最简单的if-else语句进行判断,很容易出现漏掉的情况,也很容易使代码出现奇怪的错误,在算法这一方面需要更加精进。
(三)代码的性能底下,很多地方都需要遍历所有,才会得出结果,导致刚开始提交时会出现超时的报错,还是在算法上需要精进。
(四)缺少错误处理,在输入错误或逻辑错误时,只能先依靠对结果的判断,再进行单步调试才能找到问题出现的点,会浪费很多时间。
(五) 代码结构问题,重构Component类,职责过重,可以考虑分开这个类,或者创建新类来分担这个类的职责。
五、总结
不足
(一)精进算法,在面对复杂问题时,选择更优的算法会快速的解决问题
(二)写代码时应该勤加注释,方便他人,也方便自己阅读
(三)在看到题目时,优先进行问题分解,方便做类的拆解
收获
通过第一次大作业,学到了初步的问题分解、类的创建,在解决复杂问题时能够细小化,逐个击破,这是一次很好的提升。也提升了个人的心态,在无数次失败中,看到胜利的曙光,然后走向光明,这个过程很爽,也和痛苦。谢谢阅读!
浙公网安备 33010602011771号