建造者模式:把类对象的构造与装配分别实现。
单例、工厂、原型这几种模式的重点在于创建出一个个的实例对象来,而这个建造者模式的重点在于对某个对象的组成部分的装配。
在实际开发中,一个类不可能只有简单的几个属性,往往是有大量的属性。创建对象的同时或者之后,给属性也设置好对应的值,这个对象才有使用的价值。否则一个空对象我们用它做什么呢。
如果一个表有20个字段,那么它对应的实体类就有20个属性,如何给这个类的对象的属性赋值呢。要么是利用构造方法在创建对象的同时就赋值,要么就是创建一个空对象,然后用set方法给赋值。
构造器的方法问题就是不够灵活,而set方法就是代码量大。
建造者模式可以完美的解决这个问题。
先看一下建造者模式的一般方式:
这里有三个基本角色:产品(有比较多、复杂的组件),构造者,装配者
这里以电脑这种产品为例:
public class Computer {
private String cpu;
private String memery;
private String screen;
@Override
public String toString() {
return "Computer{" +
"cpu='" + cpu + '\'' +
", memery='" + memery + '\'' +
", screen='" + screen + '\'' +
'}';
}
// 省略get与set方法
}
构造者,用来模拟构造产品的各个组成部分
// 抽象构造者
public abstract class Builder {
Computer computer = new Computer();
// 构建cpu
protected abstract void buildCpu();
// 构建memery
protected abstract void buildMemery();
// 构建screen
protected abstract void buildScreen();
}
// 具体的建造者
public class HuaweiComputer extends Builder{
@Override
protected void buildCpu() {
computer.setCpu("only amd ...");
}
@Override
protected void buildMemery() {
computer.setMemery("sangsam 1024G");
}
@Override
protected void buildScreen() {
computer.setScreen("huawei 27 big screen");
}
}
public class AppleComputer extends Builder{
@Override
protected void buildCpu() {
computer.setCpu("inter i7");
}
@Override
protected void buildMemery() {
computer.setMemery("kinston 256G");
}
@Override
protected void buildScreen() {
computer.setScreen("apple cloure");
}
}
装配者,不同的产品只是组件不同,但是装配流程是相同的
public class Director {
private Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
public Computer createComputer(){
builder.buildCpu();
builder.buildMemery();
builder.buildScreen();
return builder.computer;
}
}
测试
public class ClientDemo {
public static void main(String[] args) {
// 通过给装配者传递不同的产品构造者来得到不同的产品
// Director director = new Director(new AppleComputer());
Director director = new Director(new HuaweiComputer());
Computer computer = director.createComputer();
System.out.println(computer);
}
}
当然从这里我们可以看到这种模式的适用场景,那就是产品应当是同一类产品,产品的组成部分可能很多,各个组成部分可能不相同,但是他们组成这个产品的整理逻辑(装配流程)是相同的。
其实本质上还是封装,我们把不同的部分(组成部分)单独实现,相同的部分(装配)统一实现。
也可以把构造者和装配者这两个角色放在一起来实现,如下:
public abstract class BuilderTotal {
Computer computer = new Computer();
// 构造者:构造产品的各个组成部分
protected abstract void buildCpu();
protected abstract void buildMemry();
protected abstract void buildScreen();
// 装配者:完成组装
public Computer createCoumputer(){
this.buildCpu();
this.buildMemry();
this.buildScreen();
return this.computer;
}
}
不同的产品
public class XiaomiComputer extends BuilderTotal{
@Override
protected void buildCpu() {
computer.setCpu("xiaomi cpu");
}
@Override
protected void buildMemry() {
computer.setMemery("xiaomi memery");
}
@Override
protected void buildScreen() {
computer.setScreen("xiaomi screen");
}
}
public class HpComputer extends BuilderTotal{
@Override
protected void buildCpu() {
computer.setCpu("hp special cpu");
}
@Override
protected void buildMemry() {
computer.setMemery("hp special memery");
}
@Override
protected void buildScreen() {
computer.setScreen("hp special screen");
}
}
测试:
public class ClientDemo_02 {
public static void main(String[] args) {
BuilderTotal hp = new HpComputer();
Computer coumputer = hp.createCoumputer();
System.out.println(coumputer);
System.out.println("========================");
BuilderTotal xiaomi = new XiaomiComputer();
Computer computer = xiaomi.createCoumputer();
System.out.println(computer);
}
}
虽然这样是把不同的功能放在同一个类中实现,不符合单一原则,但其实我觉得这样的代码可读性要高一点,不过当属性比较多的时候,也会比较乱。
接下来看一种比较巧妙的方式,利用内部类的方式,对产品的各个部分(属性)进行组装,而且非常灵活。
public class Phone {
private String brand;
private String memory;
private String screen;
private String camera;
private String clour;
// 私有构造方法,创建对象由内部类的方法实现(最后用到)
private Phone(String brand, String memory, String screen, String camera, String clour) {
this.brand = brand;
this.memory = memory;
this.screen = screen;
this.camera = camera;
this.clour = clour;
}
// 借助一个内部类,用来构造属性的各个部分,并创建调用它的构造方法创建对象返回给调用者
public static PhoneBuilder phoneBuilder(){
return new PhoneBuilder();
}
// 静态内部类实现
public static class PhoneBuilder{
// 拥有和外部类相同的参数
private String brand;
private String memory;
private String screen;
private String camera;
private String clour;
// 对每一个属性提供一个方法来设置它的值,这其实就是构造组件的部分
public PhoneBuilder brand(String brand){
this.brand = brand;
// 返回自身,这样方便使用链式编程
return this;
}
public PhoneBuilder memory(String memory){
this.memory = memory;
return this;
}
public PhoneBuilder screen(String screen){
this.screen = screen;
return this;
}
public PhoneBuilder camera(String camera){
this.camera = camera;
return this;
}
public PhoneBuilder clour(String clour){
this.clour = clour;
return this;
}
// 最后提供一个方法,创建产品对象,参数值就是上面的方法接受到的
public Phone build(){
return new Phone(brand, memory, screen, camera, clour);
}
}
// 只为了测试方便,忽略
@Override
public String toString() {
return "Phone{" +
"brand='" + brand + '\'' +
", memory='" + memory + '\'' +
", screen='" + screen + '\'' +
", camera='" + camera + '\'' +
", clour='" + clour + '\'' +
'}';
}
}
只看这个类的设计会觉得很复杂,但是使用的时候就觉得爽了:
public class PhoneClient {
public static void main(String[] args) {
Phone phone = Phone.phoneBuilder() // 这一步是得到它内部的一个构造器
.brand("huawei")
.clour("black")
// .memory("64G")
// .camera("1000 wan")
.screen("18 ch")
.build(); // 这一步才是真正创建一个Phone对象,并使用上面接受的参数
System.out.println(phone);
}
}
可以看到,在得到构造器和利用构造器创建对象中间,都是设置对象的属性值,最爽的地方就在于:随意。想设置几个就设置几个,而且顺序随意,而且代码可读性很好,很明确要给给哪些属性设置什么值。
其实这完全就是lomboc中的注解@Builder的功能。之前只是照着别人用觉得方便,也没有琢磨琢磨。最近学到了这个,觉得这个设计真是巧妙。平时工作中只是简单的crud,动脑筋比较少,多学学这些优秀的东西,真的可以开阔思路。