Java chain programming and Builder (builder) design pattern

1. Chain programming

1.1. Interpretation 

        Chained programming, also called cascading programming, returns a this object pointing to the object itself when calling the function of the object, to achieve the chain effect, and can be called in cascade. 

1.2. Features 

        Multiple method calls can be called through one method , and multiple method calls can be linked together to form a "chain" , thereby improving the readability of the code. 

 1.2. Principle

        The principle of chain programming is to return a this object , that is, return the object itself, so as to achieve the chain effect. 

1.3. Advantages 

        Strong programming, concise code, and strong readability.

1.4. Conditions of use 

        The method returns the current object, that is, returns this; or the method supports chain calls, that is, returns the object that called the method. 

1.4.1. Example of use 

import lombok.Data;

/**
 * 要实现链式编程,我们需要让setParam1()和setParam2()都返回this。
 * 当手写set方法时,无需 @Data注解
 * 在此仅为了使用 @Data的toString()
 */
@Data
public class Sample {

    private String param1;
    private String param2;
    private String param3;

    public Sample setParam1(String param1) {
        this.param1 = param1;
        return this;
    }
    public Sample setParam2(String param2) {
        this.param2 = param2;
        return this;
    }
    public Sample setParam3(String param3) {
        this.param3 = param3;
        return this;
    }
}

 1.4.2. Example output

Two, String chain example

2.1. Source code 

         For example, after converting an object toString() into a String, or after assigning a String object valueOf(), perform concat() splicing, replace() replacement, and substring() interception . There are many conforming methods, and most of them can be assembled to conform to chain programming.

Three, StringBuffer and StringBuilder chain example

3.1. Source code 

3.2. Example of use 

        At the same time, the stream of List under the Collection interface also uses chain programming, which will not be introduced here. 

4. @Accessors annotation enables chain programming 

4.1. Source code 

@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.SOURCE)
public @interface Accessors {
	/**
	 * If true, accessors will be named after the field and not include a {@code get} or {@code set}
	 * prefix. If true and {@code chain} is omitted, {@code chain} defaults to {@code true}.
	 * <strong>default: false</strong>
	 * 
	 * @return Whether or not to make fluent methods (named {@code fieldName()}, not for example {@code setFieldName}).
	 */
	boolean fluent() default false;
	
	/**
	 * If true, setters return {@code this} instead of {@code void}.
     * 如果是true,setters方法返回{this} 而不是{void}
     * 
	 * <strong>default: false</strong>, unless {@code fluent=true}, then <strong>default: true</strong>
	 * 
	 * @return Whether or not setters should return themselves (chaining) or {@code void} (no chaining).
	 */
	boolean chain() default false;
	
	/**
	 * If present, only fields with any of the stated prefixes are given the getter/setter treatment.
	 * Note that a prefix only counts if the next character is NOT a lowercase character or the last
	 * letter of the prefix is not a letter (for instance an underscore). If multiple fields
	 * all turn into the same name when the prefix is stripped, an error will be generated.
	 * 
	 * @return If you are in the habit of prefixing your fields (for example, you name them {@code fFieldName}, specify such prefixes here).
	 */
	String[] prefix() default {};
}

 4.2. Use of annotations

@Data
@Accessors(chain = true)
public class Sample {

    private String param1;
    private String param2;
    private String param3;

}

        The above code is a simplification of the sample code in 1.4.1. It can be seen that when using annotations, there is no need to design specific chain implementations yourself, and annotations will start chain operations. This annotation is commonly used on entity classes in Spring. 

        At the same time, when using @Accessors(fluent = true), omit the set and get prefixes when assigning and fetching values ​​to objects, and you can directly use objects to point out elements without set. 

 4.3. Testing

 5. Use the Builder mode to realize Java chain programming

        The Builder pattern is an object creation pattern that allows us to create an object and set its property values. An example Builder pattern implementation looks like this:        

public class Sample {

    private String name;
    private String gender;
    private String age;

    private Sample(){}
    public static SampleBuilder builder() {
        return new SampleBuilder();
    }

    public static class SampleBuilder {

        private Sample sample = new Sample();

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

        public SampleBuilder setGender(String gender) {
            sample.gender = gender;
            return this;
        }
        public SampleBuilder setAge(String age) {
            sample.age = age;
            return this;
        }

        public Sample build() {
            return sample;
        }
    }
}

        In the above code example, we first created a Sample class. To implement the Builder pattern, we also created a SampleBuilder class. There are two with methods in the SampleBuilder class, which are used to set the name attribute, gender attribute and age attribute respectively. They all return SampleBuilder objects, so that chain calls can be realized. After the parameters are set, we create the Sample object through the build method. An example of chain programming using the Builder pattern is as follows:

         In the above example, we created the SampleBuilder object through Sample.builder(), and used the set method to set the parameters. After the setup is complete, we create the Sample object through the build method.

        The above is the simplest builder chain programming. The simplicity lies in that it can form a chain and assign values, that's all. But if you need to get the value and check the parameters, this class is not enough, and more regulations need to be added, which leads to the builder pattern, one of the important design patterns of Java.

Six, Builder (builder) design pattern 

6.1. Practical scope

        When the algorithm for creating a complex object should be independent of that object's constituent parts and how they are assembled .

        When the construction procedure must allow different representations of the objects being constructed.

 6.2. Role

        In such a design pattern , there are the following roles:

        1 builder: Specifies the abstract interface for each component that creates a product object.

        2 ConcreteBuilder: Implement the interface of Builder to construct and assemble the various parts of the product, define and specify the representation it creates, and provide an interface for retrieving the product.

        3 Director: Construct an object using the Builder interface.

        4 Product: Represents the complex object being constructed. ConcreteBuilder creates an internal representation of the product and defines its assembly process, including classes that define the constituent parts, including the interfaces for assembling these parts into the final product .

 6.3. Builder mode creation steps

        1) In the class, create a static inner class that contains all the properties of the outer class;

        2) In the inner class, assign a value to each attribute;

        3) Create  a build  method in the inner class and return the outer class object

4) Create a static builder method         in the external class  and return the internal class object

6.4. Examples of defective products 

public class Sample {

    //Sample 类的属性都是不可变的。所有的属性都添加了final修饰符,在构造方法中设置了值。
    private final String name;
    private final String gender;
    private final Double age;
    //对外只提供getters方法。
    public String getName() {
        return name;
    }
    public String getGender() {
        return gender;
    }
    public Double getAge() {
        return age;
    }
    //Sample 类的构造方法是私有的。也就是说调用者不能直接创建User对象。
    private Sample(SampleBuilder builder){
        this.name = builder.name;
        this.gender = builder.gender;
        this.age = builder.age;
    }
    public static class SampleBuilder {
        private String name;
        private String gender;
        private Double age;
        public SampleBuilder SampleBuilder(){
            return new SampleBuilder();
        }

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

        public SampleBuilder setGender(String gender) {
            this.gender = gender;
            return this;
        }
        public SampleBuilder setAge(Double age) {
            this.age = age;
            return this;
        }

        public Sample build() {
            //添加参数校验
            Sample sample = new Sample(this);
            if (sample.getAge() < 0 || sample.getAge() > 125){
                throw new IllegalStateException("Age out of range:" + sample.getAge());
            }
            if (!("男".equals(sample.getGender()) || "女".equals(sample.getGender()))){
                throw new IllegalStateException("Error Gender Input:" + sample.getGender());
            }
            return sample;
        }
    }
}

         It can be seen that the basic chain programming is formed, and the parameters will be verified at the same time, which meets the requirements. But the reason why it is called a defective product is because it is only a build chain development, but when it comes to design patterns , it is obviously "incompatible with virtue".

6.5. Simple example

6.5.1. Example directory 

 

6.5.2. Codes of each layer 

        The core function of the builder mode is also the advantage, which is to extract and realize the creation of complex objects ( especially objects composed of several objects ), which provides convenience for users to use such complex objects. At the same time, it also A bridge is built between the partial creation and overall assembly of complex objects, and the decoupling of partial and overall assembly is realized.

        The builder pattern is mainly used on complex objects, and it is useless for simple objects. But here we still need some simple objects to help understand the builder pattern.

Animal: Set abstract properties

package com.test5;

public class Animal {
    //名字
    private String name;
    //时速
    private Long speed;

    public String getName() {
        return name;
    }

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

    public Long getSpeed() {
        return speed;
    }

    public void setSpeed(Long speed) {
        this.speed = speed;
    }
}

AnimalBuilder : 

package com.test5;

public interface AnimalBuilder {
    void buildName();
    void buildSpeed();

    Animal builderAnimal();
}

 AnimalDirector :

package com.test5;

public class AnimalDirector {
    public Animal constructAnimal(AnimalBuilder pb) {
        pb.buildName();
        pb.buildSpeed();
        return pb.builderAnimal();
    }
}

 Eagle: create character 1

package com.test5;

public class Eagle extends Animal{
}

EagleBuilder: build character 1 

package com.test5;

public class EagleBuilder implements AnimalBuilder{

    Animal animal;

    public EagleBuilder(){
        animal = new Animal();
    }

   public void buildName(){
        animal.setName("雄鹰");
   }

   public void buildSpeed(){
        animal.setSpeed(80L);
   }

    public Animal builderAnimal(){
        return animal;
   }
}

Rabbit: create role 2 

package com.test5;

public class Rabbit extends Animal{
}

RabbitBuilder: build character 2 

package com.test5;

public class RabbitBuilder implements AnimalBuilder{

    Animal animal;

    public RabbitBuilder(){
        animal = new Animal();
    }

   public void buildName(){
        animal.setName("兔子");
   }

   public void buildSpeed(){
        animal.setSpeed(70L);
   }

    public Animal builderAnimal(){
        return animal;
   }
}

        Every time you add a new character, you only need to add a new character, and then add a build to it, so that it can be used later. 

test: 

package com.test5;

public class test {

    public static void main(String[] args) {

        AnimalDirector director = new AnimalDirector();

        Animal eagle = director.constructAnimal(new EagleBuilder());
        System.out.println(eagle.getName()+"的时速为:"+eagle.getSpeed());

        Animal rabbit = director.constructAnimal(new RabbitBuilder());
        System.out.println(rabbit.getName()+"的时速为:"+rabbit.getSpeed());

    }
}

Example result: 

雄鹰的时速为:80
兔子的时速为:70

Guess you like

Origin blog.csdn.net/weixin_52255395/article/details/131553891
Recommended