一、概览
在软件开发过程中,代码的可读性和可维护性往往是衡量代码质量的重要指标。本文将介绍两个能显著提升代码质量的设计原则:
组合函数原则(Composed Method Pattern)
抽象层次一致性原则(Single Level of Abstraction Principle, SLAP)
并通过实例说明如何在实际开发中应用这些原则。
二、组合函数模式
组合函数模式最早由 Kent Beck 在《Smalltalk Best Practice Patterns》一书中提出。这是一个简单易懂且实用的编程原则,能够对代码的可读性和可维护性产生立竿见影的效果。
组合函数模式要求:
- 所有公有函数(入口函数)名,应当读起来像一系列执行步骤的该要
- 具体实现细节应当封装在私有函数中
这种模式有助于保持代码精炼并易于复用。阅读这样的代码就像在看一本书,入口函数是目录,指向各自的私有函数,而内容则在私有函数中实现。
示例代码
不良实践:
public void processOrder(Order order) {
// 验证订单
if (order == null) {
throw new IllegalArgumentException("Order cannot be null");
}
if (order.getItems().isEmpty()) {
throw new IllegalArgumentException("Order must contain at least one item");
}
// 计算总价
double total = 0;
for (OrderItem item : order.getItems()) {
total += item.getPrice() * item.getQuantity();
}
// 应用折扣
if (total > 1000) {
total *= 0.9; // 10% 折扣
} else if (total > 500) {
total *= 0.95; // 5% 折扣
}
// 更新订单状态
order.setTotal(total);
order.setStatus(OrderStatus.PROCESSED);
orderRepository.save(order);
// 发送确认邮件
String message = "Your order #" + order.getId() + " has been processed. Total: $" + total;
emailService.sendEmail(order.getCustomerEmail(), "Order Confirmation", message);
}
这一段代码中实现了验证订单、计算总价、计算应用折扣、更新订单状态、发送确认邮件等5个步骤;
但是都是像流水账一样,从上到下写到结尾。
良好实践(应用组合函数模式):
public void processOrder(Order order) {
validateOrder(order);
double total = calculateTotal(order);
total = applyDiscount(total);
updateOrderStatus(order, total);
sendConfirmationEmail(order, total);
}
private void validateOrder(Order order) {
if (order == null) {
throw new IllegalArgumentException("Order cannot be null");
}
if (order.getItems().isEmpty()) {
throw new IllegalArgumentException("Order must contain at least one item");
}
}
private double calculateTotal(Order order) {
double total = 0;
for (OrderItem item : order.getItems()) {
total += item.getPrice() * item.getQuantity();
}
return total;
}
private double applyDiscount(double total) {
if (total > 1000) {
return total * 0.9; // 10% 折扣
} else if (total > 500) {
return total * 0.95; // 5% 折扣
}
return total;
}
private void updateOrderStatus(Order order, double total) {
order.setTotal(total);
order.setStatus(OrderStatus.PROCESSED);
orderRepository.save(order);
}
private void sendConfirmationEmail(Order order, double total) {
String message = "Your order #" + order.getId() + " has been processed. Total: $" + total;
emailService.sendEmail(order.getCustomerEmail(), "Order Confirmation", message);
}
在使用组合函数模式思想进行编程后,我们可以看到:
上述步骤中的5大操作,拆分成了5个私有函数,在入口函数processOrder中,进行统一管理调用。
好处:
提高代码的可重用性、模块化和可维护性。通过将复杂的函数拆分成多个简单的函数,每个函数都可以独立开发、测试和维护,从而减少对其他部分的影响。这种模块化的设计使得系统更容易扩展和修改,每个组件的独立性和封装性使得添加、替换或更新特定功能变得更加简单
三、抽象层次一致性原则(SLAP)
抽象层次一致性原则与组合函数密切相关。它要求函数体中的内容必须在同一抽象层次上。如果高层次抽象和底层细节杂糅在一起,代码会显得凌乱,难以理解。
按照组合函数和SLAP原则,我们应当:
- 在入口函数中只显示业务处理的主要步骤
- 通过私有方法封装具体实现细节
- 确保一个函数中的抽象在同一水平上,避免高层抽象和实现细节混杂
代码金字塔结构
满足SLAP实际上是构筑了代码结构的金字塔。金字塔结构是一种自上而下的,符合人类思维逻辑的表达方式。在构筑金字塔的过程中,要求金字塔的每一层都要属于同一个逻辑范畴,同一个抽象层次。
示例
// 顶层抽象 - 业务流程
public void registerNewUser(UserRegistrationRequest request) {
User user = createUserFromRequest(request);
validateUser(user);
saveUser(user);
sendWelcomeEmail(user);
}
// 中层抽象 - 具体步骤
private User createUserFromRequest(UserRegistrationRequest request) {
User user = new User();
mapBasicInfo(user, request);
mapAddressInfo(user, request);
mapPreferences(user, request);
return user;
}
// 底层抽象 - 实现细节
private void mapBasicInfo(User user, UserRegistrationRequest request) {
user.setUsername(request.getUsername());
user.setEmail(request.getEmail());
user.setFirstName(request.getFirstName());
user.setLastName(request.getLastName());
// 其他基本信息映射
}
在上述代码中,入口函数(注册新用户)为顶层抽象,只调用属于业务流程这一层的代码:
- createUserFromRequest方法(根据注册请求生成一个
User对象) - validateUser方法(校验用户)
- saveUser方法(保存用户)
- sendWelcomeEmail方法(发送邮件)
而例如其中的createUserFromRequest方法,则属于是中层抽象,负责具体步骤。它调用的则是底层抽象(具体实现的细节),如其中的
- mapBasicInfo方法(组装基础信息)
- mapAddressInfo 方法(组装地址信息)
- mapPreferences方法(组装偏好信息)
自上往下,这一层的代码,只调用它下一层的代码,不跨层编写代码与调用方法。
四、如何进行抽象
1、寻找共性
抽象的过程是合并同类项、归并分类和寻找共性的过程。将有内在逻辑关系的事物放在一起,然后给这个分类进行命名,这个名字就代表了这组分类的抽象。
示例:重构重复代码
// 重复代码
public void processCustomerA(Customer customer) {
System.out.println("Processing customer: " + customer.getName());
double discount = customer.getTotal() * 0.1;
customer.applyDiscount(discount);
notifyCustomer(customer);
}
public void processVipCustomer(Customer customer) {
System.out.println("Processing customer: " + customer.getName());
double discount = customer.getTotal() * 0.2;
customer.applyDiscount(discount);
notifyCustomer(customer);
}
// 抽象后的代码
public void processCustomer(Customer customer, double discountRate) {
System.out.println("Processing customer: " + customer.getName());
double discount = customer.getTotal() * discountRate;
customer.applyDiscount(discount);
notifyCustomer(customer);
}
public void processRegularCustomer(Customer customer) {
processCustomer(customer, 0.1);
}
public void processVipCustomer(Customer customer) {
processCustomer(customer, 0.2);
}
上述代码中,将两个方法中都有的一段逻辑,抽取出来成一个方法,然后分别给两个方法进行调用,这样抽取出来的方法,便于复用
注意:抽取共同的代码时,也要考虑后续如果有修改,是否修改后不影响另一个方法,所以这里应该只考虑较为稳定的代码块进行抽取
2、提升抽象层次
当我们发现有些东西无法归到一个类别中时,可以通过上升一个抽象层次的放回寺,让它们在更高的抽象层次上产生逻辑关系。
示例:支付方式抽象
// 提升抽象层次前
public class CreditCardPayment {
public void processCreditCardPayment(double amount) {
// 信用卡支付逻辑
}
}
public class PayPalPayment {
public void processPayPalPayment(double amount) {
// PayPal支付逻辑
}
}
// 提升抽象层次后
public interface PaymentMethod {
void processPayment(double amount);
}
public class CreditCardPayment implements PaymentMethod {
@Override
public void processPayment(double amount) {
// 信用卡支付逻辑
}
}
public class PayPalPayment implements PaymentMethod {
@Override
public void processPayment(double amount) {
// PayPal支付逻辑
}
}
上述代码中,原本有信用卡支付,PayPal支付两种支付方式,这两个方式可能在更底层没有共性,但是他们在更高层,则可以抽象出一个新的共性:支付
于是将他们都实现一个支付的方法:PaymentMethod
这样做的好处是:
1、提高了代码的可扩展性:
- 当增加新的支付方式时,只需要创建一个新的类实现
PaymentMethod接口,而无需修改现有代码- 避免修改现有代码:符合开闭原则,对扩展开放,对修改关闭
2、提高代码的可维护性:
- 集中管理支付逻辑,调用者只需调用统一的
processPayment方法,而不需要关心具体的支付方式3、支持多态
- 通过接口抽象,可以在运行时动态切换支付方式,类似于策略模式
3、构筑金字塔
金字塔原理作为一种强大的结构化思维方法,在编程领域展现出独特价值,它帮助开发者将复杂问题分解为有序的层次结构,使代码更加清晰、逻辑更加严密。在实际编程中,我们遵循"自下而上分析问题,自上而下编写代码"的原则,先收集具体需求并识别共性进行抽象,再从高层接口和主流程开始编写实现。
这种思维方式创造了清晰的抽象层次:
- 顶层:代表核心功能和主要意图
- 中层:展示主要逻辑分支和组件关系
- 底层:处理具体实现细节和边界情况
形成了一个既在单个函数内部,也贯穿整个代码库架构设计的金字塔结构。
结论
组合函数模式和抽象层次一致性原则是提升代码可读性和可维护性的有效工具。通过将大函数拆分为多个小函数,并确保每个函数内的抽象层次一致,我们可以构建出更加清晰、易于理解和维护的代码结构。
抽象思维是软件开发人员的核心能力之一,通过不断的训练和实践,我们可以提升自己的抽象能力,从而更好地应对复杂的软件开发挑战。记住,好的抽象能够帮助我们抓住事物本质,简化复杂问题,提高开发效率。
在日常开发中,我们应当有意识地应用这些原则和方法,不断提升自己的代码质量和抽象思维能力。


被折叠的 条评论
为什么被折叠?



