Design Patterns - Builder Pattern

foreword

People are floating in the IT arena, how can they pretend to not understand design patterns?

But I haven't written 50,000 lines of code. Talking about design patterns is bullshit. Let's look at the definition first.

In software engineering, a software design pattern is a general, reusable solution to a commonly occurring problem within a given context in software design. It is not a finished design that can be transformed directly into source or machine code. It is a description or template for how to solve a problem that can be used in many different situations. Design patterns are formalized best practices that the programmer can use to solve common problems when designing an application or system.

In software engineering, design patterns are a set of general Reusable solutions are used to solve common problems that arise during the software design process. It is not a design that can be translated directly into source code, but a set of best practice guidelines that programmers should follow during the software system design process.

What, if you don't have any work experience, you're going to make a mess, because you don't understand what you're talking about! You just remember that without design patterns, software will still be developed, even in the process of developing and maintaining large-scale software systems, it will be painful. Finally, after continuous refactoring, you will find that Nima has used a lot of design patterns.

The builder mode often appears in my work, so I organize and record it, hoping to help myself and others. I hope that after reading this article, you can use the Builder mode in the actual development process, and successfully pretend to be a force. I am even gratified as a scholar.

definition

Although the definition is basically useless, because most people don't understand it, but it is still said. . .


The intent of the Builder design pattern is to separate the construction of a complex object from its representation. By doing so the same construction process can create different representations. representation of

scenes to be used

This is very important, because if you learn something and you don't know what problem to use to solve it, what's the use? The importance of understanding the usage scenario is much higher than whether you will implement this pattern, because as long as you know what problems can be solved using the builder pattern, even if you don't know how to write it, you can complete it after investigating the relevant information. I don't want to confuse you with some big and correct terms. We only focus on specific problems. As for the extensional thinking, as your knowledge grows, you will gradually understand. Extended reading

When a class has more than 4 constructor parameters, and some of these parameters are optional, consider using the constructor pattern.

solved problem

When a class has more than 4 constructor parameters, and some of these parameters are optional, we usually have two ways to construct its object. For example, we now have the following computer class Computer, in which cpu and ram are required parameters, and the other three are optional parameters, so how do we construct an instance of this class, there are usually two common ways:

public class Computer {
    private String cpu;//必须
    private String ram;//必须
    private int usbCount;//可选
    private String keyboard;//可选
    private String display;//可选
}

First: telescoping constructor pattern, which we often use, as shown in the following code

public class Computer {
     ...
    public Computer(String cpu, String ram) {
        this(cpu, ram, 0);
    }
    public Computer(String cpu, String ram, int usbCount) {
        this(cpu, ram, usbCount, "罗技键盘");
    }
    public Computer(String cpu, String ram, int usbCount, String keyboard) {
        this(cpu, ram, usbCount, keyboard, "三星显示器");
    }
    public Computer(String cpu, String ram, int usbCount, String keyboard, String display) {
        this.cpu = cpu;
        this.ram = ram;
        this.usbCount = usbCount;
        this.keyboard = keyboard;
        this.display = display;
    }
}

The second: Javabean mode, as shown below

public class Computer {
        ...

    public String getCpu() {
        return cpu;
    }
    public void setCpu(String cpu) {
        this.cpu = cpu;
    }
    public String getRam() {
        return ram;
    }
    public void setRam(String ram) {
        this.ram = ram;
    }
    public int getUsbCount() {
        return usbCount;
    }
...
}

So what are the disadvantages of these two methods?

The first one is mainly inconvenient to use and read. You can imagine that when you want to call the constructor of a class, you first have to decide which one to use, and then there are a bunch of parameters in it. If the types of these parameters are many and the same, you have to figure out the parameters of these parameters. Meaning, it's easy to get confused. . . Who knows who uses that sour.

In the second way, the state of the object is prone to change during the construction process, causing errors. Because the properties in that class are set in steps, it's error-prone.

In order to solve these two pain points, the builder mode was born.

How to achieve

  1. Create a static inner class Builder in Computer, and then copy the parameters in Computer to the Builder class.
  2. Create a private constructor in Computer with parameters of type Builder
  3. Create a constructor in Builder public, the parameters are those required in Computer, cpu and ram.
  4. Create a setting function in the Builder, assign values ​​to the optional parameters in the Computer, and return an instance of the Builder type
  5. Create a build()method in the Builder where you build an instance of Computer and return

The following code is the final look

public class Computer {
    private final String cpu;//必须
    private final String ram;//必须
    private final int usbCount;//可选
    private final String keyboard;//可选
    private final String display;//可选

    private Computer(Builder builder){
        this.cpu=builder.cpu;
        this.ram=builder.ram;
        this.usbCount=builder.usbCount;
        this.keyboard=builder.keyboard;
        this.display=builder.display;
    }
    public static class Builder{
        private String cpu;//必须
        private String ram;//必须
        private int usbCount;//可选
        private String keyboard;//可选
        private String display;//可选

        public Builder(String cup,String ram){
            this.cpu=cup;
            this.ram=ram;
        }

        public Builder setUsbCount(int usbCount) {
            this.usbCount = usbCount;
            return this;
        }
        public Builder setKeyboard(String keyboard) {
            this.keyboard = keyboard;
            return this;
        }
        public Builder setDisplay(String display) {
            this.display = display;
            return this;
        }        
        public Computer build(){
            return new Computer(this);
        }
    }
  //省略getter方法
}

how to use

Use chained calls on the client to build the object step by step.

Computer computer=new Computer.Builder("因特尔","三星")
                .setDisplay("三星24寸")
                .setKeyboard("罗技")
                .setUsbCount(2)
                .build();

case

The builder pattern is a very practical and common creational design pattern. For example, the image processing framework Glide, the network request framework Retrofit, etc. use this pattern.

expand

In fact, the above content is a simplified way of using Builder in Java. The classic Builder mode is somewhat different from it. If you are not interested, you can stop reading.

Traditional Builder Mode

Builder pattern UML diagram is shown below

As shown above, the builder mode has 4 roles.

  • Product: The final object to be generated, such as a Computer instance.
  • Builder: Abstract base class for builders (interfaces are sometimes used instead). It defines the abstract steps for building a Product, and its entity classes need to implement these steps. It will contain a method to return the final product Product getProduct().
  • ConcreteBuilder: The implementation class of Builder.
  • Director: The algorithm that determines how to build the final product. It will include a method responsible for assembly void Construct(Builder builder). In this method, by calling the builder method, the builder can be set, and after the setting is completed, the  getProduct() final product can be obtained through the builder method.

We will then implement the first example in the traditional way.

Step 1: Our target Computer class:

public class Computer {
    private String cpu;//必须
    private String ram;//必须
    private int usbCount;//可选
    private String keyboard;//可选
    private String display;//可选

    public Computer(String cpu, String ram) {
        this.cpu = cpu;
        this.ram = ram;
    }
    public void setUsbCount(int usbCount) {
        this.usbCount = usbCount;
    }
    public void setKeyboard(String keyboard) {
        this.keyboard = keyboard;
    }
    public void setDisplay(String display) {
        this.display = display;
    }
    @Override
    public String toString() {
        return "Computer{" +
                "cpu='" + cpu + '\'' +
                ", ram='" + ram + '\'' +
                ", usbCount=" + usbCount +
                ", keyboard='" + keyboard + '\'' +
                ", display='" + display + '\'' +
                '}';
    }
}

Step 2: Abstract Builder Class

public abstract class ComputerBuilder {
    public abstract void setUsbCount();
    public abstract void setKeyboard();
    public abstract void setDisplay();

    public abstract Computer getComputer();
}

The third step: entity builder class, we can generate more entity builder classes according to the type of product to be built, here we need to build two brands of computers, Apple computer and Lenovo computer, so we generate two entity builders kind.

mac builder class

public class MacComputerBuilder extends ComputerBuilder {
    private Computer computer;
    public MacComputerBuilder(String cpu, String ram) {
        computer = new Computer(cpu, ram);
    }
    @Override
    public void setUsbCount() {
        computer.setUsbCount(2);
    }
    @Override
    public void setKeyboard() {
        computer.setKeyboard("苹果键盘");
    }
    @Override
    public void setDisplay() {
        computer.setDisplay("苹果显示器");
    }
    @Override
    public Computer getComputer() {
        return computer;
    }
}

Lenovo Computer Builder Class

public class LenovoComputerBuilder extends ComputerBuilder {
    private Computer computer;
    public LenovoComputerBuilder(String cpu, String ram) {
        computer=new Computer(cpu,ram);
    }
    @Override
    public void setUsbCount() {
        computer.setUsbCount(4);
    }
    @Override
    public void setKeyboard() {
        computer.setKeyboard("联想键盘");
    }
    @Override
    public void setDisplay() {
        computer.setDisplay("联想显示器");
    }
    @Override
    public Computer getComputer() {
        return computer;
    }
}

Step 4: Director Class (Director)

public class ComputerDirector {
    public void makeComputer(ComputerBuilder builder){
        builder.setUsbCount();
        builder.setDisplay();
        builder.setKeyboard();
    }
}

use

First generate a director (1), then generate a target builder (2), then use the director to assemble the builder (3), and use the builder to create a product instance (4) after assembly.

public static void main(String[] args) {
        ComputerDirector director=new ComputerDirector();//1
        ComputerBuilder builder=new MacComputerBuilder("I5处理器","三星125");//2
        director.makeComputer(builder);//3
        Computer macComputer=builder.getComputer();//4
        System.out.println("mac computer:"+macComputer.toString());

        ComputerBuilder lenovoBuilder=new LenovoComputerBuilder("I7处理器","海力士222");
        director.makeComputer(lenovoBuilder);
        Computer lenovoComputer=lenovoBuilder.getComputer();
        System.out.println("lenovo computer:"+lenovoComputer.toString());
}

The output is as follows:

mac computer:Computer{cpu='I5处理器', ram='三星125', usbCount=2, keyboard='苹果键盘', display='苹果显示器'}
lenovo computer:Computer{cpu='I7处理器', ram='海力士222', usbCount=4, keyboard='联想键盘', display='联想显示器'}

It can be seen that the initial use of the article is a variant of the traditional builder mode. First, it omits the role of director, and hands the construction algorithm to the client. Second, the builder is written into the product class to be built, and finally the chain is used. call.

Summarize

Design patterns are worth practicing deliberately!

source:

https://zhuanlan.zhihu.com/p/58093669

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324155872&siteId=291194637