设计模式-建造者模式(Builder)
设计模式-建造者模式(Builder)
概要
记忆关键词:类和构造分离
定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
分析:
1)当一个类的构造函数参数个数超过4个,而且这些参数有些是可选的参数,考虑使用构造者模式。
2)构建者模式的精髓是将复杂对象的构建过程封装到构建者里,使用构建者再去创建对象,即先有构建者后有产品,产品由构建者创建
总体来说,建造者模式适用于需要创建具有复杂结构或多个配置选项的对象,而且希望将构建过程与表示分离的情况。
建造者模式UML结构图如下:
一、能解决什么问题?
当一个对象的构造包含多个参数,尤其是这些参数有多种组合或依赖关系时,直接使用构造函数来创建对象会使代码变得难以维护和阅读。
参数过多的构造函数会导致“构造函数爆炸”,即需要定义多个构造函数来处理不同的参数组合。
二、涉及的角色
1. Product(目标类)
最终要生成的对象,代码示例如下:
1 public class Product { 2 ArrayList<String> parts = new ArrayList<>(); 3 4 public void add(String part) { 5 parts.add(part); 6 } 7 8 public void show() { 9 System.out.println(parts); 10 } 11 }
2. Builder(建造者的抽象基类)
承载了复杂对象的构建过程,代码示例如下:
1 public abstract class Builder { 2 3 /** 4 * 构建部分A 5 */ 6 public abstract void buildPartA(); 7 8 /** 9 * 构建部分B 10 */ 11 public abstract void buildPartB(); 12 13 /** 14 * 获取展示结果 15 * 16 * @return 执行结果 17 */ 18 public abstract Product getResult(); 19 }
3. ConcreteBuilder(具体的建造者)
依赖Product类,ConcreteBuilder跟Product属于组合关系,ConcreteBuilder属于整体类,Product属于部分类
代码示例如下:
1 public class ConcreteBuilder extends Builder { 2 3 private final Product product = new Product(); 4 5 @Override 6 public void buildPartA() { 7 product.add("构建产品的上半部分"); 8 } 9 10 @Override 11 public void buildPartB() { 12 product.add("构建产品的下半部分"); 13 } 14 15 @Override 16 public Product getResult() { 17 return product; 18 } 19 }
4. Director
决定如何构建最终产品的算法。控制Builder的生产过程,Director跟Builder属于组合关系,Director属于整体类,Builder属于部分类。
其会包含一个负责组装的方法void Construct(Builder builder), 在这个方法中通过调用builder的方法,就可以设置builder,等设置完成后,就可以通过builder的 getProduct() 方法获得最终的产品
代码示例如下:
1 public class Director { 2 private final Builder builder; 3 4 public Director(Builder builder) { 5 this.builder = builder; 6 } 7 8 public void construct() { 9 builder.buildPartA(); 10 builder.buildPartB(); 11 } 12 }
客户端调用:
1 public class Client { 2 public static void main(String[] args) { 3 4 ConcreteBuilder concreteBuilder = new ConcreteBuilder(); 5 6 //控制Builder的生产过程 7 Director director = new Director(concreteBuilder); 8 director.construct(); 9 10 Product product = concreteBuilder.getResult(); 11 product.show(); 12 } 13 } 14 15 16 //运行结果 17 [构建产品的上半部分, 构建产品的下半部分]
再来一个简化建造者模式的使用案例:
Computer类:
1 public class Computer { 2 3 /** 4 * 必须字段 5 */ 6 private final String cpu; 7 8 /** 9 * 必须字段 10 */ 11 private final String ram; 12 13 /** 14 * 可选字段 15 */ 16 private final int usbCount; 17 18 /** 19 * 可选字段 20 */ 21 private final String keyboard; 22 23 /** 24 * 可选字段 25 */ 26 private final String display; 27 28 public Computer(Builder builder) { 29 this.cpu = builder.cpu; 30 this.ram = builder.ram; 31 this.usbCount = builder.usbCount; 32 this.keyboard = builder.keyboard; 33 this.display = builder.display; 34 } 35 36 @Override 37 public String toString() { 38 return "Computer{" + 39 "cpu='" + cpu + '\'' + 40 ", ram='" + ram + '\'' + 41 ", usbCount=" + usbCount + 42 ", keyboard='" + keyboard + '\'' + 43 ", display='" + display + '\'' + 44 '}'; 45 } 46 47 public static class Builder { 48 /** 49 * 必须字段 50 */ 51 private final String cpu; 52 53 /** 54 * 必须字段 55 */ 56 private final String ram; 57 58 /** 59 * 可选字段 60 */ 61 private int usbCount; 62 63 /** 64 * 可选字段 65 */ 66 private String keyboard; 67 68 /** 69 * 可选字段 70 */ 71 private String display; 72 73 public Builder(String cpu, String ram) { 74 this.cpu = cpu; 75 this.ram = ram; 76 } 77 78 public Builder setUsbCount(int usbCount) { 79 this.usbCount = usbCount; 80 return this; 81 } 82 83 public Builder setKeyboard(String keyboard) { 84 this.keyboard = keyboard; 85 return this; 86 } 87 88 public Builder setDisplay(String display) { 89 this.display = display; 90 return this; 91 } 92 93 public Computer build() { 94 return new Computer(this); 95 } 96 } 97 }
客户端类Client:
1 public class Client { 2 public static void main(String[] args) { 3 Computer computer = new Computer.Builder("因特尔", "三星") 4 .setUsbCount(2) 5 .setKeyboard("罗技") 6 .setDisplay("LG28寸") 7 .build(); 8 System.out.println(computer.toString()); 9 } 10 } 11 12 //运行结果 13 Computer{cpu='因特尔', ram='三星', usbCount=2, keyboard='罗技', display='LG28寸'}
四、使用场景
在Java源码里,建造者模式最典型的体现就是StringBuilder
StringBuilder是Java中的一个用于构建字符串的类,它允许在不创建新的对象的情况下,对字符串进行可变操作。它通过提供一系列方法来逐步构建字符串,并通过链式调用使代码简洁易读。
代码示例如下:
1 public class StringBuilderExample { 2 public static void main(String[] args) { 3 StringBuilder builder = new StringBuilder(); 4 5 builder.append("Hello, ") 6 .append("world") 7 .append("!") 8 .insert(7, "Java ") 9 .replace(13, 18, "World") 10 .deleteCharAt(6); 11 12 String result = builder.toString(); 13 System.out.println(result); // 输出: "Hello Java World!" 14 } 15 }
注意:lombok中的@Builder 注解通过注入一个静态内部类作为建造者,简化了建造者模式的使用,减少了样板代码。这种方式在代码中使用起来更加简洁,并且提高了可读性。
举个例子:
给User类加上@Data和@Builder注解
1 @Data 2 @Builder 3 public class User { 4 private String name; 5 private int age; 6 }
通过IDEA的Delombok功能,右键点击并选择 Refactor -> Delombok, 选择 @Builder 查看代码:
1 @Data 2 public class User { 3 private String name; 4 private int age; 5 6 User(String name, int age) { 7 this.name = name; 8 this.age = age; 9 } 10 11 public static UserBuilder builder() { 12 return new UserBuilder(); 13 } 14 15 public static class UserBuilder { 16 private String name; 17 private int age; 18 19 UserBuilder() { 20 } 21 22 public UserBuilder name(String name) { 23 this.name = name; 24 return this; 25 } 26 27 public UserBuilder age(int age) { 28 this.age = age; 29 return this; 30 } 31 32 public User build() { 33 return new User(name, age); 34 } 35 36 public String toString() { 37 return "User.UserBuilder(name=" + this.name + ", age=" + this.age + ")"; 38 } 39 } 40 }
客户端类:
1 public class Client { 2 public static void main(String[] args) { 3 User user = User.builder() 4 .name("tom") 5 .age(18) 6 .build(); 7 System.out.println(user); 8 } 9 } 10 11 // 运行结果 12 User(name=tom, age=18)
五、建造者模式与简单工厂模式的区别
两者的区别在于,建造者模式多出一个Builder类,使得创建对象的灵活性大大增加,适用于如下场景:
1. 创建一个对象,多个同样的方法的调用顺序不同,产生的结果不同
2. 创建一个对象,特别复杂,参数多,而且很多参数都有默认值