当前位置: 首页 > news >正文

领域驱动设计中的编程风格选择:面向对象与过程式的平衡艺术

文章目录

  • 零 引言:编程风格的重要性
  • 一 领域对象与数据库的边界
    • 1.1 严格解耦原则
    • 1.2 解耦带来的优势
  • 二 领域服务的职责边界
    • 2.1 读写分离的哲学
    • 2.2 两种风格的权衡
    • 2.3 决策指南
  • 三 对象关联的艺术
    • 3.1 ID引用 vs 对象导航
    • 3.2 性能与表达的平衡
    • 3.3 实践模式
  • 四 领域对象与服务的协作
    • 4.1 逻辑放置的决策
    • 4.2 识别特性依恋
  • 五 封装与继承的明智使用
    • 5.1 封装的艺术
    • 5.2 继承的谨慎使用
  • 六 风格选择的实践指南
    • 6.1 评估维度
    • 6.2 渐进式改进
  • 七 结论:平衡的艺术

零 引言:编程风格的重要性

  • 在软件开发领域,编程风格的选择绝非仅仅是个人偏好的问题,而是直接影响代码质量、可维护性和系统架构的关键决策。如同建筑风格决定了建筑物的外观与功能布局,编程风格决定了软件系统的内部结构和外部行为。
  • 本文将深入探讨领域驱动设计(DDD)中的编程风格选择,特别关注如何在面向对象与过程式风格之间找到平衡点。

一 领域对象与数据库的边界

1.1 严格解耦原则

  • “领域对象不访问数据库” 这一原则是构建清晰架构的基础。想象一下,如果每个业务对象都直接与数据库对话,就像让公司每个部门的员工都直接操作财务系统一样混乱不堪。
  • 在实际项目中,我们经常看到两种违反这一原则的反模式:
  1. 显式访问:在领域对象方法中直接编写SQL语句
// 反例:领域对象中直接执行SQL
public class Order {public void save() {String sql = "INSERT INTO orders (...) VALUES (...)";// 执行SQL...}
}
  1. 隐式访问:使用JPA等ORM框架的延迟加载特性
// 反例:JPA延迟加载导致的隐式数据库访问
@Entity
public class Order {@OneToMany(fetch = FetchType.LAZY)private List<OrderItem> items;public double calculateTotal() {// 当访问items时,会隐式触发数据库查询return items.stream().mapToDouble(Item::getPrice).sum();}
}

1.2 解耦带来的优势

保持领域对象与数据库解耦的主要好处包括:

  • 可测试性:领域对象可以在不依赖数据库的情况下进行单元测试
  • 可维护性:数据库 schema变更不会直接影响业务逻辑
  • 清晰性:业务逻辑与技术实现分离,代码意图更明确

二 领域服务的职责边界

2.1 读写分离的哲学

  • “领域服务只读,应用服务可读写” 这一原则体现了关注点分离的思想。领域服务专注于业务规则验证和决策,而应用服务协调工作流程和技术细节。
  • 典型的领域服务结构:
public class OrderService {private final OrderRepository orderRepository;// 领域服务通常只读public boolean canCancelOrder(OrderId orderId) {Order order = orderRepository.findById(orderId);return order.canBeCancelled();}
}
  • 对应的应用服务可能如下:
public class OrderApplicationService {private final OrderRepository orderRepository;// 应用服务处理写操作public void cancelOrder(OrderId orderId) {Order order = orderRepository.findById(orderId);order.cancel();orderRepository.save(order);}
}

2.2 两种风格的权衡

在项目中,我们通常会遇到两种风格的选择:

  1. 薄应用服务:将更多逻辑放在领域服务中
    • 优点:业务逻辑更集中
    • 缺点:领域服务变得臃肿
  2. 厚应用服务:保持领域服务精简
    • 优点:层次职责更清晰
    • 缺点:业务逻辑可能分散

2.3 决策指南

考虑以下因素来决定风格选择:团队经验水平、项目复杂度、变更频率、性能要求

三 对象关联的艺术

3.1 ID引用 vs 对象导航

  • “用ID表示关联” 这一选择体现了务实的设计哲学。在企业应用中,完全的对象导航可能导致:内存消耗过大,序列化问题,性能瓶颈
  • 对比两种风格:
// 面向对象风格:直接引用对象
public class Order {private Customer customer; // 直接持有对象引用
}// 过程式风格:使用ID引用
public class Order {private CustomerId customerId; // 仅持有ID
}

3.2 性能与表达的平衡

  • 虽然ID引用更高效,但会损失一些表达力。为了弥补这一点,可以:
  1. 提供便捷方法获取完整对象
public class Order {private CustomerId customerId;public Customer getCustomer(CustomerRepository repo) {return repo.findById(customerId);}
}
  1. 使用DTO或视图对象组装完整数据

3.3 实践模式

根据场景灵活选择:

  • 核心领域:优先考虑表达力
  • 外围功能:优先考虑性能
  • 高频操作:使用ID引用
  • 复杂业务逻辑:考虑对象导航

四 领域对象与服务的协作

4.1 逻辑放置的决策

  • “领域对象有自己的领域服务” 这一模式反映了过程式风格的特点。关键在于合理划分逻辑:
  • 领域对象:封装自包含的状态和行为
    public class Order {private OrderStatus status;public boolean canBeCancelled() {return status == OrderStatus.PENDING;}
    }
    
  • 领域服务:处理跨对象的协调和外部依赖
    public class OrderService {public boolean canRequestRefund(OrderId id, RefundPolicy policy) {Order order = orderRepository.findById(id);return order.isPaid() && policy.allowsRefund(order);}
    }
    

4.2 识别特性依恋

  • 特性依恋(Feature Envy)是指一个类过度访问另一个类的数据。重构到表意接口(Intention-Revealing Interface)是解决这一问题的有效方法。
  • 重构前:
// 反例:PaymentProcessor过度访问Order的细节
public class PaymentProcessor {public void process(Order order) {if (order.getItems().size() > 0 && order.getTotal() > 100) {// 处理逻辑...}}
}
  • 重构后:
// 正例:Order提供意图明确的接口
public class Order {public boolean isEligibleForDiscount() {return items.size() > 0 && total > 100;}
}public class PaymentProcessor {public void process(Order order) {if (order.isEligibleForDiscount()) {// 处理逻辑...}}
}

五 封装与继承的明智使用

5.1 封装的艺术

  • 即使在过程式风格中,良好的封装也能显著提升代码质量。关键在于: 隐藏实现细节、提供明确的接口
    、控制修改入口

5.2 继承的谨慎使用

  • 继承是一把双刃剑。在领域模型中,考虑:优先使用组合而非继承、仅当确实存在"is-a"关系时才使用继承、保持继承层次扁平

  • 示例:谨慎的继承使用
// 基类:定义核心行为
public abstract class Payment {protected abstract void executePayment();
}// 派生类:实现特定支付方式
public class CreditCardPayment extends Payment {@Overrideprotected void executePayment() {// 信用卡支付实现}
}

六 风格选择的实践指南

6.1 评估维度


选择编程风格时,考虑以下维度:

  1. 项目规模
    • 小型项目:更灵活的混合风格
    • 大型项目:更严格的分层

  1. 团队组成
    • 新手较多:更明确的过程式风格
    • 经验丰富:更深入的面向对象

  1. 性能需求
    • 高性能:偏向过程式
    • 业务复杂:偏向面向对象

6.2 渐进式改进

不要试图一次性完美应用所有原则。建议:

  1. 从清晰的分层开始
  2. 识别核心领域
  3. 在核心领域应用更纯粹的面向对象
  4. 在非核心区域采用更实用的过程式风格

七 结论:平衡的艺术

编程风格的选择本质上是在多种因素间寻找平衡:

  • 表达力 vs 性能
  • 纯粹性 vs 实用性
  • 灵活性 vs 严谨性

  • 没有放之四海而皆准的最佳实践,关键在于理解每种选择的利弊,根据项目上下文做出明智决策。正如建筑大师密斯·凡·德·罗所说:“魔鬼在细节中”,优秀的软件设计同样体现在对这些风格细节的深思熟虑中。
  • 记住,我们的目标不是追求理论上的完美,而是创建可维护、可扩展且高效的软件系统。希望你能在面向对象与过程式风格之间找到适合您项目的平衡点。
http://www.lqws.cn/news/520165.html

相关文章:

  • 数学:向量的点积是什么?怎么计算?
  • 【EI会议征稿】东北大学主办第三届机器视觉、图像处理与影像技术国际会议(MVIPIT 2025)
  • 服务器开放端口如何设置,本地内网开通应用端口让外网访问连接步骤
  • OpenHarmony构建脚本build.sh解析
  • 【MongoDB】MongoDB从零开始详细教程 核心概念与原理 环境搭建 基础操作
  • 使用EasyExcel处理动态表头数据导入
  • AWS WebRTC:通过shell实现多进程启动viewer
  • Object.assign()
  • 获取YARN application 应用列表的几种方法
  • 2025年Java后端最新面试场景题 + 八股文高频面试题
  • Dagster数据管道构建指南:I/O管理与数据库连接实践
  • React Native【实战范例】账号管理(含转换分组列表数据的封装,分组折叠的实现,账号的增删改查,表单校验等)
  • rules写成动态
  • syncthing忘记密码怎么办(Mac版)?
  • 成都芯谷金融中心·文化科技园打造文化科技高地
  • 微服务思想与C++服务化框架
  • 跟着AI学习C#之项目实践Day7
  • sentinel 自定义 dashboard 用户名密码
  • 第⼀个与⼤模型交互的应⽤
  • Swagger 在 Spring Boot 中的详细使用指南
  • thinkphp8之文件上传
  • 用户体验驱动的3D设计:从功能实现到情感共鸣的设计升级
  • 融合聚类与分类的退役锂电智能分选技术:助力新能源汽车产业可持续发展
  • JVM调优实战 Day 6:JVM性能监控工具实战
  • 数据结构 顺序表与链表
  • python的易家宜超市云购物系统
  • webman 利用tcp 做服务端 对接物联网
  • 使用 Spread.net将 Excel 中的文本拆分为多段
  • 注解+AOP+自动配置实现自定义starter
  • Java8 Stream流:Stream流的思想和获取Stream流