掘金 后端 ( ) • 2024-05-11 09:39

theme: orange

一、背景

很多做业务开发的同学,很多认为平时写业务代码工作就是简单的增删改查,没有挑战,写的代码不会像一些开源框架那么优雅,随着你积累的经验越来越多,你应该跳出这种思维,业务代码也可以写的很优雅,也可以有较好的可读性扩展性

我们可以观察公司内,或者团队内工作时间长一点的同事写的代码,一般都可以找到可以学习的代码。如果你读过Spring源码的同学可以知道,Spring代码有一个非常大的特点就是扩展性极强,他扩展性强一个很重要的原因是恰当的使用了很多设计模式。

本文将结合案例分析平时业务开发中使用频率最高的两种设计模式<<策略模式>>与<<责任链模式>>。

二、策略模式

以支付功能为例子,我们知道支付一般都支持多种支付方式,我们支付功能在后端发起,不同的支付方式其实就是策略,我们可以通过策略模式来完成支付功能。将不同的支付方式代码进行解耦。

public interface PaymentStrategy {
    void pay(double amount);
}

public class WeChatPaymentStrategy implements PaymentStrategy {
    @Override
    public void pay(double amount) {
        // 微信支付逻辑
    }
}

public class AlipayPaymentStrategy implements PaymentStrategy {
    @Override
    public void pay(double amount) {
        // 支付宝支付逻辑
    }
}
import java.util.HashMap;
import java.util.Map;

public class PaymentStrategyFactory {
    private Map<String, PaymentStrategy> strategies = new HashMap<>();

    public void registerStrategy(String name, PaymentStrategy strategy) {
        strategies.put(name, strategy);
    }

    public PaymentStrategy getStrategy(String name) {
        return strategies.get(name);
    }
}

使用策略模式:

public class Main {
    public static void main(String[] args) {
        PaymentStrategyFactory factory = new PaymentStrategyFactory();

        // 注册支付策略
        factory.registerStrategy("wechat", new WeChatPaymentStrategy());
        factory.registerStrategy("alipay", new AlipayPaymentStrategy());

        // 获取支付策略
        PaymentStrategy wechatStrategy = factory.getStrategy("wechat");
        PaymentStrategy alipayStrategy = factory.getStrategy("alipay");

        // 使用支付策略
        wechatStrategy.pay(100.0);
        alipayStrategy.pay(150.0);
    }
}

通过上方实际例子,我们可以发现策略模式很好的将不同的支付方式进行了解耦,如果业务需要新增一种支付方式,我们不需要改原先的支付方式代码,这样可以达到对扩展开发,对修复关闭的设计原则,对于测试范围也可控了。

三、异步形式的责任链模式

责任链模式在业务开发中也是可以使用频率高的设计模式,我们以展示商品为例:

详情.jpg

商品页面,包含商品标题,主图等基础信息,商品价格,商品优惠活动,商品库存等等。

在微服务架构中,商品基础信息,商品价格,商品优惠活动,商品库存都是一个独立的领域服务,他们通过rpc进行通信,如果我们一个一个调用,这样代码看起来就是一本流水账,过程式编码,整体的响应时间也会拉长。最初的想法可能是这样写的:

1、查询商品基础信息
2、查询商品价格
3、查询商品活动
4、查询商品库存

由于这几个服务逻辑比较独立,其实是可以并行调用,我们可以结合责任链模式CompletableFuture进行优化。

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;

// 商品信息类
class ProductInfo {
    //模拟商品基础信息
    private String basicInfo;
    //商品价格
    private double price;
    //模拟商品活动信息
    private String activity;
    //模拟商品库存
    private int inventory;

    // 省略构造函数、getter和setter
}

// 定义责任链节点接口 补全商品处理接口
interface ServiceHandler {
    CompletableFuture<Void> handle(ProductInfo productInfo);
}

// 实现商品基础信息补全节点
class BasicInfoServiceHandler implements ServiceHandler {
    @Override
    public CompletableFuture<Void> handle(ProductInfo productInfo) {
        CompletableFuture<Void> future = new CompletableFuture<>();
        // 模拟异步调用外部服务,修改商品信息对象
        CompletableFuture.runAsync(() -> {
            // 补充商品基础信息数据
            productInfo.setBasicInfo("...");
            future.complete(null);
        });
        return future;
    }
}

// 实现商品价格补全节点
class PriceServiceHandler implements ServiceHandler {
    @Override
    public CompletableFuture<Void> handle(ProductInfo productInfo) {
        CompletableFuture<Void> future = new CompletableFuture<>();
        // 模拟异步调用外部服务,修改商品信息对象
        CompletableFuture.runAsync(() -> {
            // 补充商品价格数据
            productInfo.setPrice(100.0);
            future.complete(null);
        });
        return future;
    }
}

// 实现商品活动补全节点
class ActivityServiceHandler implements ServiceHandler {
    @Override
    public CompletableFuture<Void> handle(ProductInfo productInfo) {
        CompletableFuture<Void> future = new CompletableFuture<>();
        // 模拟异步调用外部服务,修改商品信息对象
        CompletableFuture.runAsync(() -> {
            // 补充商品活动数据
            productInfo.setActivity("...");
            future.complete(null);
        });
        return future;
    }
}

// 实现商品库存服务节点
class InventoryServiceHandler implements ServiceHandler {
    @Override
    public CompletableFuture<Void> handle(ProductInfo productInfo) {
        CompletableFuture<Void> future = new CompletableFuture<>();
        // 模拟异步调用外部服务,修改商品信息对象
        CompletableFuture.runAsync(() -> {
            // 补充商品库存数据
            productInfo.setInventory(100);
            future.complete(null);
        });
        return future;
    }
}

// 责任链
class ServiceHandlerChain {
    private List<ServiceHandler> handlers = new ArrayList<>();

    public void addHandler(ServiceHandler handler) {
        handlers.add(handler);
    }

    public ProductInfo execute(ProductInfo productInfo) {
        CompletableFuture<Void>[] futures = handlers.stream()
                .map(handler -> handler.handle(productInfo))
                .toArray(CompletableFuture[]::new);
        //等待所有的异步补全任务处理完成   
        CompletableFuture<Void> allOfFuture = CompletableFuture.allOf(futures);
        allOfFuture.join(); // 等待所有异步操作完成
        return productInfo;
    }
}

调用责任链,实现商品信息补全:

public class Main {
    public static void main(String[] args) {
        // 创建商品信息对象
        ProductInfo productInfo = new ProductInfo();

        // 创建责任链
        ServiceHandlerChain chain = new ServiceHandlerChain();
        chain.addHandler(new BasicInfoServiceHandler());
        chain.addHandler(new PriceServiceHandler());
        chain.addHandler(new ActivityServiceHandler());
        chain.addHandler(new InventoryServiceHandler());

        // 执行责任链
        ProductInfo result = chain.execute(productInfo);

        // 处理最终的商品信息对象
    }
}

通过责任链模式+CompletableFuture优化调整,我们将商品各自补全逻辑进行拆分互不依赖,如果需要展示商品其他信息可以扩展新节点,而且每个节点都是异步化处理,提升整体响应速度

四、总结

本文结合实际例子对于业务开发工作中最常用两个设计模式进行了总结,适当使用设计模式可以把一些业务需求实现的更加优雅,对于未来的扩展性也打下了基础,给后期开发测试都减少了工作量,设计模式可以结合我们实际业务适当调整,变成适合我们自己业务的模式,比如上面的异步形式的责任链

关注我们一起学习技术吧,坚持相信有输入一定要有输出,希望我们的技术能力越来越强大。

image.png