Java 迪米特法则 3 大典型误用场景:从 2 个反例到 1 个正解
Java 迪米特法则实战:3个典型误用场景与重构方案
引言:为什么你的代码总是"知道太多"?
在Java开发中,我们经常遇到这样的困境:明明只是调用一个简单的方法,却需要了解整个调用链路上所有对象的内部结构。这种"过度了解"不仅增加了代码的维护成本,还让系统变得脆弱不堪。迪米特法则(Law of Demeter)正是解决这一问题的金钥匙——它要求每个对象只与"直接朋友"交谈,而不要和"陌生人"说话。
想象一下这样的场景:你需要从图书馆借一本书,图书管理员却要求你先了解这本书的印刷厂、纸张供应商和运输路线。这显然不合理!同样,在代码中,一个类也不应该了解它依赖对象的所有细节。本文将揭示Java项目中三种最常见的迪米特法则误用模式,并通过实际案例展示如何重构为优雅的解决方案。
1. 方法链式调用:层层深入的耦合陷阱
反例:深度方法调用链
// 违反迪米特法则的典型例子
public class OrderService {
public void processOrder(Order order) {
Customer customer = order.getCustomer();
Address address = customer.getAddress();
String city = address.getCity();
// 使用city进行业务处理...
}
}
这段代码看似合理,实则违反了迪米特法则。 OrderService 为了获取城市信息,需要依次通过 Order → Customer → Address → getCity() 的调用链。这种"火车残骸"式的代码(称为"Train Wreck")存在几个严重问题:
- 过度暴露实现细节 :上层服务需要了解下层所有对象的结构
- 脆弱性 :中间任何一个环节的修改都会导致调用方代码变更
- 难以测试 :需要mock整个对象链才能进行单元测试
重构方案:封装完整行为
// 遵循迪米特法则的重构版本
public class Order {
public String getCustomerCity() {
return customer.getAddress().getCity();
}
}
public class OrderService {
public void processOrder(Order order) {
String city = order.getCustomerCity();
// 使用city进行业务处理...
}
}
关键改进点 :
- 将长方法调用链封装在
Order内部 OrderService只需与直接朋友Order交互- 城市信息的获取逻辑变化不会影响服务层
决策流程图:何时应该重构方法链?
graph TD
A[发现超过1个"."的方法调用] --> B{是否属于同一抽象层级?}
B -->|是| C[可以保留]
B -->|否| D[考虑封装到下层对象]
D --> E[新方法命名应体现业务含义]
2. 暴露内部结构:当DTO变成了数据仓库
反例:直接操作集合元素
public class School {
private List<Department> departments;
public List<Department> getDepartments() {
return departments; // 直接返回内部集合
}
}
public class ReportService {
public void generateReport(School school) {
for (Department dept : school.getDepartments()) {
// 直接操作Department对象
if (dept.getTeacherCount() > 10) {
// 业务逻辑...
}
}
}
}
这种模式的危害在于:
- 破坏封装性 :外部可以直接修改School的内部集合
- 业务逻辑泄露 :教师数量的判断应该属于School的职责
- 难以扩展 :如果需要过滤条件变化,需要修改所有调用方
重构方案:提供行为而非数据
public class School {
private List<Department> departments;
public void processLargeDepartments(Consumer<Department> processor) {
departments.stream()
.filter(dept -> dept.getTeacherCount() > 10)
.forEach(processor);
}
}
public class ReportService {
public void generateReport(School school) {
school.processLargeDepartments(dept -> {
// 只处理符合条件的部门
});
}
}
改进效果 :
- 内部集合结构完全隐藏
- 过滤条件由School统一维护
- 业务语义更加清晰("处理大型部门")
集合处理的三种正确姿势
-
防御性拷贝 (适用于不可变数据):
public List<Department> getDepartments() { return new ArrayList<>(departments); } -
视图方法 (Java 9+):
public List<Department> getDepartmentsView() { return List.copyOf(departments); } -
行为暴露 (推荐方式):
public void forEachDepartment(Consumer<Department> action) { departments.forEach(action); }
3. 服务类膨胀:当Utils成为上帝类
反例:集中式服务工具类
public class EmployeeUtils {
public static void processPayroll(Employee emp) {
BankAccount account = emp.getBankAccount();
TaxInfo taxInfo = emp.getTaxInfo();
// 长达数百行的处理逻辑...
}
public static void generateReport(Employee emp) {
Department dept = emp.getDepartment();
Manager mgr = emp.getManager();
// 各种报表生成代码...
}
}
这种"上帝类"的问题在于:
- 违反单一职责原则 :一个类承担过多功能
- 高耦合 :需要了解Employee的所有细节
- 难以维护 :任何Employee结构的变动都会影响此类
重构方案:职责分布到领域对象
public class Employee {
private BankAccount account;
private TaxInfo taxInfo;
public PayrollResult processPayroll(PayrollCalculator calculator) {
return calculator.calculate(this, account, taxInfo);
}
}
public class PayrollCalculator {
public PayrollResult calculate(Employee emp, BankAccount account, TaxInfo tax) {
// 具体的薪资计算逻辑
}
}
领域驱动设计应用 :
Employee作为聚合根控制主要行为- 具体计算逻辑委托给专门的策略类
- 外部服务只需与
Employee交互
服务类设计的黄金法则
-
角色接口原则 :按调用者角色定义接口,而非按实现
public interface PayrollProcessor { PayrollResult process(Employee emp); } -
依赖注入 :避免静态方法,提高可测试性
public class PayrollService { private final PayrollProcessor processor; public PayrollService(PayrollProcessor processor) { this.processor = processor; } } -
上下文参数化 :将运行时数据通过参数传递
public interface ReportGenerator { void generate(ReportContext context); }
4. 实战重构:从学校管理系统看迪米特应用
原始问题代码分析
public class SchoolManager {
public void printAllEmployee(CollegeManager sub) {
// 直接操作CollegeEmployee(陌生人)
List<CollegeEmployee> list1 = sub.getAllEmployee();
for (CollegeEmployee e : list1) {
System.out.println(e.getId());
}
// 操作自己的Employee(朋友)
List<Employee> list2 = this.getAllEmployee();
for (Employee e : list2) {
System.out.println(e.getId());
}
}
}
违反点分析 :
CollegeEmployee作为局部变量出现(陌生人)- 学院员工的打印逻辑应该属于
CollegeManager - 学校总部员工打印逻辑与学院员工打印耦合
分步骤重构过程
第一步:识别直接朋友
Employee(成员变量)CollegeManager(方法参数)
第二步:移除陌生人依赖
public class CollegeManager {
public void printEmployee() {
List<CollegeEmployee> list = getAllEmployee();
for (CollegeEmployee e : list) {
System.out.println(e.getId());
}
}
}
第三步:简化主类逻辑
public class SchoolManager {
public void printAllEmployee(CollegeManager sub) {
sub.printEmployee(); // 委托给朋友
List<Employee> list = this.getAllEmployee();
for (Employee e : list) {
System.out.println(e.getId());
}
}
}
最终效果对比 :
| 指标 | 重构前 | 重构后 |
|---|---|---|
| 类间耦合度 | 高(3个类直接依赖) | 低(2个类依赖) |
| 修改影响范围 | 需要修改多处 | 只需修改对应类 |
| 单元测试难度 | 需要mock多个类 | 只需mock直接朋友 |
| 业务语义清晰度 | 混合关注点 | 单一职责 |
5. 高级应用:迪米特法则在分层架构中的实践
分层架构中的朋友关系
在典型的三层架构中,各层的"朋友"定义如下:
-
表示层 :
- 直接朋友:服务接口、DTO
- 陌生人:领域对象、数据访问对象
-
业务层 :
- 直接朋友:领域模型、仓储接口
- 陌生人:具体技术实现(如JDBC、Redis)
-
数据层 :
- 直接朋友:数据库连接、ORM框架
- 陌生人:业务规则、UI组件
跨层调用的正确姿势
反模式 :表示层直接访问DAO
// 在Controller中
@Controller
public class UserController {
@Autowired
private UserDao userDao; // 违反分层原则
public void saveUser(UserForm form) {
User user = convert(form);
userDao.save(user); // 直接跳过了服务层
}
}
正确做法 :通过服务层中介
@Controller
public class UserController {
@Autowired
private UserService userService; // 直接朋友
public void saveUser(UserForm form) {
userService.registerUser(form);
}
}
@Service
public class UserService {
@Autowired
private UserRepository repository;
public void registerUser(UserForm form) {
// 业务逻辑处理...
repository.save(user);
}
}
领域驱动设计中的迪米特应用
在DDD中,迪米特法则表现为:
-
聚合根封装 :外部只能通过聚合根访问内部对象
public class Order { private List<OrderItem> items; public void addItem(Product product, int quantity) { // 封装所有业务规则 if (canAddItem(product)) { items.add(new OrderItem(product, quantity)); } } } -
限界上下文隔离 :不同上下文通过防腐层交互
public class InventoryService { private final InventoryClient client; public boolean isProductAvailable(ProductId id) { // 转换为外部系统理解的DTO InventoryRequest request = convert(id); return client.checkStock(request); } } -
领域事件通知 :通过事件而非直接方法调用
public class Order { public void cancel() { this.status = CANCELLED; registerEvent(new OrderCancelled(this.id)); } }
6. 性能与可维护性的平衡艺术
何时可以放宽迪米特法则?
虽然迪米特法则能提高代码质量,但在以下场景可以适当放宽:
-
DTO/VO对象 :数据传输对象天然需要暴露字段
public class UserDto { public String username; // 公开字段可以接受 public String email; } -
性能关键路径 :避免不必要的封装导致性能下降
// 游戏开发中的典型例子 public class ParticleSystem { public Particle[] particles; // 直接暴露数组以提高性能 } -
内部框架代码 :框架底层需要直接操作对象
// ORM框架中的实体操作 public class EntityAccessor { public Object getField(Object entity, String fieldName) { // 通过反射直接访问字段 } }
测量耦合度的三个指标
-
响应度(Response For Class, RFC) :
- 计算方法:类的方法集合 + 这些方法直接调用的其他方法数量
- 健康值:RFC < 50
-
耦合度(Coupling Between Objects, CBO) :
- 计算方法:与当前类直接关联的类数量
- 健康值:CBO < 10
-
信息流(Information Flow, IF) :
- 计算方法:Fan-in × Fan-out(传入调用×传出调用)
- 健康值:IF < 50
示例:使用JDepend测量代码质量
jdepend.xml:
<metric name="CBO" max="10" />
<metric name="RFC" max="50" />
<metric name="IF" max="50" />
7. 从理论到实践:建立迪米特编码习惯
日常开发中的自查清单
在提交代码前,问自己这几个问题:
-
这个类是否只与以下对象交互?
- 自身的成员变量
- 方法参数
- 自己创建的对象
- 组件注入的对象
-
方法中是否存在超过1个"."的调用链?
-
是否将集合/数组直接返回给调用方?
-
工具类是否知道太多领域对象的细节?
IntelliJ IDEA检测技巧
-
方法链检测 :
- 设置 → Editor → Inspections → Java → Class metrics → Method chain
- 设置最大链长度为1
-
耦合度检测 :
- 使用"Analyze → Analyze Dependencies"查看类依赖
- 使用"Diagrams → Show Dependencies"生成可视化图表
-
自定义Live Template :
// 快速生成符合迪米特的委托方法 public $RETURN_TYPE$ $DELEGATE_METHOD$($PARAMS$) { return this.$DELEGATE_FIELD$.$ORIGINAL_METHOD$($ARGS$); }
团队代码审查要点
-
重点审查 :
- 超过3层的方法调用链
- 返回集合/数组的getter方法
- 静态工具类对领域对象的操作
-
审查话术示例 :
- "这个服务类是否可以直接调用DAO?"
- "这个长方法调用链能否封装到下层对象?"
- "外部类是否需要知道这么多内部细节?"
-
渐进式改进策略 :
- 第一阶段:消除明显的"火车残骸"代码
- 第二阶段:重构返回集合的getter方法
- 第三阶段:优化跨层调用
- 第四阶段:建立领域模型间的清晰边界
8. 常见问题解答:迪米特法则的误区澄清
Q1:迪米特法则会导致大量包装方法吗?
误解 :遵循迪米特会产生大量仅调用另一个方法的"包装方法"。
事实 :良好的设计应该基于行为而非数据暴露。真正的问题在于:
- 如果发现自己在写大量委托方法,说明类职责划分可能有问题
- 应该将相关行为内聚到合适的类中,而非简单委托
示例对比 :
// 不好的做法:简单委托
public class OrderService {
private OrderRepository repository;
public Order findById(long id) {
return repository.findById(id);
}
public void save(Order order) {
repository.save(order);
}
// 数十个类似的委托方法...
}
// 好的做法:富含业务语义
public class OrderService {
private OrderRepository repository;
public Order checkout(Cart cart) {
Order order = createOrderFromCart(cart);
return repository.save(order);
}
public Order cancelOrder(long id) {
Order order = repository.findById(id);
order.cancel();
return repository.save(order);
}
}
Q2:如何区分"必要耦合"和"过度耦合"?
判断标准 :
| 特征 | 必要耦合 | 过度耦合 |
|---|---|---|
| 变更影响 | 影响范围可控 | 引发连锁反应 |
| 语义合理性 | 符合领域概念关系 | 技术实现细节泄露 |
| 测试难度 | 容易mock | 需要复杂测试脚手架 |
| 文档描述 | 能清晰说明业务关系 | 难以用业务语言解释 |
示例 :
// 必要耦合:订单与客户的关系是领域核心
public class Order {
private Customer customer; // 合理耦合
}
// 过度耦合:报表服务需要了解订单项折扣计算细节
public class ReportService {
public void generate(Order order) {
for (Item item : order.getItems()) {
Discount discount = item.getDiscount();
// 深入处理折扣细节...
}
}
}
Q3:迪米特法则与SOLID原则的关系
互补关系 :
-
单一职责原则(SRP) :
- 迪米特帮助识别类是否承担过多职责
- 一个类如果知道太多"陌生人",可能违反SRP
-
开闭原则(OCP) :
- 迪米特通过减少耦合使系统更易扩展
- 修改被隔离在最小范围内
-
接口隔离原则(ISP) :
- 两者都强调最小化依赖
- ISP关注接口粒度,迪米特关注对象交互
-
依赖倒置原则(DIP) :
- 迪米特是DIP的具体实现手段之一
- 都提倡依赖于抽象
协同应用示例 :
// 结合SOLID和迪米特的设计
public interface OrderProcessor { // DIP
void process(Order order);
}
public class BasicOrderProcessor implements OrderProcessor {
private final PaymentGateway gateway; // 通过接口依赖
public void process(Order order) {
// 仅与直接朋友交互
order.validate();
gateway.charge(order.getTotal());
order.complete();
}
}
9. 工具链支持:自动化检测与重构
静态分析工具
-
SpotBugs规则 :
LOD_LACK_OF_DEMETER:检测明显违反迪米特的代码- 配置示例:
<Match> <Bug pattern="LOD_LACK_OF_DEMETER" /> <Priority value="2" /> </Match>
-
SonarQube指标 :
java:S1201:方法链过长java:S2384:公共方法返回内部数组- 质量阈设置:
"rules": { "java:S1201": {"level": "CRITICAL"}, "java:S2384": {"level": "MAJOR"} }
-
ArchUnit测试 :
@ArchTest public static final ArchRule demeter_rule = ArchRuleDefinition.noClasses() .should().accessClassesThat().resideInAnyPackage("..internal..") .orShould().callMethodWhere(JavaMethod.Predicates.chainCall());
重构技术
-
提取方法对象 :
// 重构前 public void process(Order order) { order.getCustomer().getAddress().validate(); // 复杂处理逻辑... } // 重构后 public void process(Order order) { new OrderProcessor(order).execute(); } private class OrderProcessor { private final Order order; OrderProcessor(Order order) { this.order = order; } void execute() { order.validateCustomerAddress(); // 处理逻辑... } } -
引入参数对象 :
// 重构前 public void register(String name, String email, String street, String city) { // 需要知道所有参数细节 } // 重构后 public void register(RegistrationForm form) { // 只需与form对象交互 } -
替换委托为继承 (谨慎使用):
// 重构前 public class Stack { private ArrayList elements = new ArrayList(); public void push(Object o) { elements.add(o); } } // 重构后 public class Stack extends ArrayList { public void push(Object o) { add(o); } }
10. 真实项目案例:电商系统重构实录
重构前代码分析
场景 :订单价格计算服务
public class PricingService {
public BigDecimal calculatePrice(Order order) {
BigDecimal total = BigDecimal.ZERO;
// 违反迪米特:深入订单项和产品细节
for (OrderItem item : order.getItems()) {
Product product = item.getProduct();
BigDecimal basePrice = product.getPricing().getBasePrice();
BigDecimal discount = product.getPricing().getCurrentDiscount();
total = total.add(
basePrice.multiply(BigDecimal.ONE.subtract(discount))
.multiply(new BigDecimal(item.getQuantity())));
}
// 违反迪米特:直接操作会员等级
MemberLevel level = order.getCustomer().getMember().getLevel();
total = total.multiply(level.getDiscountFactor());
return total;
}
}
主要问题 :
- 深入订单项、产品、定价、会员等多层对象
- 价格计算逻辑分散在各层对象中
- 任何底层模型修改都会影响此服务
分阶段重构过程
阶段一:封装产品价格计算
public class Product {
private Pricing pricing;
public BigDecimal getFinalPrice(int quantity) {
return pricing.getBasePrice()
.multiply(BigDecimal.ONE.subtract(pricing.getCurrentDiscount()))
.multiply(new BigDecimal(quantity));
}
}
阶段二:封装订单项总计
public class OrderItem {
private Product product;
private int quantity;
public BigDecimal getSubtotal() {
return product.getFinalPrice(quantity);
}
}
阶段三:封装会员折扣
public class Customer {
private Member member;
public BigDecimal applyDiscount(BigDecimal amount) {
return amount.multiply(member.getLevel().getDiscountFactor());
}
}
最终服务代码 :
public class PricingService {
public BigDecimal calculatePrice(Order order) {
BigDecimal subtotal = order.getItems().stream()
.map(OrderItem::getSubtotal)
.reduce(BigDecimal.ZERO, BigDecimal::add);
return order.getCustomer().applyDiscount(subtotal);
}
}
重构效果评估
代码指标对比 :
| 指标 | 重构前 | 重构后 |
|---|---|---|
| 方法复杂度(Cyclomatic) | 12 | 3 |
| 类耦合度(CBO) | 6 | 3 |
| 代码行数(LOC) | 25 | 8 |
| 可测试性 | 需要mock 5个类 | 只需mock 2个类 |
业务收益 :
- 价格计算逻辑现在由各领域对象自己维护
- 新增折扣类型只需修改对应类,不影响服务层
- 单元测试更容易编写和维护
- 业务语义更加清晰明了
11. 设计模式中的迪米特应用
典型设计模式实现
-
外观模式(Facade) :
public class OrderFacade { private InventoryService inventory; private PaymentService payment; private ShippingService shipping; public OrderResult placeOrder(Order order) { inventory.checkStock(order); payment.process(order); return shipping.scheduleDelivery(order); } }效果 :客户端只需与Facade交互,无需了解子系统细节
-
中介者模式(Mediator) :
public class AirTrafficControl { private List<Aircraft> aircrafts; public void requestLanding(Aircraft aircraft) { // 协调所有飞机交互 aircrafts.forEach(a -> a.adjustCourse()); aircraft.clearToLand(); } }效果 :飞机之间不直接通信,通过控制塔中介
-
代理模式(Proxy) :
public class ImageProxy implements Image { private RealImage realImage; public void display() { if (realImage == null) { realImage = new RealImage(filename); } realImage.display(); } }效果 :客户端与代理交互,不知晓真实对象细节
模式选择决策树
graph TD
A[需要隐藏复杂子系统?] -->|是| B[外观模式]
A -->|否| C{对象需要集中协调?}
C -->|是| D[中介者模式]
C -->|否| E{需要控制对象访问?}
E -->|是| F[代理模式]
E -->|否| G[考虑迪米特基本封装]
12. 微服务架构中的迪米特法则
服务边界的迪米特应用
-
服务接口设计 :
- 提供粗粒度API,避免"聊天式"交互
- 示例:
// 不符合迪米特 @GetMapping("/users/{id}/address/street") // 符合迪米特 @GetMapping("/users/{id}/profile")
-
DTO设计原则 :
- 包含完整的上下文信息
- 避免嵌套过深的结构
- 示例:
// 不符合迪米特 public class OrderDto { public CustomerDto customer; // 嵌套过深 } // 符合迪米特 public class OrderDto { public String customerName; public String shippingAddress; }
-
事件驱动交互 :
// 订单服务发布事件 public class OrderService { private EventPublisher publisher; public void completeOrder(Order order) { publisher.publish(new OrderCompleted( order.getId(), order.getTotalAmount() )); } } // 物流服务订阅事件 public class ShippingListener { @EventListener public void onOrderCompleted(OrderCompleted event) { // 只需处理事件数据,不了解订单内部 } }
服务网格中的实践
-
Sidecar模式 :
- 每个服务只与自己的sidecar交互
- sidecar处理与其他服务的通信细节
-
服务契约 :
- 定义清晰的接口规范
- 使用Protobuf/OpenAPI等IDL语言
-
混沌工程验证 :
- 随机断开服务间连接
- 验证是否遵循了最小知识原则
13. 测试策略:如何验证迪米特合规性
单元测试验证
-
Mock验证法 :
@Test public void shouldOnlyDependOnDirectFriends() { // 设置 Order order = mock(Order.class); Customer customer = mock(Customer.class); when(order.getCustomer()).thenReturn(customer); // 执行 service.process(order); // 验证 verify(order).validate(); verifyZeroInteractions(customer); // 不应与Customer交互 } -
反射检测法 :
@Test public void checkNoForbiddenDependencies() { Set<Class<?>> allowed = Set.of(Order.class, Logger.class); for (Field field : service.getClass().getDeclaredFields()) { assertTrue(allowed.contains(field.getType())); } }
集成测试策略
-
组件边界测试 :
@SpringBootTest public class ComponentBoundaryTest { @Autowired private ApplicationContext context; @Test public void checkLayerDependencies() { for (String beanName : context.getBeanDefinitionNames()) { Object bean = context.getBean(beanName); // 验证Controller不直接依赖Repository等规则 } } } -
架构测试 :
@Test public void enforceDemeterInLayers() { JavaClasses classes = new ClassFileImporter() .importPackages("com.myapp"); ArchRule rule = layeredArchitecture() .layer("Controller").definedBy("..web..") .layer("Service").definedBy("..service..") .layer("Repository").definedBy("..dao..") .whereLayer("Controller").mayNotBeAccessedByAnyLayer() .whereLayer("Service").mayOnlyBeAccessedByLayers("Controller") .whereLayer("Repository").mayOnlyBeAccessedByLayers("Service"); rule.check(classes); }
14. 反模式警示:这些"优化"其实在破坏迪米特
常见错误实践
-
过度封装 :
// 反面例子:无意义的封装 public class Order { private Customer customer; public String getCustomerName() { return customer.getName(); } public String getCustomerEmail() { return customer.getEmail(); } // 数十个类似的getter... }问题 :只是将方法调用转移,未真正封装行为
-
虚假朋友 :
public class ReportService { private Order order; public void generate() { // 虽然order是成员变量,但深入其内部结构 for (Item item : order.getItems()) { // 处理item细节... } } }问题 :形式上符合迪米特,实质上仍在破坏封装
-
滥用Law of Demeter :
// 过度应用导致代码晦涩 public class Order { private Customer customer; public void process(Processor processor) { processor.execute(this::getCustomerInfo); } private CustomerInfo getCustomerInfo() { return new CustomerInfo(customer); } }问题 :引入了不必要的复杂性
健康度检查表
评估你的设计是否真正遵循迪米特:
-
[ ] 类是否主要与以下对象交互:
- 自己的成员变量
- 方法参数
- 自己创建的对象
- 直接组件依赖
-
[ ] 方法中是否避免超过1个"."的调用链?
-
[ ] 是否避免将内部集合/数组直接暴露?
-
[ ] 跨层调用是否通过明确定义的接口?
-
[ ] 领域对象是否封装了相关行为而不仅是数据?
15. 性能考量:迪米特与效率的平衡
性能敏感场景的处理
-
DTO投影优化 :
// 避免多次查询的DTO构造 @Repository public interface OrderRepository extends JpaRepository<Order, Long> { @Query("SELECT new com.example.OrderSummary(o.id, c.name, o.total) " + "FROM Order o JOIN o.customer c WHERE o.status = 'COMPLETED'") List<OrderSummary> findCompletedOrders(); } -
批量操作接口 :
public interface OrderService { // 不符合迪米特但性能更好 Map<Long, BigDecimal> getOrderTotals(Collection<Long> ids); // 替代方案:分页查询 Page<OrderTotal> findOrderTotals(Pageable pageable); } -
CQRS模式应用 :
// 命令端(严格封装) public class OrderCommandService { public void placeOrder(OrderCommand command) { // 严格验证和业务规则 } } // 查询端(适当放宽) public class OrderQueryService { public OrderView getOrderView(long id) { // 直接join多表获取展示数据 } }
性能测试指标
在放宽迪米特前进行测量:
-
关键路径分析 :
@Benchmark public void testWithDemeter(Blackhole bh) { Order order = new Order(); bh.consume(order.getFormattedTotal()); } @Benchmark public void testWithoutDemeter(Blackhole bh) { Order order = new Order(); bh.consume(order.getTotal().getAmount() .setScale(2, RoundingMode.HALF_UP) .toString()); } -
JProfiler检测 :
- 方法调用深度分析
- 对象分配热点图
- 调用树比较
-
JMeter压力测试 :
- 对比严格迪米特与优化版本的TPS
- 测量99线响应时间差异
16. 语言特性进阶:Java特有实现技巧
Java模块系统的应用
-
模块信息配置 :
// module-info.java module order.service { requires transitive customer.api; // 明确朋友关系 exports com.orders.service; opens com.orders.internal; // 谨慎使用 } -
服务加载 :
module payment.provider { provides PaymentService with CreditCardPayment; } module order.app { uses PaymentService; }
记录类(Record)的迪米特优势
// 定义不可变DTO
public record OrderView(
long id,
String customerName,
BigDecimal total
) {}
// 使用处无需担心内部结构
public class ReportGenerator {
public void generate(OrderView view) {
// 直接使用字段,无需getter
System.out.println(view.customerName());
}
}
密封类(Sealed Class)控制朋友范围
public sealed interface PaymentResult
permits Success, Failure, Pending {
public record Success(String code) implements PaymentResult {}
public record Failure(String reason) implements PaymentResult {}
public record Pending() implements PaymentResult {}
}
public class PaymentService {
// 调用方只需处理已知子类
public PaymentResult process(Payment payment) {
// ...
}
}
17. 行业实践:知名框架中的迪米特应用
Spring框架实现
-
依赖注入原则 :
- 通过
@Autowired声明直接朋友 - 避免手动查找依赖
- 通过
-
Controller最佳实践 :
@Controller public class OrderController { private final OrderService service; // 唯一直接朋友 @PostMapping public ResponseEntity<?> create(@RequestBody OrderForm form) { // 不深入form的内部结构 return ResponseEntity.ok(service.createOrder(form)); } }
Hibernate实践
-
延迟加载设计 :
@Entity public class Order { @ManyToOne(fetch = LAZY) // 避免自动加载关联 private Customer customer; public Customer getCustomer() { // 实际可能返回代理对象 } } -
DTO投影查询 :
public interface OrderSummary { Long getId(); String getCustomerName(); // 不暴露整个Customer default String getSummary() {
更多推荐



所有评论(0)