概览

在软件开发过程中,代码的可读性和可维护性往往是衡量代码质量的重要指标。本文将介绍两个能够显著提升代码质量的设计原则:组合函数模式(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);
}

良好实践(应用组合函数模式):

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);
}

抽象层次一致性原则(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());
    // 其他基本信息映射
}

顺便吆喝一句,技术大厂[机遇][机遇],前后端测试捞人,待遇还可以


如何进行抽象

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);
}




——转载自:Lvan

开源硬件平台

还没有评论,抢个沙发!