链式编程

2023/08/03 零散知识点 共 7125 字,约 21 分钟
闷骚的程序员

1. 什么是链式编程?

大家还记得java8的stream流式操作吗?
一个方法起始点,然后通过.语法不停地往后一直像调用链一样的,直到最后需要构建对象时再统一返回,这种通过.语法链式调用的编程方式就是链式编程。

2. 如何实现链式编程?

链式编程的要点就是,要求一个方法执行完逻辑之后返回当前对象,从而达到链式编程的目的。

3. 普通链式编程

一个普通类,其中的实例方法在执行完业务操作后,返回自身对象,即this。
zeh.myjavase.code01javase.Demo17LianShiBianChenga

package zeh.myjavase.code01javase;

// 链式编程(普通链式编程,非静态方法直接返回当前对象this)
public class Demo17LianShiBianChenga {
    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    //设置完成后直接返回当前this
    //对于静态方法,直接调用,static的方法内部不能使用this,这样在工具类中该如何使用链式编程呢?
    //看下一个案例。
    public Demo17LianShiBianChenga setName(String name) {
        this.name = name;
        return this;
    }

    public Integer getAge() {
        return age;
    }

    // 设置完成后直接返回当前this
    public Demo17LianShiBianChenga setAge(Integer age) {
        this.age = age;
        return this;
    }

    // 执行链式编程
    public static void main(String[] args) {
        //非静态方法实现链式编程,就是在方法内部执行完成后返回this当前对象。
        Demo17LianShiBianChenga demo17LianShiBianChenga = new Demo17LianShiBianChenga().setName("赵二虎").setAge(22);
        System.out.println("name:" + demo17LianShiBianChenga.getName());
        System.out.println("age:" + demo17LianShiBianChenga.getAge());
    }
}

运行:

name:赵二虎
age:22

普通链式编程,在非静态方法执行完成后,直接返回当前对象this。

4. 静态方法的链式编程

上一个案例在方法执行完成后直接返回当前对象this。
但如果我们要提供的方法是一个静态方法呢?
静态方法内部是不能直接返回this的,这个时候该怎么实现呢?

4.1 直接在静态方法中创建当前类对象

zeh.myjavase.code01javase.Demo17LianShiBianChengStatic1

package zeh.myjavase.code01javase;

// 静态方法的链式编程
public class Demo17LianShiBianChengStatic1 {

    private String name;

    private int age;

    public static Demo17LianShiBianChengStatic1 builder() {
        return new Demo17LianShiBianChengStatic1();
    }

    public String getName() {
        return name;
    }

    public Demo17LianShiBianChengStatic1 setName(String name) {
        this.name = name;
        return this;
    }

    public int getAge() {
        return age;
    }

    public Demo17LianShiBianChengStatic1 setAge(int age) {
        this.age = age;
        return this;
    }


    public static void main(String[] args) {
        Demo17LianShiBianChengStatic1 shiBianChengStatic1 = Demo17LianShiBianChengStatic1.builder().setAge(22).setName("Eric");
        System.out.println("name is : " + shiBianChengStatic1.getName());
        System.out.println("age is : " + shiBianChengStatic1.getAge());
    }
}

思路很简单,里面的静态方法,先直接new一个当前类的对象返回,然后其他方法就可以直接使用this,进行返回了。

运行:

name is : Eric
age is : 22

4.2 在静态方法中构建一个静态内部类对象

zeh.myjavase.code01javase.Demo17LianShiBianChengStatic2

package zeh.myjavase.code01javase;

// 使用静态内部类构建链式编程
public class Demo17LianShiBianChengStatic2 {
    // 将静态方法封装成一个专门产生内部类对象的方法,将所有的链式编程都写在内部类中。
    public static Builder builder() {
        return new Builder();
    }

    // 静态内部类构建链式编程
    // 备注:该类必须是静态的,因为上面用于创建该对象的builder()方法是静态的。
    public static class Builder {
        private StringBuilder chinese;

        private StringBuilder getChinese() {
            return chinese;
        }

        private void setChinese(StringBuilder chinese) {
            this.chinese = chinese;
        }

        //内部类的链式方法一
        public Demo17LianShiBianChengStatic2.Builder numberToChinese(Integer number) {
            String[] numbers = {"零", "一", "二", "三", "四", "五", "六", "七", "八", "九"};
            String[] units = {"", "十", "百", "千", "万", "十", "百", "千", "亿", "十"};
            String sign = number < 0 ? "负" : "";
            if (number < 0) {
                number = -number;
            }
            StringBuilder result = new StringBuilder(sign);
            String string = String.valueOf(number);
            int n = string.length();
            char[] numberCharArray = string.toCharArray();
            for (int i = 0; i < n; i++) {
                int digNum = n - i; // 位数
                int num = numberCharArray[i] - '0';
                if (num != 0) {
                    result.append(numbers[num]).append(units[digNum - 1]);
                    continue;
                }

                if (result.toString().endsWith(numbers[0])) {
                    // 如果是单位所在的位数,则去除上一个0,加上单位
                    if (digNum % 4 == 1) {
                        result.deleteCharAt(result.length() - 1);
                        result.append(units[digNum - 1]);
                    }
                } else {
                    result.append(numbers[0]);
                }
            }
            setChinese(result);
            return this;
        }

        //内部类的链式方法2
        public Demo17LianShiBianChengStatic2.Builder append(String str) {
            chinese.append(str);
            return this;
        }

        //内部类的链式方法3
        public String build() {
            return this.chinese.toString();
        }
    }

    public static void main(String[] args) {
        //使用 内部类的 builder 对静态方法进行链式编程
        String result = Demo17LianShiBianChengStatic2.builder().numberToChinese(100).append("、").build();
        System.out.println("result:" + result);
    }

}

上面案例通过在当前类中定义一个静态方法,但是该静态方法返回的不是当前类的实例对象,而是一个当前类里面的静态内部类对象,然后后续所有的链式编程都是基于该静态内部类构建的。

运行:

result:一百、

5. lombok的链式编程参考

使用lombok构建一个原始类:

package zeh.myjavase.code01javase.pojo;

import lombok.Builder;

import java.util.ArrayList;
import java.util.List;

@Builder
public class LinkedPo {

    private String name;

    private int age;

    private List<String> hobbies;

    public static void main(String[] args) {
        LinkedPo.builder().age(12).hobbies(new ArrayList<>()).name("Eric").build();
    }
}

我们看下编译后的代码是什么样子的?

package zeh.myjavase.code01javase.pojo;

import java.util.ArrayList;
import java.util.List;

public class LinkedPo {
    private String name;
    private int age;
    private List<String> hobbies;

    public static void main(String[] args) {
        builder().age(12).hobbies(new ArrayList()).name("Eric").build();
    }

    LinkedPo(String name, int age, List<String> hobbies) {
        this.name = name;
        this.age = age;
        this.hobbies = hobbies;
    }

    public static LinkedPo.LinkedPoBuilder builder() {
        return new LinkedPo.LinkedPoBuilder();
    }

    public static class LinkedPoBuilder {
        private String name;
        private int age;
        private List<String> hobbies;

        LinkedPoBuilder() {
        }

        public LinkedPo.LinkedPoBuilder name(String name) {
            this.name = name;
            return this;
        }

        public LinkedPo.LinkedPoBuilder age(int age) {
            this.age = age;
            return this;
        }

        public LinkedPo.LinkedPoBuilder hobbies(List<String> hobbies) {
            this.hobbies = hobbies;
            return this;
        }

        public LinkedPo build() {
            return new LinkedPo(this.name, this.age, this.hobbies);
        }

        public String toString() {
            return "LinkedPo.LinkedPoBuilder(name=" + this.name + ", age=" + this.age + ", hobbies=" + this.hobbies + ")";
        }
    }
}

可以看到,lombok插件在原始代码的基础上,就是增加了一个静态内部类专门用来实现链式编程。
(1)先通过静态方法builder()返回一个静态内部类对象。
(2)静态内部类持有和原始类完全相同的属性成员。
(3)在通过返回的静态内部类对象去挨个设置静态内部类中的成员属性值。
(4)最后通过静态内部类提供的实例方法build()返回原始类的对象,同时将静态内部类的成员同步给原始类即可。

6. 链式编程的灵活使用

package zeh.myjavase.code01javase;

public class Demo17LianShiBianChengStatic3 {

    private String plan;

    private String name;

    private int age;

    public String like;

    public Demo17LianShiBianChengStatic3(String plan, String name, int age, String like) {
        this.plan = plan;
        this.name = name;
        this.age = age;
        this.like = like;
    }

    public static NameConfigurer plan(String plan) {
        return new NameConfigurer(plan);
    }


    static class NameConfigurer {
        private String plan;

        public NameConfigurer(String plan) {
            this.plan = plan;
        }

        public LikeConfigurer name(String name) {
            return new LikeConfigurer(this.plan, name);
        }
    }

    static class LikeConfigurer {
        private String plan;
        private String name;

        public LikeConfigurer(String plan, String name) {
            this.plan = plan;
            this.name = name;
        }

        public AgeConfiguer like(String like) {
            return new AgeConfiguer(this.plan, this.name, like);
        }
    }

    static class AgeConfiguer {
        private String plan;
        private String name;
        private String like;
        private int age;

        public AgeConfiguer(String plan, String name, String like) {
            this.plan = plan;
            this.name = name;
            this.like = like;
        }

        public AgeConfiguer age(int age) {
            this.age = age;
            return this;
        }

        public Demo17LianShiBianChengStatic3 build() {
            return new Demo17LianShiBianChengStatic3(this.plan, this.name, this.age, this.like);
        }
    }


    @Override
    public String toString() {
        return "Demo17LianShiBianChengStatic3{" +
                "plan='" + plan + '\'' +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", like='" + like + '\'' +
                '}';
    }

    public static void main(String[] args) {
        Demo17LianShiBianChengStatic3 demo17LianShiBianChengStatic3 = Demo17LianShiBianChengStatic3.plan("Study").name("Eric").like("football").age(22).build();
        System.out.println(demo17LianShiBianChengStatic3);
    }

}

有时候我们要求在链式编程中,每.一次进去只能传递一个属性,这个属性完了之后再设置另外一个属性…
最后直到我们需要的属性都确保设置进去了,才能最终去build对象出来。
因为在有的场景下,我们build对象的逻辑是严格依赖于我们传递进去的属性值的,因此不能暴露给客户端不传递属性值就可以直接build的机会。
上面的案例就实现了这一点,它的思路就是:
每设置一个属性进去,返回的都是一个静态内部类实例。
每返回一个静态内部类实例,它内部都要确保以成员变量的方式接收上游设置进去的属性值。
就这样一步步往下透传所有属性,直到传递到最底层的那个静态内部类,它负责build最外层我们需要的对象。
注意,最底层的静态内部类最终持有的成员和最外层的类持有的成员肯定是一致的,然后它负责构建最外层的对象,将链式中一步步透传下来的属性值同步到最外层对象中去。

文档信息

Search

    Table of Contents