2025/9/15日 每日总结 UML与面向对象程序设计原则实践指南
UML与面向对象程序设计原则实践指南
在面向对象编程的学习旅程中,UML类图设计和设计原则的应用是提升代码质量与可维护性的核心。本文将通过三个实战实验,详细拆解类与类的关系、单一职责原则、依赖倒转原则及合成复用原则的具体应用,结合代码实例与可视化展示,带你吃透这些关键知识点。
一、UML类图核心:类与类的六种关系
UML类图是面向对象设计的可视化工具,其中类与类的关系定义了系统的结构骨架。以下是六种核心关系的详细解析与实例说明:
1. 继承关系(Inheritance)
继承描述子类复用父类功能并扩展自身特性的关系,体现"is-a"逻辑。
-
UML表示:带空心三角箭头的实线,箭头从子类指向父类
-
核心特点:子类拥有父类的属性和方法,可重写或扩展
-
实例场景:
Class_B继承Class_A,获得其基础功能并新增专属方法
2. 实现关系(Implementation)
实现是类与接口之间的绑定关系,类承诺实现接口定义的所有方法。
-
UML表示:带空心三角箭头的虚线,箭头从实现类指向接口
-
核心特点:接口定义规范,类提供具体实现,支持多实现
-
实例场景:
Class_A实现Inte接口,完成接口要求的所有抽象方法3. 依赖关系(Dependency)
依赖是一种临时的、弱关联关系,一个类通过局部变量、参数等方式使用另一个类。
-
UML表示:带箭头的虚线,箭头从依赖类指向被依赖类
-
核心特点:关系具有偶然性,被依赖类变化会影响依赖类
-
实例场景:
Class_A的depend方法以Class_B为参数,临时使用其功能4. 关联关系(Association)
关联是语义级别的强依赖,体现两个类长期的平等关系,可双向或单向。
-
UML表示:带箭头的实线,可标注角色和多重性(如0..1、0..*)
-
核心特点:被关联类以属性形式存在于关联类中,关系长期稳定
-
实例场景:
Class_A包含Class_B类型的属性,两者形成长期关联5. 聚合关系(Aggregation)
聚合是关联的特例,体现"整体-部分"的可分离关系,部分可属于多个整体。
-
UML表示:空心菱形+实线箭头,菱形在整体类一端
-
核心特点:整体与部分生命周期独立,部分可被共享
-
实例场景:团队(整体)与成员(部分),成员可加入多个团队
6. 组合关系(Composition)
组合是更强的聚合关系,体现"整体包含部分"的不可分离关系。
-
UML表示:实心菱形+实线箭头,菱形在整体类一端
-
核心特点:整体生命周期决定部分生命周期,部分不可独立存在
-
实例场景:人体(整体)与心脏(部分),心脏无法脱离人体存在
二、单一职责原则:登录注册模块重构
单一职责原则(SRP)要求一个类只负责一项职责,降低类的复杂度,提高可维护性。以下是基于该原则实现的用户认证系统。
设计思路
将用户认证流程拆分为6个核心职责,每个职责由独立类承担:
-
输入验证:负责用户名、密码、邮箱的格式校验
-
认证服务:处理用户登录逻辑
-
注册服务:处理用户注册逻辑
-
数据访问:负责用户数据的存储与查询
-
日志记录:记录系统操作日志
-
界面交互:处理页面展示与用户操作响应
核心代码实现
1. 输入验证类(InputValidator)
class InputValidator {
validateUsername(username, isRegister = false) {
if (!username) return { isValid: false, message: '用户名不能为空' };
if (username.length < 3 || username.length > 20)
return { isValid: false, message: '用户名长度需在3-20字符之间' };
if (!/^[a-zA-Z0-9_]+$/.test(username))
return { isValid: false, message: '用户名只能包含字母、数字和下划线' };
return { isValid: true, message: '' };
}
validateEmail(email) {
if (!email) return { isValid: false, message: '邮箱不能为空' };
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) return { isValid: false, message: '请输入有效的邮箱地址' };
return { isValid: true, message: '' };
}
validatePassword(password, isRegister = false) {
if (!password) return { isValid: false, message: '密码不能为空' };
if (password.length < 6) return { isValid: false, message: '密码长度不能少于6个字符' };
if (isRegister && !/(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/.test(password))
return { isValid: false, message: '密码必须包含大小写字母和数字' };
return { isValid: true, message: '', strength: this.getPasswordStrength(password) };
}
// 其他验证方法...
}
2. 数据访问类(UserRepository)
class UserRepository {
constructor() {
this.users = JSON.parse(localStorage.getItem('users') || '[]');
}
async findByUsername(username) {
return new Promise((resolve) => {
setTimeout(() => {
const user = this.users.find(u => u.username === username);
resolve(user || null);
}, 100);
});
}
async createUser(userData) {
return new Promise((resolve) => {
setTimeout(() => {
const newUser = { ...userData, id: Date.now() + Math.random() };
this.users.push(newUser);
this.saveToStorage();
resolve(newUser);
}, 100);
});
}
saveToStorage() {
localStorage.setItem('users', JSON.stringify(this.users));
}
}
3. 数据库设计(SQL)
CREATE DATABASE IF NOT EXISTS auth_system;
USE auth_system;
-- 用户表:仅存储用户核心信息,遵循单一职责
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
功能特性
-
支持登录/注册切换,表单验证实时反馈
-
密码强度检测(弱/中/强),注册密码复杂度校验
-
本地存储用户数据,支持用户唯一性校验
-
完整的操作日志记录,便于问题排查
三、依赖倒转与合成复用原则:画笔软件重构
在复杂系统设计中,不当的继承会导致"类爆炸"问题。通过依赖倒转原则(DIP)和合成复用原则(CRP),可实现灵活、可扩展的设计。
问题分析
原始设计中,每种"尺寸+颜色"的组合都需要一个独立类(如SmallRedPen、BigGreenPen),新增尺寸或颜色会导致类数量急剧增加。重构目标是通过抽象与组合解决该问题。
设计思路
-
抽象提取:将画笔的可变属性(尺寸、颜色)抽象为独立接口
-
组合替代继承:画笔类通过组合尺寸和颜色对象实现功能,而非继承
-
依赖抽象:所有依赖均指向抽象类,不依赖具体实现
核心代码实现
1. 抽象类定义
// 尺寸抽象类
public abstract class Size {
public abstract void sizePen();
}
// 颜色抽象类
public abstract class Color {
public abstract void colorPen();
}
2. 具体实现类
// 尺寸实现
public class SmallPen extends Size {
public void sizePen() { System.out.println("小型"); }
}
public class MiddlePen extends Size {
public void sizePen() { System.out.println("中型"); }
}
// 颜色实现
public class GreenPen extends Color {
public void colorPen() { System.out.println("绿色"); }
}
public class RedPen extends Color {
public void colorPen() { System.out.println("红色"); }
}
3. 画笔类(组合模式)
public class Pen {
private Size size;
private Color color;
// setter/getter方法
public void setSize(Size size) { this.size = size; }
public void setColor(Color color) { this.color = color; }
// 组合调用
public void draw() {
size.sizePen();
color.colorPen();
System.out.println("画笔绘制中...");
}
}
4. 测试代码
public class MainClass {
public static void main(String[] args) {
Pen pen = new Pen();
// 组合小型红色画笔
pen.setSize(new SmallPen());
pen.setColor(new RedPen());
pen.draw(); // 输出:小型 红色 画笔绘制中...
// 组合大型绿色画笔
pen.setSize(new BigPen());
pen.setColor(new GreenPen());
pen.draw(); // 输出:大型 绿色 画笔绘制中...
}
}
重构优势
-
避免类爆炸:新增尺寸或颜色只需添加对应实现类,无需修改现有代码
-
灵活性提升:运行时可动态切换画笔的尺寸和颜色
-
低耦合:依赖抽象接口,具体实现可独立变化
-
符合开闭原则:扩展新功能无需修改原有代码
可视化演示
通过HTML+JavaScript实现了交互式画笔演示,可选择不同尺寸和颜色进行绘制,直观展示组合模式的灵活性。核心实现逻辑与Java版本一致,通过组合Size和Color对象动态配置画笔属性。
四、总结与思考
面向对象设计原则并非孤立存在,而是相互配合、相辅相成的:
-
UML类图是设计的基础,清晰的关系定义能减少后期重构成本
-
单一职责原则是降低复杂度的关键,让每个组件各司其职
-
依赖倒转与合成复用原则是解决扩展性问题的核心,避免继承带来的紧耦合
在实际开发中,应避免过度设计,根据项目规模和需求灵活应用设计原则。小项目可适当简化,大型项目则需严格遵循设计规范,为后续维护和扩展奠定基础。
通过本次实验,深刻体会到"设计优于编码"的理念——良好的设计能让代码更具可读性、可维护性和扩展性,这也是每个开发者成长路上的必备技能。

浙公网安备 33010602011771号