一、建造者模式的本质
建造者模式的本质:一是封装/隐藏具体类实例对象的创建过程,这一点与工厂模式一样,因为毕竟建造者模式也是属于创建型模式;二是支持复杂对象的创建过程,同时对对象的创建过程进行灵活的定制化。
二、建造者模式目的
将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示,即使用同样的构建过程创建出不同的对象,这样对于客户端/使用者来说就不需要了解复杂的构建过程,只需要关注最终构建出来的对象是否满足要求即可。
三、示例场景
某个理财网站可提供理财产品售卖服务,用户下单后,系统后台需要生成购买人信息、理财产品合同、售卖方信息、理财经纪人信息等,按照监管部门要求,每家公司都需要先确定用户资质(即生成购买人信息,以确认资质),然后确定理财经纪人资质,然后依次生成合同和售卖方信息(这里假设为银行)。
注意事项:1)监管部门可能对监管要求有调整;2)每家公司可能会频繁发布新产品。
四、代码实现及对比
产品类定义,定义了产品属性。
public class Product {
//具体产品类
//产品中包含buyer和contract两个属性
String buyer;
String contract;
public String getBuyer() {
return buyer;
}
public void setBuyer(String buyer) {
this.buyer = buyer;
}
public String getContract() {
return contract;
}
public void setContract(String contract) {
this.contract = contract;
}
@Override
public String toString() {
return "Product{" +
"buyer=" + buyer +
", contract=" + contract +
'}';
}
}
抽象建造者类定义,定义了建造者需要实现的具体建造方法和产出物(即具体的产品类实例对象)。
public interface IProductBuilder {
public void buildBuyer();
public void buildContract();
public Product getProduct();
}
具体建造者A类定义,实现了抽象建造者的建造方法。
public class ConcreteBuilderA implements IProductBuilder {
Product product = new Product();
@Override
public void buildBuyer() {
product.setBuyer("Alice");
System.out.printf( "Alice: Buyer build complete.\n");
}
@Override
public void buildContract() {
product.setContract("This is the contract for Alice.");
System.out.printf( "Alice: Contract build complete.\n");
}
@Override
public Product getProduct() {
return product;
}
}
具体建造者B类定义,实现了抽象建造者的建造方法。
public class ConcreteBuilderB implements IProductBuilder {
Product product = new Product();
@Override
public void buildBuyer() {
product.setBuyer("Bob");
System.out.printf( "Bob: Buyer build complete.\n");
}
@Override
public void buildContract() {
product.setContract("This is the contract for Bob.");
System.out.printf( "Bob: Contract build complete.\n");
}
@Override
public Product getProduct() {
return product;
}
}
指挥者类定义,针对具体建造者对象,指定了具体建造者的建造流程,通过一个建造方法实现。
public class Director {
//Director定义了具体建造者需要执行的建造流程
//具体建造者按照Director定义的流程进行建造,并通过自己的getProduct()方法交付一个具体产品类对象实例给Director
public Product build(IProductBuilder iProductBuilder){
iProductBuilder.buildBuyer();
iProductBuilder.buildContract();
return iProductBuilder.getProduct();
}
}
客户端类定义,客户端创建一个具体建造者类实例对象和一个指挥者类对象实例,然后将具体的产品建造交给指挥者类实例对象实现。
public class Client {
public static void main(String[] args) {
//首先,客户端创建一个具体建造者类对象
IProductBuilder iProductBuilder = new ConcreteBuilderA();
//其次,客户端创建一个指挥者类对象
Director director = new Director();
//然后,将具体建造者对象作为参数交给指挥者类对象,由指挥者类指挥具体建造者类去完成产品对象的创建,如流程的定制等
Product product = director.build(iProductBuilder);
System.out.println(product);
}
}
五、建造者模式的UML类图
(1)UML类图
建造者模式中的角色如下:
1)抽象建造者,声明在所有子类生成器中通用的产品构造步骤,如上图中的IBuilder;
2)具体建造者:供构造过程的不同实现,也可以构造不遵循通用接口的产品,如上图中的ConcreteBuilderA和ConcreteBuilderB;
3)产品:最终生成的产品对象,如上图中的Product;
4)指挥者:定义调用构造步骤的顺序,这样就可以创建和复用特定的产品配置,如上图中的Director。
(2)实现要点
1)抽象建造者-IBuilder,创建一个Product,提供Product响应元素的建造方法,需要提供Product对外提供的方法;
2)具体建造者-ConcreteBuilderA和ConcreteBuilderB:根据不同的构建者实现抽象建造者定义的方法;
3)产品-Product:需要定义最终产品的属性,也就定义了建造者可以建造的具体元素;
4)指挥者-Director:提供Product建造流程,并根据客户端制定的具体建造者来指挥具体建造者按照流程进行Product的建造。
此外,客户端需要创建具体建造者和指挥者,并通过指挥者的建造方法参数将具体建造者指定给指挥者。
六、建造者模式的优缺点
(1)优点
- 封装性好,使用建造者模式可以使客户端不必知道产品内部组成的细节。
- 建造者类之间相互独立,容易扩展,对系统的扩展非常有利。
- 便于控制细节风险 由于具体的建造者类是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何影响。
(2)缺点
- 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,例如很多组成部分都不相同,不适合使用建造者模式,因此其使用范围受到一定的限制。
- 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,增加系统的理解难度和运行成本。
适用场景
- 需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性。
- 需要生成的产品对象的属性相互依赖,需要指定其生成顺序。
- 对象的创建过程独立于创建该对象的类。在建造者模式中通过引入了指挥者类,将创建过程封装在指挥者类中,而不在建造者类和客户类中。
- 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。