【精通函数式编程】(二)代码的行为参数化传递

简介: 在我们平时的工作中,我们的需求是不断变化的,这个接口怎么设计更通用一些呢?

image.png

前言

📫 作者简介:小明 java 问道之路,专注于研究计算机底层,就职于金融公司后端高级工程师,擅长交易领域的高安全/可用/并发/性能的设计和架构📫

🏆 Java 领域优质创作者、阿里云专家博主、华为云享专家🏆

🔥 如果此文还不错的话,还请👍关注点赞收藏三连支持👍一下博主哦

本文导读

在我们平时的工作中,我们的需求是不断变化的,比如一个实现一个清算的函数settlement() 时,需要支持我们使用银行卡、信用卡、红包等等不同的支付方式的结算,如果我们使用了一种新的支付工具,例如给特定用户免单的时候,这个接口怎么设计更通用一些呢?

一、行为参数化你真的没用过吗?

行为参数化是一个设计模式,扩展性会比直接写业务代码要好很多,这种模式或许你是用过的,例如对List排序,筛选,或者执行一个Thread,但是如果不用lambda表达式,这种写法上面是非常复杂的

// Comparator排序
    list.sort(new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o1.compareTo(o2);
        }
    });
    // 用 Runnable 使用Java线程
    Thread d = new Thread(new Runnable() {
        @Override
        public void run() {
        }
    });

二、行为参数化应用和外观模式的关系

下面以一个结算接口为例子,什么是行为参数化,首先定义一个接口和四个实现类,Facade Pattern又叫外观模式,这么做的目的是提供了一个统一的接口,提供了一个统一的接口,用来访问子类中的一群接口,属于结构性设计模式。

image.png

外观模式结构图

public interface SettlementService {
    // 这是一个结算的接口
    CouponInfo settlement();
}
/**
 * 这是四个实现类
 */
class PlatformCouponSettlementServiceImpl implements SettlementService {
    @Override
    public CouponInfo settlement() {
        System.out.println("结算平台券");
        System.out.println("落库");
        return new CouponInfo();
    }
}
class DiscountSettlementServiceImpl implements SettlementService {
    @Override
    public CouponInfo settlement() {
        System.out.println("结算跨店铺满减");
        System.out.println("落库");
        return new CouponInfo();
    }
}
class MerchantCouponSettlementServiceImpl implements SettlementService {
    @Override
    public CouponInfo settlement() {
        System.out.println("结算商家券1");
        System.out.println("落库");
        return null;
    }
}
class FreeSettlementServiceImpl implements SettlementService {
    @Override
    public CouponInfo settlement() {
        System.out.println("结算免单福利1");
        System.out.println("落库");
        return null;
    }
}

然后我们,通过行为参数化,传递(使用)代码,通过传递不同的代码,下面是最简单的行为参数化,但是这样的代码很不好,也没有什么扩展性,我们逐步看应该写成什么样子

public static void main(String[] args) {
        // 最简单的行为参数化, 但是这样的代码很不好,也没有什么扩展性
        CouponInfo a = settlement(new ArrayList<>(), new DiscountSettlementServiceImpl());
        CouponInfo b = settlement(new ArrayList<>(), new PlatformCouponSettlementServiceImpl());
        // 执行结果为:结算跨店铺满减 结算平台券
    }
    CouponInfo settlement(List<OrderInfo> orderList, SettlementService settlementService) {
        return settlementService.settlement();
    }

三、行为参数化(匿名内部类、Lambda表达式)

匿名类和普通的类类似,但是匿名类顾名思义不需要声明class的名字,注:匿名类原理:编译时,Java编译器会生成 匿名类所在方法()所在类的class文件(test.class),也会生成 匿名类 自己的class文件(例如foo$1.class)。由于是两个文件, 匿名内部类 无法访问当前类对象 .class文件中的某方法的局部变量。所以在新建匿名 Java编译器(JVM)索性将 变量复制一份

public static void main(String[] args) {
    // 匿名内部类的方式
    CouponInfo c = function.settlement(orderList, new SettlementService() {
        @Override
        public CouponInfo settlement() {
            System.out.println("结算商家券");
            return new CouponInfo();
        }
    });
    // lambda表达式写法
    CouponInfo d = function.settlement(orderList, () -> {
        System.out.println("结算免单福利");
        return new CouponInfo();
    });
}
CouponInfo settlement(List<OrderInfo> orderList, SettlementService settlementService) {
    return settlementService.settlement();
}

匿名内部类,实际上并不很好用,一般很笨拙和创建一个类没什么太大区别,二是变量名往往和外面的一样容易混淆,而Lambda表达式重写之后就很简洁

四、行为参数化实战(写一个调用结算的接口)

真正实战一个接口都是几千行代码,我们这里取调用入口的部分,实现统一用System.out.println()代替,首先需要一个调用的方法,下面代码的settlement()方法,作为入口,settlement方法中使用工厂模式(如果是生产代码推荐 配置化+工厂模式,如果上下线某种优惠,不需要改代码),逐一遍历每种优惠,进行结算

image.png

工厂模式类图

Consumer是一个函数式接口,具体的使用场景就是可以提前记录我们的某些操作,然后在后面再去执行,比如说:当我们在a方法中,需要把某些参数赋值给一个Integer类型的对象,而该对象只有在b方法才能赋值,那么我们可以在a方法中使用consumer记录我们要执行的操作,再把consumer作为参数传递到b方法执行

// 交易链路调用结算接口
    List<CouponInfo> settlement = settlement(orderList);
    /**
     * 结算方法入口,使用ccList遍历每个优惠类型settlementService,settlement方法参数使用lambda表达式做行为参数化
     */
    List<CouponInfo> settlement(List<OrderInfo> orderList) {
        List<CouponInfo> couponInfos = new ArrayList<>();
        // 注:ccList可以是一个bean,可以是一个工厂
        for (SettlementService settlementService : new SettlementServiceFactory().createSettlementService())
            couponInfos.add(settlementService.settlement(orderList, 
                    couponInfo -> {System.out.println("落库"); }));
        return couponInfos;
    }
// 静态工厂
class SettlementServiceFactory {
    static List<SettlementService> ccList = new ArrayList<>();
    public SettlementServiceFactory() {
        ccList.add(new PlatformCouponSettlementServiceImpl());
        ccList.add(new DiscountSettlementServiceImpl());
    }
    public List<SettlementService> createSettlementService() {
        return ccList;
    }
}
/**
 * 这是几个实现类
 */
class PlatformCouponSettlementServiceImpl implements SettlementService {
    @Override
    public CouponInfo settlement(List<OrderInfo> orderList, Consumer<CouponInfo> consumer) {
        CouponInfo couponInfo = new CouponInfo();
        System.out.println("结算平台券");
        consumer.accept(couponInfo);
        return couponInfo;
    }
}
class DiscountSettlementServiceImpl implements SettlementService {
    @Override
    public CouponInfo settlement(List<OrderInfo> orderList, Consumer<CouponInfo> consumer) {
        CouponInfo couponInfo = new CouponInfo();
        System.out.println("结算跨店铺满减");
        consumer.accept(couponInfo);
        return couponInfo;
    }
}

五、回过头看之前的行为参数化,怎么改进?

// Comparator排序
        list.sort((o1, o2) -> o1.compareTo(o2));
        list.sort(Integer::compareTo);
        // 用 Runnable 使用Java线程
        Thread d1 = new Thread(() -> {});

小结

代码的行为参数化传递是一种新的代码模式,我们之前可能使用匿名内部类的时候用过,但是现在有更加简介的方式,文章通过普通传对象、匿名内部类、Lambda表达式几种行为参数化做对比,说明java8中有更加简介的方式实现行为参数化,最后通过一个接口实战运用学到的lambda表达式的行为参数化

相关文章
|
存储 分布式计算 大数据
阿里云 EMR 强势助力,与阿里云大数据体系共创辉煌,把握时代热点,开启生态建设之旅
【8月更文挑战第26天】阿里云EMR(Elastic MapReduce)是一种大数据处理服务,与阿里云的多个服务紧密结合,共同构建了完善的大数据生态系统。EMR与对象存储服务(OSS)集成,利用OSS提供可靠、低成本且可扩展的数据存储;与MaxCompute集成,实现深度数据分析和挖掘;还支持数据湖构建服务,加速数据湖的搭建并简化数据管理与分析过程。EMR提供多种编程接口及工具,如Hive、Spark和Flink等,帮助用户高效完成大数据处理任务。
417 2
|
缓存 负载均衡 Java
Java一分钟之-Spring Cloud Netflix Ribbon:客户端负载均衡
【6月更文挑战第9天】Spring Cloud Netflix Ribbon是客户端负载均衡器,用于服务间的智能路由。本文介绍了Ribbon的基本概念、快速入门步骤,包括添加依赖、配置服务调用和使用RestTemplate。此外,还讨论了常见问题,如服务实例选择不均、超时和重试设置不当、服务列表更新不及时,并提供了相应的解决策略。最后,展示了如何自定义负载均衡策略。理解并正确使用Ribbon能提升微服务架构的稳定性和效率。
456 3
|
11月前
|
人工智能 大数据 云计算
【AI系统】AI 发展驱动力
本文介绍了阿里云在2023年云栖大会上发布的多项新技术和产品,涵盖云计算、大数据、人工智能等领域,展示了阿里云最新的技术成果和行业解决方案,助力企业数字化转型。
|
缓存 Java 调度
【Java 并发秘籍】线程池大作战:揭秘 JDK 中的线程池家族!
【8月更文挑战第24天】Java的并发库提供多种线程池以应对不同的多线程编程需求。本文通过实例介绍了四种主要线程池:固定大小线程池、可缓存线程池、单一线程线程池及定时任务线程池。固定大小线程池通过预设线程数管理任务队列;可缓存线程池能根据需要动态调整线程数量;单一线程线程池确保任务顺序执行;定时任务线程池支持周期性或延时任务调度。了解并正确选用这些线程池有助于提高程序效率和资源利用率。
192 2
|
存储 Kubernetes Cloud Native
K8s 资源全汇总 | K8s 大咖带你 31 堂课从零入门 K8s
关注“阿里巴巴云原生”公众号,后台回复“入门”,即可下载课程全部 PPT 合集!
 K8s 资源全汇总 |  K8s 大咖带你 31 堂课从零入门 K8s
|
消息中间件 Windows
win10 安装RabbitMQ的步骤--和报错解决
win10 安装RabbitMQ的步骤--和报错解决
361 4
|
运维 监控 API
深入了解微服务架构:优势与挑战
【10月更文挑战第7天】深入了解微服务架构:优势与挑战
|
NoSQL MongoDB 数据库
国内唯一 阿里云荣膺MongoDB“2024年度DBaaS认证合作伙伴奖”
阿里云连续第五年斩获MongoDB合作伙伴奖项,也是唯一获此殊荣的中国云厂商。一起学习MongoDB副本集的选举机制以及可能会出现的特殊情况。
国内唯一 阿里云荣膺MongoDB“2024年度DBaaS认证合作伙伴奖”
|
算法 C++
【洛谷 P1090】[NOIP2004 提高组] 合并果子(贪心算法+哈夫曼编码+优先队列)
该编程题目要求设计算法,将不同种类的果子合并成一堆,使得消耗的体力最小。给定果子种类数`n`(1至10000)和每种果子的数量,需输出合并的最小体力值。使用优先队列(最小堆),每次取出两个数量最少的果子堆合并,并更新总体力消耗。样例输入为3种果子(1, 2, 9),输出最小体力耗费为15。提供的AC代码采用C++实现,通过优先队列优化合并顺序。
280 0
|
小程序 安全
微信小程序自定义底部导航栏
微信小程序自定义底部导航栏