今天花了整整一天的时间,潜心研究了《软件设计》课程的实验一。这个实验内容非常扎实,涵盖了UML类图的复习和两个重要的面向对象设计原则。从“知道”到“理解”,再到亲手“实现”,这个过程让我对软件设计的精髓有了更深的认识。
上午:重温UML类图——程序世界的“地图”
实验的第一个任务是复习UML。如果把软件系统比作一座复杂的城市,那么UML类图就是这座城市的“地图”,它清晰地展示了各个“建筑”(类)之间的关系。
我重新梳理了类与类之间的五种核心关系,并画了简单的示意图来加深记忆:
-
继承(泛化):Dog is an Animal。是一种“is-a”关系,表示子类是父类的一种特化。
-
实现:Bird implements Flyable。类实现接口,承诺提供接口中定义的所有能力。
-
关联:Teacher 和 Course。一种长期的、结构上的关系,比如老师教授课程。
-
依赖:Driver 和 Car。一种临时的、使用上的关系,司机只是暂时使用汽车。
-
聚合/组合:这两者都是特殊的关联关系,强调整体与部分。
◦ 聚合:School 和 Student。部分可以脱离整体而存在,学校解散了,学生依然存在。◦ 组合:House 和 Room。部分与整体同生共死,房子拆了,房间也就不复存在了。
这部分复习为我后续理解设计原则打下了坚实的基础。
下午:实践单一职责原则(SRP)——让每个类“专一”起来
实验二是关于单一职责原则(SRP) 的。这个原则的核心思想是:一个类应该只有一个引起它变化的原因。换句话说,一个类只负责一项职责。
我实现了一个登录模块,这个设计完美体现了SRP:
• User类:只负责封装用户数据(用户名、密码)。
• UserRepository类:只负责与数据库交互,执行SQL查询。
• LoginService类:只负责处理业务逻辑(验证用户名和密码是否匹配)。
• LoginController类:只负责处理用户输入和输出。
这样做的好处太明显了!如果未来要改变数据库(比如从MySQL换到Oracle),我只需要修改UserRepository,而LoginService和LoginController完全不用动。同样,如果要改变登录方式(比如增加验证码),也只需要修改LoginService。代码的维护性和可扩展性大大提高!
在编码过程中,我还练习了Java数据库连接(JDBC),成功连接并查询了实验提供的user表。当看到控制台输出“登录成功!”时,成就感满满!
晚上:领悟依赖倒转与合成复用原则——拥抱“抽象”
实验三是最有挑战性但也最有趣的部分。它要求我们使用依赖倒转原则(DIP) 和合成复用原则(CARP) 来重构一个画笔设计。
-
问题所在:
最初的设计为每一种画笔和颜色的组合都创建一个类(如SmallRedPen, LargeBluePen),这会导致“类爆炸”,难以维护。 -
重构秘诀:
• 合成复用原则:优先使用组合(has-a),而不是继承(is-a)。我们将Color(颜色)作为Pen(画笔)的一个属性,通过组合的方式使用它。
• 依赖倒转原则:依赖于抽象(接口或抽象类),而不是具体实现。我们引入了Color接口,让Pen依赖于这个抽象的接口,而不是具体的RedColor或BlueColor类。
重构后的核心代码思想:
// 抽象:定义颜色行为
public interface Color {
void applyColor();
}
// 具体实现:红色和蓝色
public class RedColor implements Color { ... }
public class BlueColor implements Color { ... }
// 画笔类:通过组合使用颜色,并依赖于Color接口
public abstract class Pen {
protected Color color; // 这里是关键!依赖抽象
public Pen(Color color) { this.color = color; }
public abstract void draw();
}
现在,如果我需要添加一种新的颜色(比如绿色),我只需要创建一个新的GreenColor类实现Color接口即可,完全不需要修改任何Pen相关的代码!同样,添加新画笔类型也很容易。这种设计的灵活性让我真正体会到了面向对象设计的强大威力。
今日总结与感悟
• UML是沟通的桥梁:清晰的UML图是团队协作和软件设计的基石。
• SRP是设计的起点:让代码保持简洁、高内聚的第一道防线。
• DIP和CARP是扩展的利器:通过面向接口编程和组合,可以构建出极其灵活、可扩展的系统。
今天的实验虽然只是模仿,但亲手敲下这些代码,并看到它们按照设计原则优雅地运行,比单纯看书理解深刻得多。路漫漫其修远兮,继续加油!