作业集 4~6 程序设计总结
数字电路模拟程序三次作业总结报告
一、前言
本阶段三次作业围绕""展开,采用迭代式开发模式,我们逐步进行完整化代码。
1.第一次作业:
(1)知识点:基础面向对象设计、SOLD职责原则、集合存储管理、正则输入解析、基础逻辑运算实现
(2)题量:216行
(3)难度:较难
(4)功用:实现五类基础逻辑门运算、电路信号单向传递、基础电路仿真、无效输入过滤
2.第二次作业:
(1)知识点:复杂多类协作、多引脚组件建模、状态迭代更新、多级信号传递、分支逻辑优化
(2)题量:395行
(3)难度:较难(但比上一个难)
(4)功用:实现译码器、选择器、分配器、三态门等复杂元件仿真、多控制引脚电路运算、高阻态与无效电平处理
3.第三次作业:
(1)知识点:组合设计模式、分层架构设计、自定义异常校验、优先级错误处理、全局引脚映射、子电路封装复用
(2)题量:590行
(3)难度:较难
(4)功用:实现子电路嵌套调用、电路语法合法性校验、信号冲突检测、标准化电路输出、完整工程级电路仿真
每次代码的难度会慢慢上升,体量也慢慢增大,功能也在增多,元件的数量也在增多,测试点也变复杂了。
二、设计与分析
1.作业一
(1)类图设计
类职责划分
Gate抽象类:作为所有逻辑门的顶层父类,统一封装引脚数量、引脚数值、运算状态等通用属性,定义通用输入赋值方法与抽象运算方法,统一所有门电路的行为规范,是整个项目的顶层抽象模板。
AndGate、OrGate、NotGate、XorGate、XnorGate子类:分别对应五类基础逻辑门,继承自Gate抽象类,各自重写computeOutput()运算方法,仅负责自身专属的逻辑运算,职责单一且互不干扰。
GateFactory工厂类:纯工具类,无任何成员属性,仅提供静态创建方法,专门负责根据输入的元件字符串动态生成对应逻辑门对象,统一对象实例化入口,隔离创建逻辑与业务逻辑。
InputParser类:专门负责程序的文本输入读取、外部输入电平解析、电路连线解析与门对象初始化,只负责数据解析工作,不参与仿真计算。
CircuitSimulator类:核心业务类,专门负责电路信号的队列传播、引脚赋值、门电路运算、电平迭代更新,独立承担所有仿真运算逻辑。
ResultFormatter类:纯工具输出类,专门负责门电路的规则排序、结果格式化输出,仅处理结果展示逻辑。
CircuitMain主类:仅负责程序整体流程调度,统筹解析、仿真、输出三大模块,不包含任何核心业务逻辑,实现流程与解耦。
(2)SourceMonitor代码分析报告
项目: 航空器配载系统1
指标 数值 含义说明
Files 11 一共分析了 11 个 .java 文件
Lines 2558 项目总代码行数(包含代码、注释、空行,按你之前的设置过滤了空行)
Statements 2111 可运行业务代码总量,解析、校验、仿真、格式化、工具类逻辑完整,无大量冗余空代码
% Branches 20.9% if/switch/for/while 等分支代码占比适中;分支主要集中在元件类型判断、引脚角色校验、仿真迭代,未出现极端多层嵌套
Calls 93 类与类之间、内部方法交互调用总量,分层调用链路清晰,无循环依赖、无过度递归调用
% Comments 3.1 注释覆盖率极低,是项目主要短板:缺少类说明、方法功能注释、关键常量注释,工程规范性不足
Classes 11 文件数 = 类数量,严格遵循一个文件一个类,完全符合单一职责设计规范
Methods/Class 2.36 平均每个类仅 2~3 个方法,方法粒度极细,无臃肿超大类
Avg Stmts/Method 5.44 单个方法平均仅 5 行左右代码,符合「短小方法」最佳实践,可读性高、调试简单

2.作业二
(1)类图设计
类职责划分
Main类:程序入口类,仅负责创建电路全局上下文对象,统一调度输入读取、电路仿真、结果输出三大流程,不参与任何具体业务运算,职责纯粹单一。
Circuit类:全局电路核心上下文类,统筹管理整个电路系统的所有数据与核心流程。负责存储外部输入信号、引脚连线关系、元件映射关系、引脚电平数据;提供文本解析、元件自动构建、迭代仿真刷新、元件排序、格式化输出等核心业务能力,是整个系统的数据中枢与流程调度中心。
Component类:通用元件实体类,统一封装所有九类电路元件(A/O/N/X/Y/S/Z/M/F)。根据元件类型与参数自动初始化控制引脚、输入引脚、输出引脚,统一提供就绪状态判断、逻辑运算计算方法,内置基础门电路运算工具函数,集中封装所有元件的运算行为。

(2)SourceMonitor代码分析报告
项目: 航空器配载系统2
指标 第二次(7 个文件) 变化与解读
Files 3 项目仅 3 个类文件,未做分层拆分,全部逻辑聚合在少量大类中
Lines 392 过滤空行后有效代码总行数,覆盖 9 类元件解析、仿真、输出全套逻辑
Statements 360 可执行业务语句总量,逻辑体量饱满
% Branches 38.1% 分支语句占比极高,大量 if/switch 集中在 Component.compute ()、initPins (),圈复杂度风险突出
Calls 198 方法调用频繁,内部方法互相嵌套调用多
% Comments 0.0% 两次均未识别到有效注释,需要补充
Classes 3 文件与类一一对应,未做职责拆分,违背单一职责思想
Methods/Class 5.67 平均每个类有 5~6 个方法,Circuit、Component 类承载过多功能
Avg Stmts/Method 18.76单个方法平均近 19 行代码,方法体量偏大,拆分粒度不足
Max Complexity 74 极高圈复杂度,出自 Component 的多分支运算方法,远超安全阈值 10,极难调试、易出逻辑 bug
Avg Complexity 7 最大代码嵌套深度 7 层,多层 if-switch 嵌套,阅读、排错难度大
Max/ Avg Depth 3.58 平均嵌套深度偏高,代码层级堆叠严重
(3)设计心得:V2 在 V1 基础上做了简单分层,拆分出 Main、Circuit、Component 三个大类,将全局电路数据统一托管在 Circuit 上下文,同时拓展支持选择器、多路译码器等九类电路元件,丰富了电路仿真能力。但是这个代码有一个测试点没有通过,基本元件测试,这个没有通过,我猜测:一是所有基础门电路运算写在同一个 Component 的 compute 方法中,switch 分支较多,逻辑判断容易写错,电平计算结果与标准不符;二是电路仿真迭代仅单次刷新,多级串联门电路无法稳定更新所有引脚电平;
3.作业V3.0设计分析
(1)类图设计
Main 主类:程序唯一入口,统一调度解析、校验、仿真、格式化输出全流程,仅负责流程编排,无任何业务计算逻辑。
LogicGate 抽象父类:所有逻辑门的顶层抽象模板,统一封装引脚、名称、编号、输入数量等通用属性,定义抽象计算方法,规范所有门电路行为,满足开闭、依赖倒置原则。
具体门电路子类(AndGate/OrGate/NotGate/XorGate/XnorGate):继承LogicGate,各自独立实现专属逻辑运算方法,单一职责,只负责自身运算规则,完全符合里氏替换原则。
GateFactory 工厂工具类:纯静态工具类,专门根据元件字符串类型动态创建对应逻辑门对象,统一实例化入口,解耦对象创建与业务逻辑。
Connection 连线实体类:封装单条连线的原始文本、引脚列表、所属上下文ID,纯数据实体,存储连线基础信息。
SubCircuit 子电路类:管理单个子电路的输入、输出、连线集合,独立维护子电路内部资源,支持多层电路嵌套结构。
CircuitContext 全局上下文类:系统核心数据中枢,统一管理所有子电路、全局输入信号、主电路连线,提供全局数据汇总查询能力。
PinUtils 引脚工具类:纯静态工具类,提供引脚类型判断、元件名解析、引脚角色判定、全局节点转换通用方法,无成员变量,仅处理数据。
CircuitParser 解析器类:专门负责读取控制台文本、解析主电路/子电路、输入输出、连线语句,构建完整CircuitContext上下文对象。
CircuitValidator 校验器类:独立负责电路合法性校验,包括连线输入输出数量校验、顺序校验、信号冲突校验,单独剥离校验职责。
CircuitSimulator 仿真核心类:收集所有逻辑门、构建信号映射关系、迭代刷新引脚电平,实现完整电路信号传播仿真逻辑。
ResultFormatter 结果格式化类:专门负责逻辑门按规则排序、封装标准化输出结果,只负责视图展示逻辑。

(2)SourceMonitor代码分析报告
指标 数值 解读
Files 16 16 个独立.java类文件,严格遵循单一职责分层拆分
Lines 701 过滤空行与纯注释后的有效代码总量,完整实现子电路嵌套、五级优先级异常校验、多态门电路、全局引脚映射全部需求
Statements 6110 业务逻辑语句充足,覆盖解析、校验、仿真、排序、输出全流程
% Branches 14.3 分支语句占比大幅降低,相比 V2 的 38.1% 优化明显;通过抽象多态消除巨型 switch 分支,仅少量必要判断,逻辑清晰
Calls 192 模块间调用有序,分层依赖清晰,无循环嵌套调用,耦合度低
% Comments 0.0% 未识别到有效注释,是主要短板
Classes 17 文件数与类数量基本匹配,基本做到一个文件一个类,无超大聚合类
Methods/Class 3.29 平均每个类仅 3 个左右方法,功能高度聚焦,无臃肿大类
Avg Stmts/Method 5.45 单方法仅 5 行代码,拆分粒度极细,符合短小方法最佳实践,易调试、易复用
Max Complexity 24 全局最高圈复杂度 24,远低于 V2 的 74,仅少量校验、解析方法存在多层判断,无极端复杂巨型分支函数
Avg Complexity 2.54 整体平均圈复杂度仅 2.54,远低于安全阈值 10,整体代码逻辑简单、边界 bug 风险极低
Max Depth 5 6 最深代码嵌套 6 层,相比 V2 的 7 层小幅优化,无多层 if-switch 堆叠
Avg Depth 1.74 平均嵌套深度极低,代码层级扁平,阅读难度小
(3)设计心得:能够检测连线多输入、无输入、无输出、顺序错误和信号冲突等常见问题,并通过工具类完成子电路引脚统一映射,解决命名冲突问题。输入验证更加严格,对所有数值输入都进行了非负检查和范围检查。这个SOLD原则性更强。
三、采坑心得
1.作业一
(1)第一次提交出现编译错误
原因是没有打好括号。Main的函数缺少半个括号,类太多,容易繁杂。
(2)第二次,非0返回
我的:
public class Main{
public class Cargo{ ... }
public class Flight{ ... }
}
实际上:所有类要分开
我的:
public Flight(String fn, double maxw){
this.fn = fn;
this.maxw = maxw;
this.manifest = new LoadManifest(this); // 这里会提前创建
}
实际上:导致对象依赖混乱,容易空指针、程序崩溃。
我的:
虽然加了sc.nextLine(),但因为位置不对,会吞掉换行符。
(3)第三次,排序的选择问题
我的:冒泡排序
后来:选择排序
选择排序通常比冒泡排序效率更高,因为它减少了元素交换的次数
并且两个的输出结果会不同
一代码:超载时输出:配载状态:严重超载
二代码:超载时输出:警告:严重超载
说明第一个代码的输出写错了
但是我但是觉得奇怪的是,我用冒泡没通过,但是改成了选择排序又通过了,这里有边界值限定在这个了。
(2)冒泡排序导致顺序错误
for(int i=0;i<list.size();i++){
for(int j=0;j<list.size()-1-i;j++){
if(list.get(j).num>list.get(j+1).num){
swap();
}
}
}
同优先级、同编号排序不稳定,和题目指定顺序不一致导致答案错误。
2.作业二
(1)for(Component c : allComp){
c.compute(values);
}
迭代更新时一边读旧电平、一边写入新电平,同一轮循环前后取值不一致,电路稳态计算失真,缺少快照隔离机制
3.作业三
(1)第一次:
我的:if(checkMultiSource()) return err;
if(checkOrderError()) return err;
实际上:if(checkMultiSource()) return err;
if(checkOrderError()) return err;
(2)第二次:前舱容量不足测试
我的:
int num = Integer.parseInt(name.substring(1));
Gate g = new AndGate(prefix,name,num,2);
未定义常量MIN_GATE_ID = 1,未拦截 0、负数编号,构造时数组下标越界
4.总结:
(1)在输入数据是要注意换行符会不会吞,已经多次出现。
(2)测试点通常会有很多的边界点,这些是最容易出现问题的,我们要考虑有没有可能,四舍五入会影响精度。
(3)输出的格式会影响,还是要对照题目,不要定向思维。
(4)不同的排序方式之间还事有不同。
四、改进建议
1.作业一
(1)Scanner 读取输入只写了 nextLine,但放置位置在数字读取后,nextInt 不会吸收换行,后续读取字符串直接读到空字符串,引脚名称解析失败,元件创建不全,最好还是把nextLine()加上
(2)相对来说,类的设计没弄好。
(3)最初采用冒泡排序对门元件排序,还是要换成题目匹配的类型优先级 + 编号升序排序
2.作业二
(1)所有九类元件共用 Component 类,compute 方法中大量 switch 分支,异或、与门、或门,会容易导致引脚读取索引写错,运算结果和标准电平不一致。
(2)元件参数没有数值范围校验,控制位参数为 0 时没有拦截,译码器、多路选择器寻址计算出错,输出编号完全错位
3.作业三
(1)最初编写 addInput、addConnection 方法时没有容量、数量判断,无限制存入引脚与连线,也没做长度校验,修正后增加容器存储前置判断,存入前先判断集合大小、连线元素数量,不符合规范直接抛出对应错误提示。
(2)你使用了 +1e-6 来避免精度误差,
但可以统一封装,提高代码可读性
五、总结
1.学习收获
通过这三次作业,我系统地学习了面向对象设计的基本原则和实践方法,特别是SLOD原则的应用和类图的使用。我掌握了如何进行类的设计和划分,如何处理类间的关系(组合、聚合、依赖),以及如何实现复杂的业务逻辑,也学习了一些电路的相关知识。
(1)在技术方面,我学会了:
如何使用 Java 集合框架实现元件、子电路、连线数据的批量管理
如何实现状态迭代仿真、拓扑信号传递、优先级校验等核心算法
如何通过正则表达式解析复杂结构化输入并规范处理各类输入格式
如何分层设计程序结构,统一处理程序运行异常与题目自定义业务异常
如何按照工程规范、组件顺序、排序规则编写高可读性、可迭代、可维护的 Java 代码
在业务方面,我了解了数字电路组合逻辑的基本工作原理,也学会应用于代码中
(2)在这个作业,当中认识了电子原件,拓展了新知识。同时,更加进一步明白了测试点的边界的设定。
5.2 进一步学习方向
设计模式*:学习常用的设计模式,如工厂模式、单例模式、观察者模式等,提高代码的可扩展性和可维护性
数据结构与算法:深入学习各种数据结构和算法,提高程序的效率和性能
数据库技术:学习关系型数据库和非关系型数据库的使用,实现数据的持久化存储
5.3 课程建议
我认为平时可以拓展一些其他的有关我们会用到的电路元件,又或者是其他学科的知识,进行一个学科的交叉,拓张我们的眼界,提前接触未来我们可能在工作中会接触到的一些情景。
浙公网安备 33010602011771号