微信公众号:潇雷
当努力到一定程度,幸运自会与你不期而遇。
一、初识建造者模式
1.1 概述
今天,我们来学习创建者模式的最后一种:建造者模式(builder)。
先上百度百科:
建造者模式是设计模式的一种,将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
怎么理解呢?
这个定义有三个关键点:
- 复杂对象
- 构建和表示分离
- 同样的构建可以创建不同的表示
首先,Builder模式适合复杂对象,这个复杂对象的特点是什么呢?即这个对象的构建由多个部分共同构建而成,并且这个对象的构建比较困难的时候。因此,构建和表示分离开的含义就是,不同的构建,相同的装配,这也就是构建和装配的解耦。同样的构建可以创建不同的表示,即客户端执行同样的步骤,也可以得到不一样的表示结果。例如,我们在某团上买菜,我们挑好后,只需执行下单这个动作,对于顾客来说,这个步骤是固定的,而外卖员则会根据顾客的要求,一步步的装配这些组成部分。
1.2 适用场景
- 当一个类的构造函数参数个数超过4个,而且这些参数有些是可选的参数,考虑使用建造者模式。
- 创建的对象较复杂,由多个部件构成,各部件面临着复杂的变化,但构件间的建造顺序是稳定的。
- Builder模式不适合创建差异性很大的产品类
- 产品的构建和最终的表示是独立的。
二、建造者模式的实现
2.1 结构
Builder模式包含如下角色:
- 抽象建造者类(Builder):这个接口规定要实现复杂对象的那些部分的创建,并不涉及具体的部件对象的创建。
- 具体建造者类(ConcreteBuilder):实现Builder接口,完成复杂产品的各个部件的具体创建方法。在构造过程完成后,提供产品的实例。
- 产品类(product):要创建的复杂对象
- 指挥者类(Director):调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按照某种顺序的创建。
2.2 例子
既然是建造者模式,就拿建楼来举例子。可以分为别墅(villa)和瓦房(RoofedHouse)。
建造一栋楼是一个复杂的过程,假设需要钢筋(rebar)、水泥(cement)、砖头(brick)三种材料。
2.2.1 定义产品类
(1)定义house实体类
public class House {
//钢筋
private String rebar;
//水泥
private String cement;
//砖头
private String brick;
public String getRebar() {
return rebar;
}
public void setRebar(String rebar) {
this.rebar = rebar;
}
public String getCement() {
return cement;
}
public void setCement(String cement) {
this.cement = cement;
}
public String getBrick() {
return brick;
}
public void setBrick(String brick) {
this.brick = brick;
}
@Override
public String toString() {
return "House{" +
"rebar='" + rebar + '\'' +
", cement='" + cement + '\'' +
", brick='" + brick + '\'' +
'}';
}
}
2.2.2 定义抽象建造者类
public abstract class HouseModel {
protected House house = new House();
public abstract void buildRebar();
public abstract void buildCement();
public abstract void buildBrick();
public abstract House createHouse();
}
2.2.3 定义具体的建造者类
public class VillaHouse extends HouseModel{
@Override
public void buildRebar() {
house.setRebar("别墅钢筋");
}
@Override
public void buildCement() {
house.setCement("高级水泥");
}
@Override
public void buildBrick() {
house.setBrick("国家级红砖");
}
@Override
public House createHouse() {
return house;
}
}
public class RoofedHouse extends HouseModel{
@Override
public void buildRebar() {
house.setRebar("生锈了的钢筋");
}
@Override
public void buildCement() {
house.setCement("没粘性的水泥");
}
@Override
public void buildBrick() {
house.setBrick("土砖头");
}
@Override
public House createHouse() {
return house;
}
}
2.2.4 定义指挥者类
public class Director {
private HouseModel model;
public Director(HouseModel houseModel){
this.model=houseModel;
}
public House construct(){
model.buildBrick();
model.buildRebar();
model.buildCement();
return model.createHouse();
}
}
2.2.5 测试
public class Client {
public static void main(String[] args) {
showHouse(new VillaHouse());
showHouse(new RoofedHouse());
}
private static void showHouse(HouseModel houseModel){
Director director = new Director(houseModel);
House house = director.construct();
System.out.println(house.toString());
}
}
打印结果:
House{rebar='别墅钢筋', cement='高级水泥', brick='国家级红砖'}
House{rebar='生锈了的钢筋', cement='没粘性的水泥', brick='土砖头'}
Director 指挥者类在建造者模式中具有很重要的作用,它用于指导具体的构建者如何构建产品,并可以控制调用次序,并向调用者返回一个完整的产品类。
2.3 优缺点
2.3.1 优点
- 建造者模式的封装性很好。使用建造者模式可以有效的封装变化,对整体而言可以取得比较好的稳定性。
- 在建造者模式中,客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
- 可以更加精细地控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程
- 建造者模式很容易进行扩展。如果有新的需求,通过实现一个新的建造者类就可以完成,符合开闭原则。
2.3.2 缺点
- 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式。
三、建造者模式的其他应用
3.1 多参数代码重构
建造者模式还可以对多参数的代码进行重构。可以自由组合参数,建造者模式的设计一般来说还是比较复杂和灵活的。
public class Phone {
private String cpu;
private String screen;
private String memory;
private String mainboard;
public Phone(String cpu, String screen, String memory, String mainboard) {
this.cpu = cpu;
this.screen = screen;
this.memory = memory;
this.mainboard = mainboard;
}
public String getCpu() {
return cpu;
}
public void setCpu(String cpu) {
this.cpu = cpu;
}
public String getScreen() {
return screen;
}
public void setScreen(String screen) {
this.screen = screen;
}
public String getMemory() {
return memory;
}
public void setMemory(String memory) {
this.memory = memory;
}
public String getMainboard() {
return mainboard;
}
public void setMainboard(String mainboard) {
this.mainboard = mainboard;
}
@Override
public String toString() {
return "Phone{" +
"cpu='" + cpu + '\'' +
", screen='" + screen + '\'' +
", memory='" + memory + '\'' +
", mainboard='" + mainboard + '\'' +
'}';
}
}
public class Client {
public static void main(String[] args) {
//构建Phone对象
Phone phone = new Phone("intel","三星屏幕","金士顿","华硕");
System.out.println(phone);
}
}
public class Phone {
private String cpu;
private String screen;
private String memory;
private String mainboard;
private Phone(Builder builder) {
cpu = builder.cpu;
screen = builder.screen;
memory = builder.memory;
mainboard = builder.mainboard;
}
public static final class Builder {
private String cpu;
private String screen;
private String memory;
private String mainboard;
public Builder() {}
public Builder cpu(String val) {
cpu = val;
return this;
}
public Builder screen(String val) {
screen = val;
return this;
}
public Builder memory(String val) {
memory = val;
return this;
}
public Builder mainboard(String val) {
mainboard = val;
return this;
}
public Phone build() {
return new Phone(this);}
}
@Override
public String toString() {
return "Phone{" +
"cpu='" + cpu + '\'' +
", screen='" + screen + '\'' +
", memory='" + memory + '\'' +
", mainboard='" + mainboard + '\'' +
'}';
}
}
public class Client {
public static void main(String[] args) {
Phone phone = new Phone.Builder()
.cpu("intel")
.mainboard("华硕")
.memory("金士顿")
.screen("三星")
.build();
System.out.println(phone);
}
}
3.2 建造者模式在JDK中运用
印象中,一个熟悉的面试题就是stringBuffer 和 StringBuilder 的区别是什么。区别就是StringBuilder是线程不安全的,而StringBuffer是线程安全的。而这个StringBuilder 采用的就是建造者模式。
Appendable 定义了抽象方法,为抽象建造者类
public interface Appendable {
Appendable append(CharSequence csq) throws IOException;
Appendable append(CharSequence csq, int start, int end) throws IOException;
Appendable append(char c) throws IOException;
}
AbstractStringBuilder 实现了Appendable接口方法所有的append()方法
abstract class AbstractStringBuilder implements Appendable, CharSequence {
}
StringBuilder 充当了指挥者角色和具体的建造者角色。返回this对象本身充当产品类。
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
}
四、小结
本篇文章讲解了建造者模式的概念和简单实现,只能做个简单的入门,真正的建造者模式在源码中的应用还是非常复杂的,只能说有机会再去深入,入门,这篇文章就够了,这也是浅谈设计模式想要达到的目的。下面对这几天学的创建型模式做个简单总结。
建造者模式与工厂模式的区别是建造者模式注重部件构建的过程,一步步地精确构建出一个复杂的对象;而工厂方法注重的整体对象的创建方式。
创建型模式分类:
- 单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。
- 原型(Prototype)模式:将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。
- 工厂方法(FactoryMethod)模式:定义一个用于创建产品的接口,由子类决定生产什么产品。
- 抽象工厂(AbstractFactory)模式:提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。
- 建造者(Builder)模式:将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。