在软件工程中,"坏味道"(Code Smells)是指代码中那些可能暗示更深层次问题的表面迹象。行为模式坏味道特指与对象行为、职责分配和交互方式相关的设计缺陷。
行为模式坏味道(Behavioral Smells)是代码坏味道的一个重要子集,主要关注对象如何交互、职责如何分配以及行为如何组织。与结构型坏味道(如长方法、大类)不同,行为模式坏味道往往更隐蔽,因为它们不会直接导致明显的代码质量问题,但会随着系统演进逐渐积累技术债务。
| 特征 | 描述 |
|---|---|
| 职责混乱 | 类的职责不清晰,或承担了不属于它的责任 |
| 过度耦合 | 类与类之间的依赖关系过于紧密 |
| 低内聚 | 类内部的方法和数据缺乏逻辑关联 |
| 违反迪米特法则 | 对象了解了太多关于其他对象的内部细节 |
| 继承滥用 | 使用继承来解决本应该用组合解决的问题 |
行为模式坏味道的危害往往是延迟爆发的:
当一个类继承另一个类,仅仅是为了复用部分代码,而不是真正的"is-a"关系时,就出现了继承滥用。这通常表现为:
// ❌ 坏味道:Square 继承 Rectangle 违反了里氏替换原则
class Rectangle {
protected int width;
protected int height;
public void setWidth(int width) {
this.width = width;
}
public void setHeight(int height) {
this.height = height;
}
public int getArea() {
return width * height;
}
}
class Square extends Rectangle {
@Override
public void setWidth(int width) {
this.width = width;
this.height = width; // 强制正方形
}
@Override
public void setHeight(int height) {
this.width = height; // 强制正方形
this.height = height;
}
}
// 问题:以下代码会失败
void testRectangle(Rectangle rect) {
rect.setWidth(5);
rect.setHeight(4);
assert rect.getArea() == 20; // Square 返回 16
}
// ✅ 好的设计:使用组合而非继承
interface Shape {
int getArea();
}
class Rectangle implements Shape {
private int width;
private int height;
public Rectangle(int width, int height) {
this.width = width;
this.height = height;
}
@Override
public int getArea() {
return width * height;
}
}
class Square implements Shape {
private int side;
public Square(int side) {
this.side = side;
}
@Override
public int getArea() {
return side * side;
}
}
上帝类是一个知道太多、做太多的类。它通常具有以下特征:
// ❌ 坏味道:OrderManager 是一个典型的上帝类
public class OrderManager {
// 数据库连接
private Connection dbConnection;
// 订单相关
private List<Order> orders;
private OrderValidator validator;
// 用户相关
private UserService userService;
private EmailService emailService;
// 库存相关
private InventoryService inventoryService;
private WarehouseService warehouseService;
// 支付相关
private PaymentGateway paymentGateway;
private RefundService refundService;
// 报表相关
private ReportGenerator reportGenerator;
private ExcelExporter excelExporter;
// 日志相关
private Logger logger;
private AuditService auditService;
// 超过 20 个方法,处理各种业务逻辑
public void createOrder(OrderRequest request) { /* ... */ }
public void cancelOrder(String orderId) { /* ... */ }
public void processPayment(String orderId, PaymentInfo payment) { /* ... */ }
public void handleRefund(String orderId, RefundRequest refund) { /* ... */ }
public void updateInventory(String productId, int quantity) { /* ... */ }
public void sendNotification(String userId, String message) { /* ... */ }
public void generateDailyReport() { /* ... */ }
public void exportToExcel(List<Order> orders) { /* ... */ }
public void auditOrderChanges(String orderId) { /* ... */ }
// ... 还有更多方法
}
// ✅ 好的设计:按职责拆分
// 1. 订单核心服务
public class OrderService {
private final OrderRepository orderRepository;
private final OrderValidator validator;
public Order createOrder(OrderRequest request) { /* ... */ }
public void cancelOrder(String orderId) { /* ... */ }
public Order getOrder(String orderId) { /* ... */ }
}
// 2. 支付服务
public class PaymentService {
private final PaymentGateway paymentGateway;
private final RefundService refundService;
public PaymentResult processPayment(String orderId, PaymentInfo payment) { /* ... */ }
public RefundResult processRefund(String orderId, RefundRequest refund) { /* ... */ }
}
// 3. 库存服务
public class InventoryService {
private final InventoryRepository inventoryRepository;
private final WarehouseService warehouseService;
public void reserveInventory(String productId, int quantity) { /* ... */ }
public void releaseInventory(String productId, int quantity) { /* ... */ }
}
// 4. 通知服务
public class NotificationService {
private final EmailService emailService;
private final SmsService smsService;
public void sendOrderConfirmation(String userId, Order order) { /* ... */ }
public void sendShippingNotification(String userId, Shipment shipment) { /* ... */ }
}
// 5. 报表服务
public class ReportService {
private final ReportGenerator reportGenerator;
private final ExcelExporter excelExporter;
public Report generateDailyReport() { /* ... */ }
public byte[] exportOrdersToExcel(List<Order> orders) { /* ... */ }
}
// 6. 审计服务
public class AuditService {
private final AuditRepository auditRepository;
public void logOrderChange(String orderId, OrderChange change) { /* ... */ }
}
一个方法对另一个类的兴趣超过了对自己所在类的兴趣。具体表现为:
// ❌ 坏味道:Order 类过度依赖 Customer 类的数据
public class Order {
private Customer customer;
private List<OrderItem> items;
private BigDecimal totalAmount;
// 这个方法更关心 Customer 的数据
public String generateInvoice() {
StringBuilder invoice = new StringBuilder();
// 大量访问 customer 的数据
invoice.append("Customer: ").append(customer.getFullName()).append("\n");
invoice.append("Address: ").append(customer.getAddress().getStreet()).append("\n");
invoice.append(" ").append(customer.getAddress().getCity()).append(", ")
.append(customer.getAddress().getState()).append("\n");
invoice.append("Email: ").append(customer.getEmail()).append("\n");
invoice.append("Phone: ").append(customer.getPhone()).append("\n\n");
// 少量使用自己的数据
invoice.append("Order Total: ").append(totalAmount);
return invoice.toString();
}
}
public class Customer {
private String firstName;
private String lastName;
private Address address;
private String email;
private String phone;
public String getFullName() {
return firstName + " " + lastName;
}
// getters...
}
// ✅ 好的设计:将方法移到数据所属的类
public class Order {
private Customer customer;
private List<OrderItem> items;
private BigDecimal totalAmount;
public String generateInvoice() {
StringBuilder invoice = new StringBuilder();
// 委托给 Customer 生成自己的信息
invoice.append(customer.getInvoiceInfo()).append("\n\n");
invoice.append("Order Total: ").append(totalAmount);
return invoice.toString();
}
}
public class Customer {
private String firstName;
private String lastName;
private Address address;
private String email;
private String phone;
public String getFullName() {
return firstName + " " + lastName;
}
// Customer 负责生成自己的发票信息
public String getInvoiceInfo() {
StringBuilder info = new StringBuilder();
info.append("Customer: ").append(getFullName()).append("\n");
info.append("Address: ").append(address.getFormattedAddress()).append("\n");
info.append("Email: ").append(email).append("\n");
info.append("Phone: ").append(phone);
return info.toString();
}
}
public class Address {
private String street;
private String city;
private String state;
private String zipCode;
public String getFormattedAddress() {
return street + "\n" + city + ", " + state + " " + zipCode;
}
}
可以使用以下指标来判断是否存在依恋情结:
如果方法 M 在类 A 中,但满足以下条件,则可能存在依恋情结:
- 方法 M 访问类 B 的方法/字段次数 > 访问类 A 的方法/字段次数
- 方法 M 使用了类 B 的数据来计算结果
- 方法 M 很少或从不修改类 A 的状态
客户端向一个对象请求另一个对象,然后再向后者请求另一个对象,依此类推。形成了一条长长的调用链:
// 典型的消息链
String city = order.getCustomer().getAddress().getCity().getName();
// ❌ 坏味道:深层消息链
public class ReportGenerator {
public void generateOrderReport(Order order) {
// 深层消息链,违反了迪米特法则
String customerName = order.getCustomer().getProfile().getPersonalInfo().getFullName();
String street = order.getCustomer().getAddress().getStreetDetails().getLine1();
String city = order.getCustomer().getAddress().getCity().getName();
String managerName = order.getCustomer().getAccount().getAssignedManager().getName();
// 使用这些数据生成报表...
}
}
// 相关的类结构
class Order {
private Customer customer;
public Customer getCustomer() { return customer; }
}
class Customer {
private Profile profile;
private Address address;
private Account account;
// getters...
}
class Profile {
private PersonalInfo personalInfo;
public PersonalInfo getPersonalInfo() { return personalInfo; }
}
class Address {
private StreetDetails streetDetails;
private City city;
// getters...
}
class Account {
private Manager assignedManager;
public Manager getAssignedManager() { return assignedManager; }
}
// ✅ 好的设计:隐藏委托关系
public class ReportGenerator {
public void generateOrderReport(Order order) {
// 通过 Order 提供的方法获取信息
String customerName = order.getCustomerName();
String street = order.getCustomerStreet();
String city = order.getCustomerCity();
String managerName = order.getCustomerManagerName();
// 生成报表...
}
}
// 在 Order 类中隐藏委托细节
class Order {
private Customer customer;
// 提供便捷方法,隐藏内部结构
public String getCustomerName() {
return customer.getFullName();
}
public String getCustomerStreet() {
return customer.getStreetAddress();
}
public String getCustomerCity() {
return customer.getCityName();
}
public String getCustomerManagerName() {
return customer.getManagerName();
}
}
// Customer 也隐藏自己的委托
class Customer {
private Profile profile;
private Address address;
private Account account;
public String getFullName() {
return profile.getFullName();
}
public String getStreetAddress() {
return address.getStreetLine1();
}
public String getCityName() {
return address.getCityName();
}
public String getManagerName() {
return account.getManagerName();
}
}
| 策略 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 隐藏委托 | 消息链使用频率高 | 保持封装,客户端简单 | 需要在中间类添加大量方法 |
| 提取方法 | 消息链在多处使用 | 代码复用 | 可能创建过多小方法 |
| 引入参数对象 | 需要多个相关数据 | 减少参数数量 | 需要创建新类 |
一个类的大部分方法都简单地委托给其他类,自己几乎没有实质功能。这种类成为了不必要的中间层:
// ❌ 坏味道:AccountManager 只是简单的委托
public class AccountManager {
private AccountService accountService;
private TransactionService transactionService;
private NotificationService notificationService;
// 所有这些方法都只是简单委托
public Account getAccount(String id) {
return accountService.getAccount(id);
}
public void createAccount(AccountRequest request) {
accountService.createAccount(request);
}
public void updateAccount(String id, AccountUpdate update) {
accountService.updateAccount(id, update);
}
public List<Transaction> getTransactions(String accountId) {
return transactionService.getTransactions(accountId);
}
public void transfer(String fromId, String toId, BigDecimal amount) {
transactionService.transfer(fromId, toId, amount);
}
public void sendNotification(String userId, String message) {
notificationService.send(userId, message);
}
}
// ✅ 好的设计:移除中间人,客户端直接使用服务
// 客户端代码
public class AccountController {
// 直接注入需要的服务
private final AccountService accountService;
private final TransactionService transactionService;
public AccountController(AccountService accountService,
TransactionService transactionService) {
this.accountService = accountService;
this.transactionService = transactionService;
}
@GetMapping("/accounts/{id}")
public Account getAccount(@PathVariable String id) {
return accountService.getAccount(id); // 直接调用
}
@PostMapping("/accounts/{id}/transfer")
public void transfer(@PathVariable String fromId,
@RequestBody TransferRequest request) {
transactionService.transfer(fromId, request.getToId(), request.getAmount());
}
}
// 如果确实需要协调多个服务,添加有价值的逻辑
public class AccountFacade {
private final AccountService accountService;
private final TransactionService transactionService;
private final NotificationService notificationService;
private final AuditService auditService;
// 这个方法有实质的协调逻辑,不是简单委托
@Transactional
public TransferResult processTransfer(TransferRequest request) {
// 1. 验证账户
Account fromAccount = accountService.validateAndGet(request.getFromId());
Account toAccount = accountService.validateAndGet(request.getToId());
// 2. 检查余额
if (fromAccount.getBalance().compareTo(request.getAmount()) < 0) {
throw new InsufficientFundsException();
}
// 3. 执行转账
Transaction transaction = transactionService.transfer(
request.getFromId(),
request.getToId(),
request.getAmount()
);
// 4. 发送通知
notificationService.sendTransferNotification(fromAccount.getOwner(), transaction);
notificationService.sendTransferNotification(toAccount.getOwner(), transaction);
// 5. 记录审计日志
auditService.logTransfer(transaction);
return new TransferResult(transaction.getId(), transaction.getStatus());
}
}
并非所有中间人都是坏味道,以下情况可以保留:
两个类过于亲密,花费太多时间去探究彼此的私有部分:
// ❌ 坏味道:Account 和 Transaction 过于亲密
public class Account {
private String id;
private BigDecimal balance;
private List<Transaction> transactions;
private AccountStatus status;
// 直接暴露内部集合
public List<Transaction> getTransactions() {
return transactions; // 返回原始引用!
}
// 让外部类直接修改内部状态
public void setBalance(BigDecimal balance) {
this.balance = balance;
}
}
public class Transaction {
private String id;
private Account fromAccount;
private Account toAccount;
private BigDecimal amount;
public void execute() {
// 直接操作 Account 的内部状态
BigDecimal newFromBalance = fromAccount.getBalance().subtract(amount);
BigDecimal newToBalance = toAccount.getBalance().add(amount);
fromAccount.setBalance(newFromBalance); // 直接修改!
toAccount.setBalance(newToBalance); // 直接修改!
// 直接操作另一个类的内部集合
fromAccount.getTransactions().add(this);
toAccount.getTransactions().add(this);
}
}
// ✅ 好的设计:保持适当的边界
public class Account {
private final String id;
private BigDecimal balance;
private final List<Transaction> transactions = new ArrayList<>();
private AccountStatus status;
// 返回不可修改的视图
public List<Transaction> getTransactions() {
return Collections.unmodifiableList(transactions);
}
// 包级私有,只有同包类可以调用
void addTransaction(Transaction transaction) {
this.transactions.add(transaction);
}
// 通过方法而非直接设置来修改余额
void debit(BigDecimal amount) {
if (amount.compareTo(balance) > 0) {
throw new InsufficientFundsException();
}
this.balance = balance.subtract(amount);
}
void credit(BigDecimal amount) {
this.balance = balance.add(amount);
}
public BigDecimal getBalance() {
return balance;
}
}
public class Transaction {
private final String id;
private final Account fromAccount;
private final Account toAccount;
private final BigDecimal amount;
private TransactionStatus status;
// 通过 AccountService 执行,而非直接操作
public void execute(AccountService service) {
service.executeTransfer(this);
}
// Getters...
public Account getFromAccount() { return fromAccount; }
public Account getToAccount() { return toAccount; }
public BigDecimal getAmount() { return amount; }
}
// 由服务层协调操作
public class AccountService {
@Transactional
public void executeTransfer(Transaction transaction) {
Account from = transaction.getFromAccount();
Account to = transaction.getToAccount();
BigDecimal amount = transaction.getAmount();
// 通过方法调用来修改状态,而非直接访问字段
from.debit(amount);
to.credit(amount);
from.addTransaction(transaction);
to.addTransaction(transaction);
transaction.markAsCompleted();
}
}
| 策略 | 说明 | 示例 |
|---|---|---|
| 移动方法 | 将方法移到更合适的类 | 将验证逻辑移到数据所有者 |
| 移动字段 | 将字段移到使用它最多的类 | 将计算结果缓存移到使用方 |
| 提取类 | 将共同部分提取到新类 | 提取共同的配置到 Settings 类 |
| 隐藏委托 | 不直接暴露内部对象 | 返回不可修改的集合视图 |
| 双向→单向 | 将双向关联改为单向 | 订单知道客户,但客户不直接知道订单 |
子类继承了父类的方法和数据,但只使用其中一小部分,或者需要覆盖/禁用大部分继承来的功能:
// ❌ 坏味道:Bird 继承 Animal,但 Penguin 拒绝了 fly()
public abstract class Animal {
public abstract void eat();
public abstract void sleep();
public abstract void move();
}
public abstract class Bird extends Animal {
public abstract void fly(); // 假设所有鸟都会飞
@Override
public void move() {
fly(); // 鸟通过飞行移动
}
}
public class Sparrow extends Bird {
@Override
public void eat() { /* ... */ }
@Override
public void sleep() { /* ... */ }
@Override
public void fly() { /* 正常飞行 */ }
}
public class Penguin extends Bird {
@Override
public void eat() { /* ... */ }
@Override
public void sleep() { /* ... */ }
@Override
public void fly() {
// ❌ 企鹅不会飞!拒绝了继承的遗赠
throw new UnsupportedOperationException("Penguins cannot fly!");
}
@Override
public void move() {
swim(); // 被迫覆盖 move(),因为父类假设通过飞行移动
}
public void swim() { /* 企鹅会游泳 */ }
}
// ✅ 好的设计:使用组合和接口隔离
// 定义能力接口
interface Eater {
void eat();
}
interface Sleeper {
void sleep();
}
interface Flyer {
void fly();
}
interface Swimmer {
void swim();
}
interface Walker {
void walk();
}
// 具体类实现需要的接口
public class Sparrow implements Eater, Sleeper, Flyer, Walker {
@Override
public void eat() { /* ... */ }
@Override
public void sleep() { /* ... */ }
@Override
public void fly() { /* 飞行 */ }
@Override
public void walk() { /* 走路 */ }
}
public class Penguin implements Eater, Sleeper, Swimmer, Walker {
@Override
public void eat() { /* ... */ }
@Override
public void sleep() { /* ... */ }
@Override
public void swim() { /* 游泳 */ }
@Override
public void walk() { /* 走路 */ }
// 不需要实现 fly()!
}
public class Duck implements Eater, Sleeper, Flyer, Swimmer, Walker {
@Override
public void eat() { /* ... */ }
@Override
public void sleep() { /* ... */ }
@Override
public void fly() { /* 飞行 */ }
@Override
public void swim() { /* 游泳 */ }
@Override
public void walk() { /* 走路 */ }
}
以下信号表明可能存在被拒绝的遗赠:
// 信号1:抛出异常
@Override
public void someMethod() {
throw new UnsupportedOperationException();
}
// 信号2:空实现
@Override
public void someMethod() {
// 什么都不做
}
// 信号3:只调用 super
@Override
public void someMethod() {
super.someMethod();
// 没有额外逻辑
}
// 信号4:覆盖后行为完全不同
@Override
public void fly() {
// 完全不是飞行,而是其他行为
swim();
}
为了"未来可能的需求"而添加的过度设计,实际上这些需求可能永远不会到来:
// ❌ 坏味道:为未来可能的需求过度设计
// 抽象的抽象:处理器工厂创建处理器提供者,提供者再创建处理器
public interface PaymentProcessorFactory {
PaymentProcessorProvider getProvider(String type);
}
public interface PaymentProcessorProvider {
PaymentProcessor createProcessor();
PaymentValidator createValidator();
PaymentLogger createLogger();
}
public interface PaymentProcessor { /* ... */ }
public interface PaymentValidator { /* ... */ }
public interface PaymentLogger { /* ... */ }
// 只有一个实现
public class StandardPaymentProcessorFactory implements PaymentProcessorFactory {
@Override
public PaymentProcessorProvider getProvider(String type) {
return new StandardPaymentProcessorProvider();
}
}
public class StandardPaymentProcessorProvider implements PaymentProcessorProvider {
@Override
public PaymentProcessor createProcessor() {
return new StandardPaymentProcessor();
}
@Override
public PaymentValidator createValidator() {
return new StandardPaymentValidator();
}
@Override
public PaymentLogger createLogger() {
return new StandardPaymentLogger();
}
}
// 复杂的配置系统
public class PaymentConfiguration {
private Map<String, ProcessorConfig> processorConfigs;
private Map<String, ValidatorConfig> validatorConfigs;
private Map<String, LoggerConfig> loggerConfigs;
private Map<String, RetryConfig> retryConfigs;
private Map<String, CircuitBreakerConfig> circuitBreakerConfigs;
// ... 大量配置,但只使用默认值
}
// 预留的大量扩展点
public abstract class AbstractPaymentService {
protected abstract PaymentProcessorFactory getProcessorFactory();
protected abstract PaymentConfiguration getConfiguration();
protected abstract RetryPolicy getRetryPolicy();
protected abstract CircuitBreaker getCircuitBreaker();
protected abstract MetricsCollector getMetricsCollector();
protected abstract AuditLogger getAuditLogger();
protected abstract NotificationService getNotificationService();
// 实际业务逻辑
public PaymentResult processPayment(PaymentRequest request) {
// 使用上述抽象方法获取依赖
}
}
// ✅ 好的设计:简单直接,需要时再抽象
// 简单的支付服务
public class PaymentService {
private final PaymentProcessor processor;
private final PaymentValidator validator;
// 通过构造函数注入依赖
public PaymentService(PaymentProcessor processor,
PaymentValidator validator) {
this.processor = processor;
this.validator = validator;
}
public PaymentResult processPayment(PaymentRequest request) {
validator.validate(request);
return processor.process(request);
}
}
// 具体的处理器实现
public class StripePaymentProcessor implements PaymentProcessor {
private final StripeClient stripeClient;
public StripePaymentProcessor(StripeClient stripeClient) {
this.stripeClient = stripeClient;
}
@Override
public PaymentResult process(PaymentRequest request) {
// 直接调用 Stripe API
return stripeClient.charge(request);
}
}
// 当确实需要多种支付方式时,引入简单工厂
public class PaymentProcessorFactory {
private final Map<String, PaymentProcessor> processors;
public PaymentProcessorFactory(Map<String, PaymentProcessor> processors) {
this.processors = processors;
}
public PaymentProcessor getProcessor(String type) {
PaymentProcessor processor = processors.get(type);
if (processor == null) {
throw new UnsupportedPaymentTypeException(type);
}
return processor;
}
}
// 配置保持简单
@Configuration
public class PaymentConfiguration {
@Bean
public PaymentProcessor stripeProcessor(StripeClient client) {
return new StripePaymentProcessor(client);
}
@Bean
public PaymentProcessor paypalProcessor(PayPalClient client) {
return new PayPalPaymentProcessor(client);
}
@Bean
public PaymentProcessorFactory processorFactory(
Map<String, PaymentProcessor> processors) {
return new PaymentProcessorFactory(processors);
}
}
YAGNI(You Aren't Gonna Need It)是应对夸夸其谈未来性的核心原则:
"不要为未来的需求编写代码。只在真正需要时才实现功能。"
实践建议:
┌─────────────────┐
│ 1. 识别坏味道 │
│ (代码审查/工具) │
└────────┬────────┘
▼
┌─────────────────┐
│ 2. 确保测试覆盖 │
│ (没有测试不重构)│
└────────┬────────┘
▼
┌─────────────────┐
│ 3. 小步重构 │
│ (每次一个变换) │
└────────┬────────┘
▼
┌─────────────────┐
│ 4. 运行测试 │
│ (验证正确性) │
└────────┬────────┘
▼
┌─────────────────┐
│ 5. 提交代码 │
│ (保存进度) │
└────────┬────────┘
▼
┌─────────────────┐
│ 6. 重复直到完成 │
└─────────────────┘
| 重构手法 | 适用坏味道 | 说明 |
|---|---|---|
| 提取类 | 上帝类 | 将相关字段和方法移到新类 |
| 移动方法 | 依恋情结 | 将方法移到使用它数据最多的类 |
| 隐藏委托 | 消息链 | 在中间类提供便捷方法 |
| 移除中间人 | 中间人 | 让客户端直接调用最终服务 |
| 以委托取代继承 | 过度使用继承 | 将继承改为组合 |
| 折叠继承体系 | 被拒绝的遗赠 | 合并过于相似的类 |
| 内联类 | 中间人 | 将类合并到使用它的地方 |
| 工具 | 语言 | 功能 | 适用坏味道 |
|---|---|---|---|
| SonarQube | 多语言 | 全面的代码质量分析 | 上帝类、消息链、复杂度过高 |
| Checkstyle | Java | 编码规范检查 | 类长度、方法长度 |
| PMD | Java | 静态分析 | 未使用代码、复杂表达式 |
| ESLint | JavaScript | 代码质量和风格 | 类似上述 |
| RuboCop | Ruby | 代码分析 | 类似上述 |
| PyLint | Python | 代码分析 | 类似上述 |
关键指标阈值建议:
┌─────────────────────┬──────────┬─────────────┐
│ 指标 │ 警告阈值 │ 危险阈值 │
├─────────────────────┼──────────┼─────────────┤
│ 类代码行数 │ 300 │ 500 │
│ 方法代码行数 │ 30 │ 50 │
│ 类的方法数 │ 15 │ 25 │
│ 类的字段数 │ 10 │ 15 │
│ 方法参数数 │ 4 │ 6 │
│ 圈复杂度 │ 10 │ 20 │
│ 传入依赖数 (Afferent)│ 20 │ 50 │
│ 传出依赖数 (Efferent)│ 10 │ 20 │
│ 继承深度 │ 3 │ 5 │
└─────────────────────┴──────────┴─────────────┘
| 原则 | 核心思想 | 预防的坏味道 |
|---|---|---|
| Single Responsibility | 一个类只负责一件事 | 上帝类 |
| Open/Closed | 对扩展开放,对修改关闭 | 夸夸其谈未来性 |
| Liskov Substitution | 子类可替换父类 | 被拒绝的遗赠 |
| Interface Segregation | 接口应该小而专注 | 被拒绝的遗赠 |
| Dependency Inversion | 依赖抽象而非具体 | 过度耦合 |
"只和你的直接朋友交谈,不要和陌生人说话。"
通俗解释:
违反示例:
// ❌ 违反迪米特法则
String city = order.getCustomer().getAddress().getCity().getName();
// ✅ 遵循迪米特法则
String city = order.getCustomerCity();
何时使用继承:
何时使用组合:
// ❌ 继承
public class Stack extends ArrayList {
// Stack 不是 ArrayList!
}
// ✅ 组合
public class Stack {
private List elements = new ArrayList<>();
public void push(Object item) { elements.add(item); }
public Object pop() { return elements.remove(elements.size() - 1); }
}
| 坏味道 | 核心问题 | 重构策略 |
|---|---|---|
| 过度使用继承 | 使用继承代替组合 | 以委托取代继承 |
| 上帝类 | 类知道太多、做太多 | 提取类、按职责拆分 |
| 依恋情结 | 方法对其他类数据更感兴趣 | 移动方法 |
| 消息链 | 深层对象导航 | 隐藏委托、提取方法 |
| 中间人 | 类只是简单委托 | 移除中间人、内联方法 |
| 狎昵关系 | 类之间过于亲密 | 移动方法/字段、隐藏委托 |
| 被拒绝的遗赠 | 子类不需要继承的功能 | 以委托取代继承、接口隔离 |
| 夸夸其谈未来性 | 为不存在的需求设计 | 删除死代码、YAGNI |
本文档持续更新,欢迎反馈和补充。