# 图形面积计算器重构 - 实验报告 ## 一、实验概述 ### 实验目的 通过重构图形面积计算器,理解面向对象编程中的抽象类、继承和多态概念,比较组合与继承的优缺点。 ### 实验背景 原有 Circle、Rectangle、Triangle 类独立存在,无法统一处理。通过引入抽象类 Shape,实现代码的统一管理和复用。 --- ## 二、AI使用情况记录 ### 1. AI协助项目创建 - **创建项目结构**:通过 Trae IDE 环境快速创建项目目录 - **文件生成**:使用代码生成工具创建 6 个 Java 类文件 - **编译运行**:使用终端工具编译和测试程序 ### 2. AI代码协助 - **抽象类设计**:协助设计 Shape 抽象类,包含抽象方法 getArea() - **继承关系**:实现 Circle、Rectangle、Triangle 继承 Shape - **多态实现**:设计 ShapeUtil 的 printArea() 方法 - **类图绘制**:生成 UML 类图描述 ### 3. AI功能总结 | 功能 | 使用情况 | |------|---------| | 文件创建 | ✅ 自动创建 7 个文件 | | 代码生成 | ✅ 生成完整 Java 代码 | | 编译运行 | ✅ 自动编译和测试 | | 文档生成 | ✅ 生成类图和报告 | --- ## 三、核心设计思路 ### 1. 抽象类 Shape 的设计 **为什么使用抽象类**: - 定义统一接口:所有图形都有 getArea() 方法 - 强制子类实现:抽象方法确保每个图形都必须计算面积 - 代码复用:共享 name 属性和 getName() 方法 ```java public abstract class Shape { private String name; public Shape(String name) { this.name = name; } public abstract double getArea(); } ``` ### 2. 继承关系设计 **Circle、Rectangle、Triangle 继承 Shape**: - **is-a 关系**:圆是一种图形,矩形是一种图形,三角形是一种图形 - **复用代码**:继承 name 属性,无需重复定义 - **实现多态**:每个图形按自己的方式计算面积 ### 3. 多态的应用 **ShapeUtil.printArea(Shape shape)**: - 参数是 Shape 类型 - 可以接受 Circle、Rectangle、Triangle 任何子类 - 自动调用对应子类的 getArea() 方法 --- ## 四、组合 vs 继承 ### 问题:组合 vs 继承,什么时候用哪个? ### 1. 继承(Inheritance) **是什么**: - 类与类之间的 is-a 关系 - 子类继承父类的属性和方法 - 用 extends 关键字实现 **本项目使用继承的原因**: ```java // 继承关系:圆 是一种 图形 Circle extends Shape // 继承关系:矩形 是一种 图形 Rectangle extends Shape // 继承关系:三角形 是一种 图形 Triangle extends Shape ``` **继承的优点**: ✅ 代码复用:子类自动拥有父类的属性和方法 ✅ 多态性:可以用父类类型统一处理子类 ✅ 符合自然逻辑:圆确实是一种图形 **继承的缺点**: ❌ 耦合度高:父类改变会影响所有子类 ❌ 破坏封装:子类可以访问父类的 protected 成员 ❌ 不灵活:继承关系在编译时确定,运行时无法改变 --- ### 2. 组合(Composition) **是什么**: - 类与类之间的 has-a 关系 - 一个类包含另一个类的对象作为成员 - 用成员变量实现 **组合的例子**: ```java // 组合关系:汽车 有一个 引擎 public class Car { private Engine engine; // 组合 public Car() { this.engine = new Engine(); } } public class Engine { // 引擎的功能 } ``` **组合的优点**: ✅ 低耦合:类之间相对独立 ✅ 灵活性:运行时可以替换组合的对象 ✅ 符合设计原则:优先使用组合而非继承 **组合的缺点**: ❌ 代码较多:需要显式调用组合对象的方法 ❌ 不能直接复用:无法自动继承功能 --- ### 3. 选择原则:什么时候用继承,什么时候用组合? | 场景 | 推荐使用 | 原因 | |------|----------|------| | **is-a 关系**(是一种) | 继承 | 圆是一种图形,符合继承逻辑 | | **has-a 关系**(有一个) | 组合 | 汽车有一个引擎,使用组合 | | **需要代码复用** | 继承 | 子类可以复用父类代码 | | **需要灵活性** | 组合 | 运行时可以动态改变 | | **需要多态** | 继承 | 父类引用指向子类对象 | --- ### 4. 本项目为什么选择继承? **分析**: 1. **is-a 关系明确**: - 圆 是一种 图形 ✅ - 矩形 是一种 图形 ✅ - 三角形 是一种 图形 ✅ 2. **需要多态**: - ShapeUtil.printArea(Shape shape) 需要统一处理 - 用 Shape 类型可以接受任何图形 3. **代码复用**: - 所有图形都有 name 属性 - 继承避免重复代码 **如果用组合会怎样?** ```java // 不推荐的组合方式 public class Circle { private Shape shape; // 组合 public Circle() { this.shape = new Shape("圆"); } } ``` 这样就失去了多态的优势,不适合本项目。 --- ## 五、实验总结 ### 1. 完成的工作 ✅ 创建抽象类 Shape,包含抽象方法 getArea() ✅ 让三个图形继承 Shape,实现面积计算 ✅ 编写 ShapeUtil,提供 printArea() 方法 ✅ 绘制完整的 UML 类图 ✅ 完成实验报告,记录 AI 使用情况 ✅ 回答"组合 vs 继承"问题 ### 2. 学到的知识点 - 抽象类的定义和使用 - 继承关系的实现 - 多态的实际应用 - 组合与继承的对比 - 面向对象设计原则 ### 3. 代码验证结果 程序运行成功,输出: ``` ========== 图形面积计算器测试 ========== --- 圆形测试 --- 图形名称: 圆 面积: 78.53981633974483 ---------------------- --- 矩形测试 --- 图形名称: 矩形 面积: 24.0 ---------------------- --- 三角形测试 --- 图形名称: 三角形 面积: 20.0 ---------------------- ========== 测试完成 ========== ``` --- ## 六、反思 ### 1. 继承的使用场景 通过本次实验,我深刻理解了继承的适用场景: - 当存在明确的 is-a 关系时 - 当需要多态性时 - 当需要代码复用时 ### 2. 设计原则的重要性 "优先使用组合而非继承"是重要的设计原则,但不是绝对的。关键是根据具体场景选择合适的方式。 ### 3. AI 工具的价值 AI 工具在代码生成、项目搭建、文档编写等方面大大提高了效率,但核心的设计思路和概念理解还需要自己掌握。 --- **实验完成日期**:2026年3月28日