写点什么

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

  • 2022 年 7 月 21 日
  • 本文字数:3502 字

    阅读完需:约 11 分钟

前言:

📫 作者简介:小明 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 又叫外观模式,这么做的目的是提供了一个统一的接口,提供了一个统一的接口,用来访问子类中的一群接口,属于结构性设计模式。

​外观模式结构图

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 方法中使用工厂模式(如果是生产代码推荐 配置化+工厂模式,如果上下线某种优惠,不需要改代码),逐一遍历每种优惠,进行结算

​工厂模式类图

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 表达式的行为参数化


发布于: 刚刚阅读数: 4
用户头像

物有本末,事有终始。知所先后,则近道矣 2020.03.20 加入

🏆CSDNJava领域优质创作者/阿里云专家博主/华为云享专家 📫就职某大型金融互联网公司后端高级工程师 👍专注于研究计算机底层/Java/架构/设计模式/算法

评论

发布
暂无评论
【函数式编程实战】(二)代码的行为参数化传递_函数式接口_小明Java问道之路_InfoQ写作社区