建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于建立型模式,它提供了一种建立对象的最佳方式。html
一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其余对象的。java
意图:将一个复杂的构建与其表示相分离,使得一样的构建过程能够建立不一样的表示。算法
主要解决:主要解决在软件系统中,有时候面临着"一个复杂对象"的建立工做,其一般由各个部分的子对象用必定的算法构成;因为需求的变化,这个复杂对象的各个部分常常面临着剧烈的变化,可是将它们组合在一块儿的算法却相对稳定。编程
什么时候使用:一些基本部件不会变,而其组合常常变化的时候。设计模式
如何解决:将变与不变分离开。ide
关键代码:建造者:建立和提供实例,导演:管理建造出来的实例的依赖关系。函数
应用实例: 一、去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是常常变化的,生成出所谓的"套餐"。 二、JAVA 中的 StringBuilder。测试
优势: 一、建造者独立,易扩展。 二、便于控制细节风险。ui
缺点: 一、产品必须有共同点,范围有限制。 二、如内部变化复杂,会有不少的建造类。this
使用场景: 一、须要生成的对象具备复杂的内部结构。 二、须要生成的对象内部属性自己相互依赖。
注意事项:与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。
角色介绍:
通用类型代码以下:
(1)产品类
产品类的方法和属性都是为了添加产品自身的属性,有些产品须要有多个属性,有些产品可能只有一个属性。下面的代码只是一个例子,其余属性以此方式添加便可。
/** * 产品类 */ public class Product { protected String name; public void setName(String name) { this.name = name; } }
(2)抽象建造者类
与产品的各个属性相对应,并为之提供相应的方法。这个就像静态代理模式,经过代理类 Builder 来生成被代理对象 Product。
/** * 抽象建造者 * 若是有多个产品类就有几个具体的建造者,并且这多个产品类具备相同接口或抽象类 */ public abstract class Builder { // 跟产品中的具体方法相对应 public abstract void setName(String name); // 建造产品 public abstract Product create(); }
(3)具体建造者类
每一个具体的 builder 都有本身独特的属性,都是对某一种具体 Product 的代理。
public class ConcreteBuilder extends Builder { private Product product = new Product();
// 设置具体的产品名字 @Override public void setName(String name) { product.setName(name); } @Override public Product create() { return product; } }
(4)指挥者(导演)类
经过 Director 来生成具体的 Product。但其实也能够直接采用链式的方式生成,每一个 ConcreteBuilder 的方法返回 this 便可,最后在调用 create 方法,即建立成功。这样就能够省去 Director 这个类,逻辑也更加清晰。
/** * 指挥者类 * 指挥者类起到封装的做用,避免高层模块深刻到建造者内部的实现类 */ public class Director { // 构建不一样的产品 private Builder mBuilder = null; // 根据须要传入具体的产品 public Director(Builder builder) { mBuilder = builder; } // 获取最终的产品 public Product getAProduct(String name) { mBuilder.setName(name); // 设置不一样的零件,产生不一样的产品 return mBuilder.create(); } }
在《 Effective Java 第2版 》中有提到,遇到多个构造器参数时要考虑使用构建器(Builder模式)。相比于重叠构造器(telescoping constructor)模式和 JavaBeans 模式,Builder 模式实现的对象更利于使用。
在这种模式下,你提供第一个只有必要参数的构造器,第二个构造器有一个可选参数,第三个有两个可选参数,依此类推,最后一个构造器包含全部的可选参数。下面看看其编程实现:
/** * 使用重叠构造器模式 */ public class Person { //必要参数 private final int id; private final String name; //可选参数 private final int age; private final String sex; private final String phone; private final String address; private final String desc; public Person(int id, String name) { this(id, name, 0); } public Person(int id, String name, int age) { this(id, name, age, ""); } public Person(int id, String name, int age, String sex) { this(id, name, age, sex, ""); } public Person(int id, String name, int age, String sex, String phone) { this(id, name, age, sex, phone, ""); } public Person(int id, String name, int age, String sex, String phone, String address) { this(id, name, age, sex, phone, address, ""); } public Person(int id, String name, int age, String sex, String phone, String address, String desc) { this.id = id; this.name = name; this.age = age; this.sex = sex; this.phone = phone; this.address = address; this.desc = desc; } }
从上面的代码中,当你想要建立实例的时候,就利用参数列表最短的构造器,但该列表中包含了要设置的全部参数:
Person person = new Persion(1, "李四", 20, "男", "18800000000", "China", "测试使用重叠构造器模式");
这个构造器调用一般须要许多你本不想设置的参数,但仍是不得不为它们传递值。
一句话:重叠构造器可行,可是当有许多参数的时候,建立使用代码会很难写,而且较难以阅读。
遇到许多构造器参数的时候,还有第二种代替办法,即 JavaBeans 模式。在这种模式下,调用一个无参构造器来建立对象,而后调用 setter 方法来设置每一个必要的参数,以及每一个相关的可选参数:
/** * 使用JavaBeans模式 */ public class Person { //必要参数 private int id; private String name; //可选参数 private int age; private String sex; private String phone; private String address; private String desc; public void setId(int id) { this.id = id; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } public void setSex(String sex) { this.sex = sex; } public void setPhone(String phone) { this.phone = phone; } public void setAddress(String address) { this.address = address; } public void setDesc(String desc) { this.desc = desc; } }
建立各个须要的对象:
Person p1=new Person(); Person p2=new Person("张三"); Person p3=new Person("李四",18); Person p4=new Person("王五",21,180); Person p5=new Person("赵六",17,170,65.4);
能够想象一下这样建立的坏处,最直观的就是四个参数的构造函数的最后面的两个参数究竟是什么意思,可读性不怎么好,若是不点击看源码,不知道哪一个是 weight 哪一个是 height。还有一个问题就是当有不少参数时,编写这个构造函数就会显得异常麻烦,这时候若是换一个角度,试试 Builder 模式,你会发现代码的可读性一会儿就上去了。
经过与其余两种模式的对比,才能更加清楚地知道建造者模式 (Builder) 到底好在什么地方,为何这样设计。
参考文献
一、建造者模式