第二次BLOG

(1)前言
这次BLOG针对两次大作业和一次测试,我会分析两次大作业的做法、设计、遇到的问题等等,针对测试检查基础知识。
(2)设计与分析
第一次大作业分析:
image
image

根据以上分析可以得出分析结果:

  1. Main.main() 方法过于复杂
    • 复杂度高达 28(远高于平均复杂度 3.82)
    • 包含 57 条语句
    • 最大块深度达到 7
    • 有 41 个方法调用
    • 这是代码中最严重的问题,违反了单一职责原则
  2. Main.parseGate() 方法也过于复杂
    • 复杂度 9
    • 24 条语句
    • 需要重构简化
  3. 代码注释率偏低
    • 仅 6.2% 的行有注释
    • 建议至少达到 20-30% 的注释率
    以下是这次大作业类图:
    image

Main 类:
• 包含主要的控制逻辑
• 方法:
o parseGate(String)- 解析门电路
o compareGate(Gate, Gate)- 比较门电路
o getGateType(String)- 获取门类型
o main(String[])- 程序入口
o 构造函数
Gate 基类:
• 属性:
o ready(boolean) - 就绪状态
• 方法:
o toString()- 字符串表示
o compute()- 计算逻辑(应该是抽象方法)
o 构造函数
具体门类:
• 都继承自 Gate
• 每个类都有自己的:
o 构造函数
o compute()方法实现
下面是我对这一次作业的改进意见:

  1. 引入工厂模式
    // 建议创建 GateFactory
    class GateFactory {
    public static Gate createGate(String type, int id, int inputCount) {
    switch(type.toLowerCase()) {
    case "xor": return new XorGate(id, inputCount);
    case "xnor": return new XnorGate(id, inputCount);
    // ... 其他类型
    }
    }
    }

  2. 重构 Main 类
    移除 parseGate()和 getGateType()到专门的类
    Main 只保留主流程控制

  3. 完善 Gate 基类
    abstract class Gate {
    protected boolean ready;
    protected int id;
    protected String type;

    public abstract void compute(); // 明确声明为抽象方法

    // 添加通用方法
    public boolean isReady() { return ready; }
    public int getId() { return id; }
    }

  4. 添加比较器接口
    interface GateComparator {
    int compare(Gate g1, Gate g2);
    }

第二次作业:
image
image

主要问题分析

  1. 高复杂度问题(严重)
    • Main.parseComponent() 复杂度高达18(远超推荐值<10)
    • Main.main() 复杂度14,也过高
    • 最大复杂度18表明存在过度复杂的逻辑,可能需要重构
  2. 代码结构问题
    • 方法过长:平均每个方法10.29个语句,部分方法过长
    • 嵌套过深:最大块深度8层,平均2.30层,存在深层嵌套
    • 分支语句占比30.8%,控制流较复杂
  3. 代码可读性问题
    • 注释比例仅9.8%,远低于建议的20-30%
    • 缺乏足够文档说明
    image

Component (抽象基类)
├── 基本门电路 (AndGate, OrGate, NotGate, XorGate, XnorGate)
├── 三态门 (TriStateGate)
├── 组合逻辑器件 (Multiplexer, Decoder, Demultiplexer)
└── Main (控制器/工厂类)
优点:
• ✅ 符合开闭原则:新增组件只需继承Component
• ✅ 统一的接口规范
• ✅ 模板方法模式确保执行流程一致性
2. 工厂模式应用
图中显示多个类有<>标记指向Main,表明:
• Main类承担工厂角色,负责组件创建
• 符合单一职责原则:组件使用与创建分离
3. 继承层次清晰
逻辑门基础 (And/Or/Not) → 复杂门电路 (Xor/Xnor) → 组合器件 (Mux/Decoder)
• 层次结构合理,便于扩展
• 多态性得到充分利用
现存问题的根源:

  1. Main类过重(复杂度18)
    o 原因:承担了parseComponent()和工厂创建双重职责
    o 违反了单一职责原则
  2. 高复杂度方法
    下面是我对这一次作业的改进意见:
    阶段一:立即改进
    // 1. 拆分Main类的职责
    class ComponentFactory { // 专门负责创建
    static Component create(String type, Map<String, Object> params)
    }

class ComponentParser { // 专门负责解析
static Component parse(String input)
}

// 2. Main类只负责协调
class Main {
public static void main(String[] args) {
Component comp = ComponentParser.parse(input);
// ... 使用组件
}
}
阶段二:架构优化
// 1. 引入Builder模式
interface ComponentBuilder {
ComponentBuilder setInputs(Map<String, Integer> inputs);
ComponentBuilder setConfiguration(Map<String, Object> config);
Component build();
}

// 2. 添加配置类
class CircuitConfig {
private Map<String, Component> components;
private Map<String, Connection> connections;
// 支持从文件/字符串加载配置
}
阶段三:设计模式增强
// 1. 策略模式 - 不同的计算策略
interface ComputeStrategy {
Object compute(Map<String, Integer> inputs);
}

// 2. 访问者模式 - 支持电路分析
interface CircuitVisitor {
void visit(AndGate gate);
void visit(OrGate gate);
// ... 其他组件
}
本次测试分析:
Java中,静态方法只能访问静态的成员变量和静态的成员方法。
Java中,子类中所有的构造方法默认都会访问父类中空参数的构造方法。
Java中,接口中可以定义非抽象方法。
Java中,外部类不能用private修饰。
Java中,接口可以定义为抽象接口。例如:public abstract interface Inter{}
Java中,以下代码会抛出异常。

package kaoshi;
public class Demo_Exception {
public static void main(String[] args) {
System.out.println(1.0 / 0);
}
}
Java中,String是关键字。
Java中,主类的静态代码块优先于主方法(main方法)执行。
核心错误原因分析

  1. 绝对化思维
    • 认为"只能"、"都会"、"不能"等绝对描述都是对的
    • 实际上Java有很多特例和边界情况
  2. 忽略版本差异
    • 特别是接口特性,Java 8是重要分水岭
    • 不同版本JDK行为可能不同
  3. 概念混淆
    • 把"可以"和"必须"混淆
    • 把"语法允许"和"最佳实践"混淆

• 警惕绝对词:"只能"、"都会"、"所有"、"一定" → 通常有例外
• 考虑版本:特别是Java 8的特性变化
• 区分编译时/运行时:有些是编译错误,有些是运行时异常
• 动手验证:不确定就写个小程序测试
(3)采坑心得
一次次的"我懂了"和"我又错了"
在完成两次大作业和一次测试的过程中,我深刻体会到编程不仅是要让代码"能跑",更要让代码"优雅"。每一次的踩坑,都是一次宝贵的成长机会。

  1. "代码能跑就行"的陷阱
    第一次大作业时,我抱着完成任务的心态,把所有逻辑都塞进Main.main()方法里。看着584行代码,我甚至还觉得有点自豪——"看我写了这么多!" 坑点体验:
    // 曾经的"得意之作" - 一个近60行的main方法
    public static void main(String[] args) {
    // 读取文件
    // 解析每一行
    // 创建对象
    // 处理输入
    // 计算结果
    // 输出结果
    // ... 还有各种异常处理
    // 当需要修改时:天啊,这里改一点,那边全崩了!
    }
    醒悟时刻:当要求我新增一个门电路类型时,我发现要在几十个if-else中寻找插入点,还要确保不影响到其他逻辑。那一刻,我理解了什么叫"代码耦合"。
  2. 设计模式的"真香"时刻
    在第二次作业中,我尝试用继承结构来组织代码,但还是在Main.parseComponent()中埋下了复杂度18的"地雷"。 教训:
    • 不要因为看起来"简单"就把所有逻辑堆在一起
    • 工厂模式不只是书本上的概念,当你有10种组件要创建时,它会成为你的救星
    • 一个方法超过20行,就该考虑拆分了
    重构前后的对比:
    // 重构前:一个方法做所有事
    public static Component parseComponent(String line) {
    // 解析类型
    // 创建对象
    // 配置参数
    // 设置连接
    // 返回对象
    // 复杂度:18 😱
    }

// 重构后:职责分离
class Parser { /* 只负责解析 / }
class Factory { /
只负责创建 / }
class Configurator { /
只负责配置 */ }
// 每个方法复杂度:<5 😊
3. 测试题的"细节魔鬼"
那些看似简单的判断题,让我明白了Java语言的精妙和"坑点": 浮点数除0的迷惑:
System.out.println(1.0 / 0); // 输出 Infinity
System.out.println(1 / 0); // 抛出 ArithmeticException
同样的除法,整型和浮点型处理完全不同!这个细节让我意识到:计算机是精确的,不理解"大概"。 接口的进化理解: 我曾经坚定地认为"接口只能有抽象方法",直到查阅资料才发现Java 8引入了默认方法。这教会我:技术是发展的,知识需要更新。
4. 注释的"打脸"经历
我总觉得自己代码写得很清楚,不需要注释。但一周后回看自己的代码:
// 这是什么意思?为什么是0x3F?
int mask = 0x3F;

// 这个循环条件为什么这么复杂?
for (int i = (n & 1) == 0 ? 0 : 1; i < arr.length; i += 2) {
// ...
}
心得:注释不是给现在的自己看的,是给未来的自己和接手的同学看的。9.8%的注释率,意味着90%的代码需要别人猜意图。
5. 继承体系的"甜蜜负担"
建立Component继承体系时,我享受到了多态带来的便利:
Component c = new AndGate();
Object result = c.computeOutput(inputs); // 统一的接口
但同时也遇到了问题:
• 基类设计不够完善,有些方法在子类中重复实现
• 新增功能时,需要在所有子类中修改
• 类型转换的混乱
反思:不要为了继承而继承。如果类之间没有真正的"is-a"关系,组合可能比继承更合适。
💡 收获的编程智慧

  1. 复杂度控制是硬功夫
    • 一个方法超过10行,警惕
    • 嵌套超过3层,重构
    • 圈复杂度超过10,必须拆分
  2. 写代码要像写文章
    • 有清晰的章节结构(类和方法)
    • 段落分明(代码块)
    • 注释如同批注,帮助理解
    • 变量名是词汇,要准确达意
  3. 测试是最好的老师
    • 边界条件总会找到你的漏洞
    • 异常处理不是可有可无的装饰
    • 别人的用例能发现你的盲区
  4. 设计需要远见
    不要只考虑当前需求,思考:
    • 如果要加新功能,改动有多大?
    • 别人能看懂我的设计吗?
    • 这个设计能应对变化吗?
    🎯 未来编程的"避坑指南"
    基于这些经验,我给自己制定了编程规范:
  5. 单一职责原则:每个方法只做一件事,每个类只有一个变化原因
  6. 复杂度监控:写完后用工具分析圈复杂度,超过7就重构
  7. 注释三要素:为什么这么做、特殊逻辑说明、TODO标记
  8. 防御性编程:考虑所有异常情况,包括"不可能"发生的情况
  9. 持续学习:语言特性、设计模式、最佳实践要不断更新
    🌟 最后的感悟
    编程就像搭积木,初学者只追求"能立起来",有经验者考虑"结构稳固",高手思考"易于修改和扩展"。两次大作业和一次测试,让我从"能跑就行"走向了"优雅设计"。 最大的收获不是完成了作业,而是建立了对代码质量的敏感度。现在看到复杂的代码,我会本能地思考:"这里能拆分成多个方法吗?""这个设计符合开闭原则吗?" 踩过的坑,都是成长的台阶。感谢这些"痛苦"的重构经历,它们让我成为了更好的程序员。记住:好代码不是写出来的,是改出来的。

(4)总结:
这个数字电路仿真系统展示了良好的抽象能力和合理的继承设计,但同时也暴露出常见的初学者架构问题:

  1. "上帝类"反模式:Main类承担过多职责
  2. 硬编码依赖:组件创建逻辑固化
  3. 缺乏配置性:调整参数需要修改代码
  4. 注释不足:可读性和可维护性受影响
    最大的收获:通过这个项目,我深刻理解了:
    • 类图不仅是设计工具,更是思考工具
    • 复杂度度量不仅是数字,更是质量信号
    • 设计模式不仅是理论,更是实践指南
    最终建议:采用渐进式重构,先解决Main类的职责过重问题,再逐步引入工厂模式、配置管理等高级特性。这样既能保证系统稳定性,又能持续提升架构质量。 这个项目不仅是数字电路仿真的实现,更是面向对象设计思想的实践演练。从"能运行"到"设计优雅",这是每个程序员成长的必经之路。
posted @ 2025-12-13 17:15  段乐康  阅读(4)  评论(0)    收藏  举报