设计模式之 ==> 建造者设计模式
一、什么是建造者(Builder)设计模式
将一个复杂对象的构建过程和它的表示分离,使得同样的构建过程可以创建不同的表示。
建造者模式可以将一个产品的内部表象和产品的生成过程分割开来,从而使一个建造过程生成具有不同的内部表象的产品对象。
如果我们使用了建造者模式,那么用户只需要指定需要建造的类型就可以得到它们,而具体的建造过程和细节就不需要知道了。
二、建造者模式的使用场景
它主要是用于创建一些复杂的对象,这些对象的内部构建的建造顺序通常是稳定的,但对象内部的构建通常面临着复杂的变化。
怎么来理解这句话呢?举个例子:我们要创建一组不同类型产品,这些产品都有很多属性,创建过程又有很多道工序,但创建的流程(工序)大体上是一致的。如果没有使用建造者模式的时候,我们得紧盯创建的每一个步骤,生怕该步骤不对或者缺少了某一个步骤,并且每一个产品对象都得一步一步盯着,这样效率低下且容易出错。使用了建造者模式后,会有一个 Builder类(或接口)指定创建产品各个步骤(各个部分)的一个抽象类或接口,一个 Director类,实现 Builder类(或接口),它是一个指挥者,根据需求来创建产品。另外,还有一个甚至多个 ConcreteBuilder类,它们是具体的创建者,用于创建不同类型的产品,也需要实现 Builder类(或接口)。我们来看一下下面这张图就清晰了:
三、建造者模式写法举例
首先,先来一个 Product 类,包括产品的各种属性的描述,包括:产品名、长、宽、高、厚度、颜色等等。
public class Product { private String name; private Long length; private Long wide; private Long high; private Long thickness; private String color; public String getName() { return name; } public Product setName(String name) { this.name = name; return this; } public Long getLength() { return length; } public Product setLength(Long length) { this.length = length; return this; } public Long getWide() { return wide; } public Product setWide(Long wide) { this.wide = wide; return this; } public Long getHigh() { return high; } public Product setHigh(Long high) { this.high = high; return this; } public Long getThickness() { return thickness; } public Product setThickness(Long thickness) { this.thickness = thickness; return this; } public String getColor() { return color; } public Product setColor(String color) { this.color = color; return this; } @Override public String toString() { return "Product{" + "name='" + name + '\'' + ", length=" + length + ", wide=" + wide + ", high=" + high + ", thickness=" + thickness + ", color='" + color + '\'' + '}'; } }
再来一个 Builder 接口,描述如何制造 Product 对象的各种属性的方法。
public interface ProductBuilder { String buildProductName(); Long buildLength(); Long buildWide(); Long buildHigh(); Long buildThickness(); String buildColor(); }
然后,再来两个不同的产品类型,分别设定产品的产品名、长、宽、高、厚度、颜色等属性的值。
public class ProductA implements ProductBuilder { @Override public String buildProductName() { System.out.println("产品A"); return "产品A"; } @Override public Long buildLength() { System.out.println("长度100"); return 100L; } @Override public Long buildWide() { System.out.println("宽度50"); return 50L; } @Override public Long buildHigh() { System.out.println("高度20"); return 20L; } @Override public Long buildThickness() { System.out.println("厚度5"); return 5L; } @Override public String buildColor() { System.out.println("红色"); return "红色"; } }
public class ProductB implements ProductBuilder { @Override public String buildProductName() { System.out.println("产品B"); return "产品B"; } @Override public Long buildLength() { System.out.println("长度500"); return 500L; } @Override public Long buildWide() { System.out.println("宽度200"); return 200L; } @Override public Long buildHigh() { System.out.println("高度20"); return 20L; } @Override public Long buildThickness() { System.out.println("厚度5"); return 5L; } @Override public String buildColor() { System.out.println("黄色"); return "黄色"; } }
最后,就是指挥者 Director 类,负责建造产品,先获取到产品的产品名、长、宽、高、厚度、颜色等属性的值,然后把这些属性塞入 Product 对象对应的属性当中。
public class ProductDirector { private ProductBuilder builder; public ProductDirector(ProductBuilder builder) { this.builder = builder; } public Product createProduct() { return new Product() .setName(builder.buildProductName()) .setLength(builder.buildLength()) .setWide(builder.buildWide()) .setHigh(builder.buildHigh()) .setThickness(builder.buildThickness()) .setColor(builder.buildColor()); } }
我们来看下客户端调用和返回结果
public class App { @Test public void test01(){ ProductDirector productA = new ProductDirector(new ProductA()); Product product1 = productA.createProduct(); System.out.println(product1); System.out.println("-----------------------------------------"); ProductDirector productB = new ProductDirector(new ProductB()); Product product2 = productB.createProduct(); System.out.println(product2); } }
返回结果:
从结果可以看到:我们成功的制造了两个属性不一样的产品,但是如果我们还需要建造第三类产品 ProductC,则还需要添加一个 ProductC类来实现 ProductBuilder 接口,如果还需要添加呢,有成百上千个呢,显然这样还是太麻烦。我们考虑能不能让客户端自己来设定想要建造什么样的 Product 对象,所有属性都由客户端自己来设定,这样就可以大大的简化我们的代码。
四、简化实现
public class User { private String name; private Integer age; private String info; private String password; private String balance; private User(Builder builder) { this.name = builder.name; this.age = builder.age; this.info = builder.info; this.password = builder.password; this.balance = builder.balance; } public static class Builder { private String name; private Integer age; private String info; private String password; private String balance; public static Builder of() { return new Builder(); } public Builder name(String name) { this.name = name; return this; } public Builder age(Integer age) { this.age = age; return this; } public Builder info(String info) { this.info = info; return this; } public Builder password(String password) { this.password = password; return this; } public Builder balance(String balance) { this.balance = balance; return this; } public User build() { return new User(this); } } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + ", info='" + info + '\'' + ", password='" + password + '\'' + ", balance='" + balance + '\'' + '}'; } }
这次我们需要建造的是 User 对象,它有name、age、info、password、balance 五个属性。在它的内部我们加一个静态的内部类 Builder,这个静态内部类有和 User 类一样的五个属性,这个静态内部类就负责获取这五个属性的值,然后传给 User 对象对应的属性,从而来生成User对象。那么,这五个属性的值从哪里来呢?可以从客户端传入,客户端想给每个属性什么值都可以(只要类型正确),这样对于客户端来说,我想制造什么样的 User 对象都由客户端自己决定,不用预先写好对应的类了。我们来看一下客户端调用:
public class App { @Test public void test01() { User user = User.Builder.of() .name("大牛") .age(25) .info("大牛 info") .password("123456") .balance("大牛 balance") .build(); System.out.println(user); } }
这样,我们客户端调用起来就非常方便且灵活,也大大节省了代码量。