Welcome to follow the official wechat account: FSA Full stack action 👋

One, foreword

  • Builder pattern
    • What it does: Separates the construction of a complex object from its representation, so that the same construction process can create different representations.
    • Core actions: Privatize the class constructor for complex objects (Products), design and create the Builder class.

Second, use the Builder pattern

  • Example: Building a computer
  • Key points: Multi-constructor, builder pattern

In the process of application development, complex products are always created. Taking computer assembly as an example, there are many parameters to be configured from hardware to software, that is, the constructor needs to pass in N parameters, so we may write the following code:

/** * computer **@author GitLqr
 */
class Computer(
    val mainboard: String, / / the mainboard
    val cpu: String, / / processor
    val ram: String, / / memory
    val battery: String, / / power
    valgpu: String? ./ / graphics card
    valhardDisk: String? ./ / hard disk
    valnetworkInterface: String? .// Network cable interface
    valcdDriver: String? ./ / drive
    valos: String? ./ / system
    valchassis: String? ./ / case
    valmouse: String? ./ / mouse
    valkeyboard: String? ./ / keyboard
    val monitor: String? / / display
)

/ / use
val nbComputer = Computer(
    "Amd X8QB6 - F"."Intel Xeon E7-8870"."CORSAIR Avengers LPX DDR4 2133 64GB 7000"."Siemens Luxury Power Supply Cabinet"."Leadtek/ Lytai Quadro Plex 7000"."WD_BLACK SN850 2T".Intel EXPX9501AFXLR."ThinkPad 43N3215 Blu-ray Burner Ultrabay"."Windows 11"."The Man of the Game, Steve Lovski."."RAPOO 3710 2.4g Laser Wireless Mouse".Optimus Maximus Concept Keyboard.Sharp LB-1085 108-inch FULL HD LCD
)
Copy the code

But not all arguments are required. In Java, we typically reduce the number of arguments passed in when an object is created by overloading the constructor. In Kotlin, overloading the constructor defines the secondary constructor:

/** * Computer class (overloaded constructor) **@author GitLqr
 */
class Computer(
    val mainboard: String, / / the mainboard
    val cpu: String, / / processor
    val ram: String, / / memory
    val battery: String, / / power
    valgpu: String? ./ / graphics card
    valhardDisk: String? ./ / hard disk
    valnetworkInterface: String? .// Network cable interface
    valcdDriver: String? ./ / drive
    valos: String? ./ / system
    valchassis: String? ./ / case
    valmouse: String? ./ / mouse
    valkeyboard: String? ./ / keyboard
    val monitor: String? / / display
) {
    // The secondary constructor needs to call the primary constructor to pass parameters
    constructor(mainboard: String, cpu: String, ram: String, battery: String) :
            this(mainboard, cpu, ram, battery, null.null.null.null.null.null.null.null.null)}/ / use
val simpleComputer = Computer("Amd X8QB6 - F"."Intel Xeon E7-8870"."CORSAIR Avengers LPX DDR4 2133 64GB 7000"."Siemens Luxury Power Supply Cabinet")
Copy the code

Overloading constructors is not a good idea, however. Once overloading many, the code becomes difficult to maintain. Therefore, in Java, complex object creation scenarios are handled using the Builder pattern.

  • Product: Privatized class constructor for complex objects (to ensure that consumers cannot create instances directly from the constructor)
  • Builder:
    • Will pass parametersPassed in through the constructor
    • Optional parametersPassed in through a set of setters
    • To provide abuild()Method returns a Product object

Let’s modify the above code with the builder pattern:

/** * Computer class (Builder mode) **@author GitLqr
 */
class Computer private constructor( // Privatize the class constructor for complex objects
    val mainboard: String, / / the mainboard
    val cpu: String, / / processor
    val ram: String, / / memory
    val battery: String, / / power
    valgpu: String? ./ / graphics card
    valhardDisk: String? ./ / hard disk
    val networkInterface: String? // Network cable interface
    ...
) {
    // [required argument] passed through the constructor
    class Builder(val mainboard: String, val cpu: String, val ram: String, val battery: String) {
        var gpu: String? = null
        var hardDisk: String? = null
        var networkInterface: String? = null.fun setGpu(gpu: String): Builder {
            this.gpu = gpu
            return this
        }

        fun setHardDisk(hardDisk: String): Builder {
            this.hardDisk = hardDisk
            return this
        }

        fun setNetworkInterface(networkInterface: String): Builder {
            this.networkInterface = networkInterface
            return this}...// Remember, there is also a build() method for creating complex objects and returning them
        fun build(a): Computer {
            returnComputer(mainboard, cpu, ram, battery, gpu, hardDisk, networkInterface, ...) }}}/ / use
val computer =
    Computer.Builder("Amd X8QB6 - F"."Intel Xeon E7-8870"."CORSAIR Avengers LPX DDR4 2133 64GB 7000"."Siemens Luxury Power Supply Cabinet")
        .setGpu("Leadtek/ Lytai Quadro Plex 7000")
        .setHardDisk("WD_BLACK SN850 2T")
        .setNetworkInterface(Intel EXPX9501AFXLR)
        .build()
Copy the code

To summarize the strengths and weaknesses of the Builder pattern:

  • Advantages:
    • In combination withChain callsDesign, more beautiful code
    • Optional parameter Settings are more semantic
  • Disadvantages:
    • When there are many optional parameters, the Builder code is verbose
    • You might forget it when you use Builderbuild()Method call
    • The Builder object must be created before the Product object can be created, adding additional overhead

3. Improve the Builder model

  • Example: Building a computer
  • Important: The Builder pattern essentially emulates named optional parameters

Kotlin, as better Java, has more features of modern languages, and named optional parameters is one of many features that can be used to improve (or replace) the builder pattern.

Kotlin’s named optional parameters have the following characteristics:

  • When specifying the value of a parameter, it is possible to specify its parameter name, not its position among the parameters
  • Since parameters can be set to default values, this allows us to give values for only some of the parameters, rather than all of them

Let’s modify the above code with Kotlin’s named optional argument:

/** * Computer class (Builder mode) improved: named optional parameter with default value **@author GitLqr
 */
class Computer(
    val mainboard: String, / / the mainboard
    val cpu: String, / / processor
    val ram: String, / / memory
    val battery: String, / / power
    val gpu: String? = null./ / graphics card
    val hardDisk: String? = null./ / hard disk
    val networkInterface: String? = null.// Network cable interface
    val cdDriver: String? = null./ / drive
    val os: String? = null./ / system
    val chassis: String? = null./ / case
    val mouse: String? = null./ / mouse
    val keyboard: String? = null./ / keyboard
    val monitor: String? = null / / display
)

/ / use
val computer = Computer(
    "Amd X8QB6 - F"."Intel Xeon E7-8870"."CORSAIR Avengers LPX DDR4 2133 64GB 7000"."Siemens Luxury Power Supply Cabinet",
    gpu = "Leadtek/ Lytai Quadro Plex 7000",
    os = "Windows 11",
    mouse = "RAPOO 3710 2.4g Laser Wireless Mouse",
    keyboard = Optimus Maximus Concept Keyboard,
    monitor = Sharp LB-1085 108-inch FULL HD LCD
)
Copy the code

To summarize the advantages of named optional parameters:

  • The code is simple and elegant, whether it’s the class structure of Product or creating an instance of Product
  • When creating a Product instance, specify the parameter name explicitly instead of passing it in order
  • All parameters are declared using val, which is more secure than using VAR in Builder

Fourth, constraints on parameters

Another advantage of the builder mode is that it is possible to constrain parameters in the build() method. For example, if a computer has a specified operating system (OS), but no hardDisk (hardDisk), this is obviously not correct. If you use the builder mode, you can constrain parameters in the build() method as follows:

/** * Builder mode: constrain and validate parameters in the build() method@author GitLqr
 */
class Computer private constructor(...). {class Builder(...). {...fun build(a): Computer {
            if(os ! =null && hardDisk == null) {
                throw IllegalArgumentException("Can't install an OPERATING system without a hard drive.")}returnComputer(...) }}}Copy the code

So what happens when you need to add constraints to parameters after switching to named optional parameters? Constraint determination can be done in the init block, and by the way Kotlin provides the require() method, which is equivalent to Java assert:

/** * name Optional parameter: init{} block to constrain and verify parameters **@author GitLqr
 */
class Computer(
    val mainboard: String, / / the mainboard
    val cpu: String, / / processor
    val ram: String, / / memory
    val battery: String, / / power
    val gpu: String? = null./ / graphics card
    val hardDisk: String? = null./ / hard disk
    val networkInterface: String? = null.// Network cable interface
    val cdDriver: String? = null./ / drive
    val os: String? = null./ / system
    val chassis: String? = null./ / case
    val mouse: String? = null./ / mouse
    val keyboard: String? = null./ / keyboard
    val monitor: String? = null / / display
) {
    init {

        // if (os ! = null && hardDisk == null) {
        // throw IllegalArgumentException(" CAN't install OS without hard disk ")
        // }require(! (os ! =null && hardDisk == null)) {
            "Can't install an OPERATING system without a hard drive."}}}Copy the code

So, in summary, in Kotlin, you don’t need to use the builder pattern

The Builder pattern is described in Effective Java as follows: Essentially, the Builder pattern emulates named optional parameters, just like in Ada and Python.

If this article is helpful to you, please click on my wechat official number: FSA Full Stack Action, which will be the biggest incentive for me. The public account not only has Android technology, but also iOS, Python and other articles, which may have some skills you want to know about oh ~