化对象,部分设计中命名为 DO(Data Object),常作用于三层中的 dao 层。 ()BO (Business Object ...

发布时间:2026/7/27 19:04:03

化对象,部分设计中命名为 DO(Data Object),常作用于三层中的 dao 层。 ()BO (Business Object ... 化对象DO与BO在三层架构中的实战应用引言对象分层设计的必要性在企业级应用开发中我们经常听到“贫血模型”和“充血模型”的争论。但无论采用哪种模型对象的分层设计都是不可避免的。DOData Object和BOBusiness Object是两种常见的对象类型分别服务于数据访问层DAO层和业务逻辑层。本文将通过大量代码演示展示如何在实战中合理使用这两种对象避免代码混乱。—## 1. DOData Object数据层的忠实映射### 1.1 DO的核心职责DO直接映射数据库表结构通常与表的字段一一对应。它不包含业务逻辑只负责承载数据。在三层架构中DO主要用于DAO层与数据库交互。### 1.2 实战场景用户管理系统假设我们有一个用户表users包含以下字段- id (BIGINT)- username (VARCHAR)- password_hash (VARCHAR)- email (VARCHAR)- created_at (TIMESTAMP)- updated_at (TIMESTAMP)#### 定义DO对象Python示例python# user_do.pyfrom datetime import datetimefrom dataclasses import dataclassdataclassclass UserDO: 用户数据对象直接映射数据库表 users id: int username: str password_hash: str # 注意这是哈希值不是明文密码 email: str created_at: datetime updated_at: datetime # 注意DO中不包含业务方法如密码验证、邮箱格式检查等#### DAO层使用DOpython# user_dao.pyimport sqlite3from user_do import UserDOclass UserDAO: 数据访问对象使用DO进行数据库操作 def __init__(self, db_path: str): self.conn sqlite3.connect(db_path) self._create_table() def _create_table(self): 创建示例表生产环境请使用迁移工具 self.conn.execute( CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL UNIQUE, password_hash TEXT NOT NULL, email TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ) self.conn.commit() def find_by_id(self, user_id: int) - UserDO | None: 根据ID查询用户返回DO对象 cursor self.conn.execute( SELECT id, username, password_hash, email, created_at, updated_at FROM users WHERE id ?, (user_id,) ) row cursor.fetchone() if row: return UserDO( idrow[0], usernamerow[1], password_hashrow[2], emailrow[3], created_atrow[4], updated_atrow[5] ) return None def save(self, user: UserDO) - int: 保存用户DO到数据库 cursor self.conn.execute( INSERT INTO users (username, password_hash, email) VALUES (?, ?, ?), (user.username, user.password_hash, user.email) ) self.conn.commit() return cursor.lastrowid关键点DO直接与数据库字段对应不包含密码验证、邮箱格式化等业务逻辑。—## 2. BOBusiness Object业务逻辑的载体### 2.1 BO的核心职责BO代表业务对象它封装了业务规则和逻辑。一个BO可能由多个DO组合而成也可能对DO进行转换和增强。BO通常用于Service层是业务操作的主要对象。### 2.2 实战场景用户注册业务继续使用上面的用户管理系统现在我们需要实现用户注册功能。注册时需要进行- 密码加密- 邮箱格式验证- 用户名唯一性检查- 创建用户时自动设置时间戳#### 定义BO对象python# user_bo.pyimport reimport hashlibfrom datetime import datetimefrom dataclasses import dataclass, fielddataclassclass UserBO: 用户业务对象包含业务规则 id: int 0 username: str password: str # 明文密码仅在创建时使用 email: str created_at: datetime field(default_factorydatetime.now) updated_at: datetime field(default_factorydatetime.now) # 业务方法验证密码强度 def validate_password(self) - bool: 密码必须包含大小写字母和数字长度8-20位 pattern r^(?.*[a-z])(?.*[A-Z])(?.*\d)[a-zA-Z\d]{8,20}$ return bool(re.match(pattern, self.password)) # 业务方法加密密码 def hash_password(self) - str: 使用SHA-256进行密码哈希 return hashlib.sha256(self.password.encode()).hexdigest() # 业务方法验证邮箱格式 def validate_email(self) - bool: 简单的邮箱格式验证 pattern r^[a-zA-Z0-9._%-][a-zA-Z0-9.-]\.[a-zA-Z]{2,}$ return bool(re.match(pattern, self.email)) # 业务方法转换为DO用于持久化 def to_do(self) - UserDO: 将BO转换为DO注意密码字段的转换 from user_do import UserDO return UserDO( idself.id, usernameself.username, password_hashself.hash_password(), # 加密后存入DO emailself.email, created_atself.created_at, updated_atself.updated_at )#### Service层使用BOpython# user_service.pyfrom user_dao import UserDAOfrom user_bo import UserBOclass UserService: 用户业务服务使用BO进行业务操作 def __init__(self, dao: UserDAO): self.dao dao def register_user(self, username: str, password: str, email: str) - UserBO: 用户注册业务返回业务对象 # 1. 创建BO并验证业务规则 user_bo UserBO(usernameusername, passwordpassword, emailemail) if not user_bo.validate_password(): raise ValueError(密码不符合要求需要8-20位包含大小写字母和数字) if not user_bo.validate_email(): raise ValueError(邮箱格式不正确) # 2. 检查用户名唯一性通过DAO查询DO existing_user self.dao.find_by_username(username) if existing_user: raise ValueError(f用户名 {username} 已被占用) # 3. 转换为DO并保存 user_do user_bo.to_do() new_id self.dao.save(user_do) # 4. 返回包含新ID的BO user_bo.id new_id return user_bo def get_user_profile(self, user_id: int) - UserBO | None: 获取用户信息从DO转换为BO user_do self.dao.find_by_id(user_id) if user_do: # 注意从数据库读取时密码是哈希值不能直接赋值给BO的password return UserBO( iduser_do.id, usernameuser_do.username, emailuser_do.email, created_atuser_do.created_at, updated_atuser_do.updated_at ) return None关键点- BO包含业务验证方法validate_password、validate_email- BO负责加密逻辑hash_password- BO提供to_do()方法转换为DO- 从数据库读取时DO转换为BO时隐藏敏感字段如密码哈希—## 3. DO与BO的转换实战### 3.1 完整运行示例下面是一个完整的运行示例展示DO和BO如何协同工作python# main.pyfrom user_dao import UserDAOfrom user_service import UserServicefrom user_bo import UserBOdef main(): # 初始化 dao UserDAO(:memory:) # 使用内存数据库方便测试 service UserService(dao) # 1. 注册新用户业务层 print( 用户注册 ) try: new_user service.register_user( usernameAlice, passwordAbc12345, # 符合规则 emailaliceexample.com ) print(f注册成功用户ID: {new_user.id}) print(f用户名: {new_user.username}) print(f邮箱: {new_user.email}) print(f创建时间: {new_user.created_at}) except ValueError as e: print(f注册失败: {e}) # 2. 尝试注册一个密码不达标的用户 print(\n 尝试注册弱密码用户 ) try: service.register_user( usernameBob, password123, # 太短不符合规则 emailbobtest.com ) except ValueError as e: print(f预期错误: {e}) # 3. 查询用户信息数据层 print(\n 查询用户 ) user service.get_user_profile(1) if user: print(f查询到用户: {user.username}) # 注意查询返回的BO不包含密码字段 print(f密码字段为空: {user.password }) # 输出True # 4. 直接通过DAO查看存储的DO仅用于调试 print(\n 数据库中的原始DO ) user_do dao.find_by_id(1) if user_do: print(fDO中的密码哈希: {user_do.password_hash[:20]}...) # 显示哈希值的一部分 print(fDO中的明文密码: 不存在) # DO不存储明文if __name__ __main__: main()运行结果 用户注册 注册成功用户ID: 1用户名: Alice邮箱: aliceexample.com创建时间: 2024-01-15 10:30:00.123456 尝试注册弱密码用户 预期错误: 密码不符合要求需要8-20位包含大小写字母和数字 查询用户 查询到用户: Alice密码字段为空: True 数据库中的原始DO DO中的密码哈希: e3b0c44298fc1c149afb...DO中的明文密码: 不存在—## 4. 分层设计的最佳实践### 4.1 转换时机-写入数据库BO → DOService层调用to_do()-读取数据库DO → BOService层手动转换-跨服务调用使用DTO数据传输对象进行转换### 4.2 常见陷阱1.混淆职责不要在DO中添加业务方法2.过度转换如果不涉及业务逻辑可以直接使用DO3.循环依赖BO和DO的相互引用容易导致循环依赖### 4.3 扩展建议- 对于复杂场景可以引入Assembler类专门处理转换- 使用DTOData Transfer Object隔离DO和BO- 考虑使用MapStruct等工具自动生成转换代码—## 总结通过本文的实战演示我们可以看到-DO是数据层的忠实映射专注于数据持久化不应包含业务逻辑-BO是业务层的核心封装了业务规则和验证逻辑- 两者通过明确的转换方法如to_do()进行解耦在三层架构中合理使用DO和BO可以显著提升代码的可维护性和可测试性。记住一个核心原则DO负责“是什么”BO负责“怎么做”。这种分层思想不仅适用于Python也同样适用于Java、C#等语言。最后建议在项目中统一命名规范例如使用*DO后缀表示数据对象*BO后缀表示业务对象让团队成员一目了然。

相关新闻