概述
建造者模式:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。 [构建与表示分离,同构建不同表示]
假如一个对象的构建很复杂,需要很多步骤。则可以使用建造者模式,将其构建对象和组装成一个对象这两步给分开来。构建部分为(builder)和组织部分(director),实现了构建和装配的解耦。
不同的构建器,相同的装配也可以做出不同的对象。
相同的构建器,不同的装配顺序也可以做出不同的对象。
UML
- Dirextor: 指挥者类,用于统一组装流程
- Builder:抽象Builder类,规范产品的组建,一般是由子类实现。
- ConcreteBulider: 抽象Builder类的实现类,实现抽象Builder类定义的所有方法,并且返回一个组建好的对象
- Product: 产品类
适用场合
- 相同的方法,不同的执行顺序,产生不同的事件结果时。
- 多个部件或零件装配到一个对象中,但是产生的运行结果又不同。
- 产品类非常复杂,或者产品中的调用顺序不同产生不同的作用时,就可以考虑建造者模式。
- 当初始化一个对象特别复杂,参数多,且很多参数都具有默认值时。
例子
以生产PC为例,这里我们假设生产一台PC只需三个步骤,创建cpu、创建内存、创建显示器,将三个步骤抽象成一个Builder,且该Builder有一个创建待加工的产品的方法和返回成品的方法;
以联想电脑和惠普电脑为例,认为它们在生产电脑的过程中,以上三个步骤的实现是不一致的,对应着具体的HPBuilder和LenovoBuilder;
同时,我们把电脑产品封装成Computer类,其拥有cpu、内存、显示器三个属性;
然后,再创建一个指挥者类Director,其拥有一个建造者对象和建造PC产品的方法construct,该方法通过具体建造者对象,依次执行每个步骤,最后返回建造完成的产品对象;
类图:
代码实现:
产品角色
package com.dyleaf.create.BuilderPattern;
public class Computer {
private String cpu;
private String ram;
private String monitor;
/**
* @return the cpu
*/
public String getCpu() {
return cpu;
}
/**
* @param cpu the cpu to set
*/
public void setCpu(String cpu) {
this.cpu = cpu;
}
/**
* @return the ram
*/
public String getRam() {
return ram;
}
/**
* @param ram the ram to set
*/
public void setRam(String ram) {
this.ram = ram;
}
/**
* @return the monitor
*/
public String getMonitor() {
return monitor;
}
/**
* @param monitor the monitor to set
*/
public void setMonitor(String monitor) {
this.monitor = monitor;
}
public String toString(){
return "PC:" + this.cpu + ", " + this.ram + ", " + this.monitor;
}
}
抽象建造者
package com.dyleaf.create.BuilderPattern;
public abstract class Builder {
private Computer pc ;
public abstract void buildCpu();
public abstract void buildRam();
public abstract void buildMonitor();
public void createComputer(){
this.pc = new Computer();
}
public Computer getComputer(){
return this.pc;
}
}
两个具体建造者
package com.dyleaf.create.BuilderPattern;
public class LenovoBuilder extends Builder{
@Override
public void buildCpu() {
System.out.println("lenovo: build cpu start...");
this.getComputer().setCpu("lenovo cpu");
System.out.println("lenovo: build cpu end...");
}
@Override
public void buildRam() {
System.out.println("lenovo: build ram start...");
this.getComputer().setRam("lenovo ram");
System.out.println("lenovo: build ram end...");
}
@Override
public void buildMonitor() {
System.out.println("lenovo: build monitor start...");
this.getComputer().setMonitor("lenovo monitor");
System.out.println("lenovo: build monitor end...");
}
}
package com.dyleaf.create.BuilderPattern;
public class HPBuilder extends Builder{
@Override
public void buildCpu() {
System.out.println("hp: build cpu start...");
this.getComputer().setCpu("hp cpu");
System.out.println("hp: build cpu end...");
}
@Override
public void buildRam() {
System.out.println("hp: build ram start...");
this.getComputer().setRam("hp ram");
System.out.println("hp: build ram end...");
}
@Override
public void buildMonitor() {
System.out.println("hp: build monitor start...");
this.getComputer().setMonitor("hp monitor");
System.out.println("hp: build monitor end...");
}
}
指挥者
package com.dyleaf.create.BuilderPattern;
public class Director {
private Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
public Computer construct(){
this.builder.createComputer();
this.builder.buildCpu();
this.builder.buildRam();
this.builder.buildMonitor();
return this.builder.getComputer();
}
/**
* @return the builder
*/
public Builder getBuilder() {
return builder;
}
/**
* @param builder the builder to set
*/
public void setBuilder(Builder builder) {
this.builder = builder;
}
}
Test
package com.dyleaf.create.BuilderPattern;
public class Test {
public static void main(String[] args) {
Builder hpBuilder = new HPBuilder();
Director director = new Director(hpBuilder);
Computer hpPC = director.construct();
System.out.println(hpPC.toString());
Builder lenovoBuilder = new LenovoBuilder();
director.setBuilder(lenovoBuilder);
Computer lenovoPC = director.construct();
System.out.println(lenovoPC.toString());
}
}
运行结果如下,相同的指挥者使用不同的建造者创建了不同的产品:
优缺点
优点
- 使用Builder会导致写两遍相关属性的代码和SETTER方法(Builder中一次,Director中一次),但是代码的可读性和可用性大大提高。
- 单个builder构建多个对象时Builder参数可在创建期间进行调整,还可以根据对象不同而进行改变。
缺点
- 使用Builder模式是肯定会增加代码量的。- 一般需要嵌套到类中,容易忘记给某个属性添加builder支持。