6.3 KiB
图形面积计算器重构 - 实验报告
一、实验概述
实验目的
通过重构图形面积计算器,理解面向对象编程中的抽象类、继承和多态概念,比较组合与继承的优缺点。
实验背景
原有 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() 方法
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 关键字实现
本项目使用继承的原因:
// 继承关系:圆 是一种 图形
Circle extends Shape
// 继承关系:矩形 是一种 图形
Rectangle extends Shape
// 继承关系:三角形 是一种 图形
Triangle extends Shape
继承的优点: ✅ 代码复用:子类自动拥有父类的属性和方法 ✅ 多态性:可以用父类类型统一处理子类 ✅ 符合自然逻辑:圆确实是一种图形
继承的缺点: ❌ 耦合度高:父类改变会影响所有子类 ❌ 破坏封装:子类可以访问父类的 protected 成员 ❌ 不灵活:继承关系在编译时确定,运行时无法改变
2. 组合(Composition)
是什么:
- 类与类之间的 has-a 关系
- 一个类包含另一个类的对象作为成员
- 用成员变量实现
组合的例子:
// 组合关系:汽车 有一个 引擎
public class Car {
private Engine engine; // 组合
public Car() {
this.engine = new Engine();
}
}
public class Engine {
// 引擎的功能
}
组合的优点: ✅ 低耦合:类之间相对独立 ✅ 灵活性:运行时可以替换组合的对象 ✅ 符合设计原则:优先使用组合而非继承
组合的缺点: ❌ 代码较多:需要显式调用组合对象的方法 ❌ 不能直接复用:无法自动继承功能
3. 选择原则:什么时候用继承,什么时候用组合?
| 场景 | 推荐使用 | 原因 |
|---|---|---|
| is-a 关系(是一种) | 继承 | 圆是一种图形,符合继承逻辑 |
| has-a 关系(有一个) | 组合 | 汽车有一个引擎,使用组合 |
| 需要代码复用 | 继承 | 子类可以复用父类代码 |
| 需要灵活性 | 组合 | 运行时可以动态改变 |
| 需要多态 | 继承 | 父类引用指向子类对象 |
4. 本项目为什么选择继承?
分析:
-
is-a 关系明确:
- 圆 是一种 图形 ✅
- 矩形 是一种 图形 ✅
- 三角形 是一种 图形 ✅
-
需要多态:
- ShapeUtil.printArea(Shape shape) 需要统一处理
- 用 Shape 类型可以接受任何图形
-
代码复用:
- 所有图形都有 name 属性
- 继承避免重复代码
如果用组合会怎样?
// 不推荐的组合方式
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日