• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

函数式编程-过程作为返回值的应用分步过程

武飞扬头像
乐征skyline
帮助1

之前的文章提到函数式编程的一等函数(First-class Function)四个性质中有“可以将过程作为返回值”这一点,但这一点在实际使用中不如“将过程作为参数”(高阶函数)用得多。本文介绍一种这个性质用于分步函数的应用。

(注意:本文旨在介绍一种编程技巧,希望可以给读者一点启发,并非介绍某类问题的最优解,实际使用还需具体问题具体分析)

问题场景

相信所有人都接触过一类需求:完成某个任务x,这个任务x由三个步骤a、b、c组成,比如:一个拍照功能,可能包含三个步骤:1. 调起摄像头拍照;2. 编辑优化照片;3. 保存照片到相册。那么这类功能最简单的形式就是:

class Demo{
    public static void main(String[] args) {
        taskX();
    }
    
    static void taskX(){
        //1. 执行第一步
        System.out.println("step 1...");
        //1. 执行第二步
        System.out.println("step 2...");
        //1. 执行第三步
        System.out.println("step 3...");
    }
}

但是,有时候我们并不要求这三个步骤一气呵成,允许完成各个步骤后去做点其他事情,然后再回来做剩余工作。又或者其中某个步骤在有些时候不能直接执行,需要一些准备工作。比如上面所说的拍照示例中,在Android平台调用摄像头得请求到摄像头权限,然后保存相册还需要文件访问权限。这类行为和系统平台相关,如果我们想一套代码运行于多个平台上,就得分离核心功能和平台相关功能。

一种方式是直接把三个步骤写成三个子过程:

class Demo {
    public static void main(String[] args) {
        TaskX.step1();
        System.out.println("do other task....");
        TaskX.step2();
        System.out.println("do other task2....");
        TaskX.step3();
    }
}

class TaskX{
    static void step1() {
        System.out.println("step 1...");
    }

    static void step2() {
        System.out.println("step 1...");
    }

    static void step3() {
        System.out.println("step 1...");
    }
}

但是这样相当于直接暴露三个子步骤,外部可以以任意顺序调用三个步骤,而且如果step2的执行依赖于step1的执行成功,那么就可能引发问题,需要额外的文档/注释来说明,即便有了注释也没法保证安全。(这里假设写TaskX模块的人与使用的人并非同一个)

如果我们可以像下面这样表达一个过程:

主过程:
    1. 子过程1
    2. 子过程2
    3. 子过程3

并且这些子过程的执行分步执行,那么就可以处理这个情况了。

分步过程的实现

如果我们把一个无参无返回值的过程的类型写作() -> void,那么分步函数的类型是不是可以写作() -> -> -> void,表示可以分三步执行,这个表示法稍微再加点元素就是() -> () -> () -> void,跟柯里化的形式很像,对吧?接下来就是要利用这个。

这样的过程用Scala很容易表达:

val taskX = () => {
  println("step 1...")
  () => {
    println("step 2...")
    () => {
      println("step 3...")
    }
  }
}

但是用Java,我们就得写成:

class Demo {
    public static void main(String[] args) {
        Supplier<Supplier<Runnable>> taskXSupplier = doTaskX();
        Supplier<Runnable> step2Supplier = taskXSupplier.get();//执行step1
        System.out.println("do other task....");
        Runnable step3 = step2Supplier.get();//执行step2
        System.out.println("do other task....");
        step3.run();////执行step3
    }

    static Supplier<Supplier<Runnable>> doTaskX() {
        return () -> {
            System.out.println("step 1...");
            return () -> {
                System.out.println("step 2...");
                return () -> System.out.println("step 3...");
            };
        };
    }
}

调用方的代码太难看,我们添加几个函数式接口来稍微美化一下:

class Demo {
    public static void main(String[] args) {
        Step1 step1 = doTaskX();
        Step2 step2 = step1.run();//执行step1
        System.out.println("do other task....");
        Step3 step3 = step2.run();//执行step2
        System.out.println("do other task....");
        step3.run();//执行step3
    }

    static Step1 doTaskX() {
        return () -> {
            System.out.println("step 1...");
            return () -> {
                System.out.println("step 2...");
                return () -> System.out.println("step 3...");
            };
        };
    }

    public interface Step1 {
        Step2 run();
    }

    public interface Step2 {
        Step3 run();
    }

    public interface Step3 extends Runnable {

    }
}

这样每次写接口定义也挺麻烦,我们可以预先定义好Step1Step2...Step10,这样写过程的时候想分几步,就选择对应类型的接口。

简化分步过程的编写

如果不想定义接口,我们可以编写这样一个工具MultiStepTask

class MultiStepTask {
    private final Iterator<Runnable> stepIterator;

    private MultiStepTask(List<Runnable> stepList) {
        stepIterator = stepList.iterator();
    }

    public static MultiStepTask create(Runnable... actions) {
        List<Runnable> stepList = Arrays.stream(actions).toList();
        return new MultiStepTask(stepList);
    }

    public void doNext() {
        if (stepIterator.hasNext()) {
            stepIterator.next().run();
        }
    }

    public void doComplete() {
        while (stepIterator.hasNext()) {
            stepIterator.next().run();
        }
    }

    public boolean isCompleted(){
        return !stepIterator.hasNext();
    }
}

这样我们的创建分步过程的代码就变成了:

static MultiStepTask taskX() {
    return MultiStepTask.create(
            () -> System.out.println("step 1..."),
            () -> System.out.println("step 2..."),
            () -> System.out.println("step 3...")
    );
}

然后调用的代码就是:

class Demo {
    public static void main(String[] args) {
        MultiStepTask taskX = taskX();
        taskX.doNext();//执行step1
        System.out.println("do other task....");
        taskX.doNext();//执行step2
        System.out.println("do other task....");
        taskX.doNext();//执行step3
    }
}

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgaifai
系列文章
更多 icon
同类精品
更多 icon
继续加载