别再请求数据!掌握“Tell, Don't Ask”原则
发布: (2025年12月19日 GMT+8 08:57)
3 min read
原文: Dev.to
Source: Dev.to
介绍
在面向对象编程(OOP)中,最常见的错误之一是把对象当作普通的数据结构来使用。我们经常把对象的数据取出来,进行一些逻辑处理,然后再把结果放回去。本文阐述了 Tell, Don’t Ask(TDA) 原则,并通过一个简单的 Shop 与 Account 示例展示它如何提升代码质量。
Tell, Don’t Ask 原则
- Don’t Ask:不要向对象请求数据再自行进行逻辑处理。
- Tell:告诉对象该做什么,让它自行处理自己的状态。
遵循 TDA 能提升 封装性 与 内聚性。
反模式示例
Shop 类在处理本应属于 Account 的逻辑,违反了封装。
package com.mateandgit.programming.chapter1_2.problem;
public class Shop {
public void sell(Account account, Product product) {
// ❌ BAD: Asking for data (getMoney)
long price = product.getPrice();
long mileage = account.getMoney();
// ❌ BAD: The Shop decides if the account has enough money
if (mileage >= price) {
// ❌ BAD: Modifying the account's state externally
account.setMoney(mileage - price);
System.out.println(product.getName() + " purchased.");
} else {
System.out.println("Insufficient balance.");
}
}
}
问题
- 封装性破坏 –
Shop知道Account的内部细节(money字段以及余额的计算方式)。 - 逻辑重复 – 任何其他需要扣款的类都必须重复
if (balance >= price)的检查。 - 难以维护 – 一旦验证规则改变(例如“最低余额必须保持 100”),所有访问
Account的类都需要更新。
重构方案
将逻辑移到 Account 类中。Shop 现在只向 Account 发出指令。
package com.mateandgit.programming.chapter1_2.solution;
public class Shop {
public void sell(Account account, Product product) {
// ✅ GOOD: Ask a question (canAfford)
if (account.canAfford(product.getPrice())) {
// ✅ GOOD: Tell the object to do something (withdraw)
account.withdraw(product.getPrice());
System.out.println(product.getName() + " purchased.");
} else {
System.out.println("Insufficient balance.");
}
}
}
在 Account 类中,你会实现 canAfford() 与 withdraw() 等方法来封装相应的逻辑。
重构的好处
- 更好的封装 –
Shop不再了解取款的实现细节,只是发送一条消息。 - 逻辑可复用 – 验证规则(
canAfford)集中在Account中。 - 可读性提升 – 代码读起来像一句话:“如果账户能负担,就取款”。
- 更高的内聚性和更低的耦合度 – 对象自行管理状态,使代码更清晰、更易维护。
应用 Tell, Don’t Ask
- 不要:
getSomething() → calculate → setSomething()。 - 要:在对象内部创建方法并 告诉 它执行相应操作。
结果:高内聚、低耦合,封装性更好。