七、建造者模式
需求示例
需要建一栋房子,步骤:打地基 -> 砌墙 -> 封顶 (不管是别墅还是小破楼都是这一流程)
现在需要创建不同的房子:别墅、小破楼等等
传统的解决方案
代码示例:
抽象实现过程
/**
* @author cVzhanshi
* @create 2023-06-20 16:58
*/
public abstract class AbstractHouse {
// 建房子步骤一
public abstract void buildOne();
// 建房子步骤二
public abstract void buildTwo();
// 建房子步骤三
public abstract void buildThree();
// 建房子
public void build() {
buildOne();
buildTwo();
buildThree();
}
}
具体实现过程子类
/**
* @author cVzhanshi
* @create 2023-06-20 17:00
*/
// 小房子
@Slf4j
public class SmallHouse extends AbstractHouse {
@Override
public void buildOne() {
log.info("建小房子第一步:buildOne");
}
@Override
public void buildTwo() {
log.info("建小房子第二步:buildTwo");
}
@Override
public void buildThree() {
log.info("建小房子第三步:buildThree");
}
}
// 大房子
@Slf4j
public class BigHouse extends AbstractHouse{
@Override
public void buildOne() {
log.info("建大房子第一步:buildOne");
}
@Override
public void buildTwo() {
log.info("建大房子第二步:buildTwo");
}
@Override
public void buildThree() {
log.info("建大房子第三步:buildThree");
}
}
客户端建房子
@Slf4j
public class Client {
public static void main(String[] args) {
// 建大房子
BigHouse bigHouse = new BigHouse();
bigHouse.build();
log.info("=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=");
// 建小房子
SmallHouse smallHouse = new SmallHouse();
smallHouse.build();
}
}
// 输出
17:15:44.332 [main] INFO cn.cvzhanshi.design.builder.BigHouse - 建大房子第一步:buildOne
17:15:44.336 [main] INFO cn.cvzhanshi.design.builder.BigHouse - 建大房子第二步:buildTwo
17:15:44.336 [main] INFO cn.cvzhanshi.design.builder.BigHouse - 建大房子第三步:buildThree
17:15:44.336 [main] INFO cn.cvzhanshi.design.builder.Client - =-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=
17:15:44.338 [main] INFO cn.cvzhanshi.design.builder.SmallHouse - 建小房子第一步:buildOne
17:15:44.338 [main] INFO cn.cvzhanshi.design.builder.SmallHouse - 建小房子第二步:buildTwo
17:15:44.338 [main] INFO cn.cvzhanshi.design.builder.SmallHouse - 建小房子第三步:buildThree
达到了new不同类实现不同的房子
分析:
- 优点:比较好理解,清晰明了;
- 缺点:设计的程序结构,过于简单,没有设计缓存层对象,程序的扩展和维护不好。把产品和创建产品的过程封装在了一起,耦合性增强了。
- 解决方案:将产品和产品的创建过程解耦 => 建造者模式
7.1 建造者模式基本介绍
-
它可以把复杂对象的建造过程抽象出来,使这个抽象过程的不同实现方法可以构造出不同属性的对象
-
建造者模式是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。
四个角色
- Product(产品角色):一个具体的产品对象。
- Builder(抽象建造者):创建一个Product对象的各个属性的指定接口或抽象类。
- ConcreteBuilder(具体建造者):实现接口或者继承抽象类,构建产品的各个属性。
- Director(指挥者):构建一个使用Builder接口的对象。它主要是用于创建一个复杂的对象。它主要有两个作用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程。
对应的原理类图:
7.2 建造者模式实现需求示例
思路分析类图
根据类图编写代码
产品类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class House {
private String one;
private String two;
private String three;
}
抽象的建造者
/**
* @author cVzhanshi
* @create 2023-06-20 18:20
*/
public abstract class HouseBuilder {
protected House house = new House();
// 建房子步骤一
public abstract void buildOne();
// 建房子步骤二
public abstract void buildTwo();
// 建房子步骤三
public abstract void buildThree();
public House buildHouse() {
return house;
}
}
具体实现的建造者
@Slf4j
public class LowHouseBuilder extends HouseBuilder {
@Override
public void buildOne() {
log.info("小破楼:One");
}
@Override
public void buildTwo() {
log.info("小破楼:Two");
}
@Override
public void buildThree() {
log.info("小破楼:Three");
}
}
@Slf4j
public class HighHouseBuilder extends HouseBuilder{
@Override
public void buildOne() {
log.info("建高楼咯:One");
}
@Override
public void buildTwo() {
log.info("建高楼咯:Two");
}
@Override
public void buildThree() {
log.info("建高楼咯:Three");
}
}
指挥者
@Data
@AllArgsConstructor
public class HouseDirector {
private HouseBuilder houseBuilder;
public House constructHouse() {
houseBuilder.buildOne();
houseBuilder.buildTwo();
houseBuilder.buildThree();
return houseBuilder.buildHouse();
}
}
客户端使用
@Slf4j
public class Client {
public static void main(String[] args) {
// 盖小破楼
HouseBuilder lowHouseBuilder = new LowHouseBuilder();
// 准备创建房子的指挥者
HouseDirector houseDirector = new HouseDirector(lowHouseBuilder);
// 完成盖房子,返回产品
House house = houseDirector.constructHouse();
log.info("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-");
// 盖高楼
HouseBuilder highHouseBuilder = new HighHouseBuilder();
// 准备创建房子的指挥者
HouseDirector houseDirector2 = new HouseDirector(highHouseBuilder);
// 完成盖房子,返回产品
House house2 = houseDirector2.constructHouse();
}
}
// 输出
18:37:39.160 [main] INFO cn.cvzhanshi.design.builder.LowHouseBuilder - 小破楼:One
18:37:39.164 [main] INFO cn.cvzhanshi.design.builder.LowHouseBuilder - 小破楼:Two
18:37:39.164 [main] INFO cn.cvzhanshi.design.builder.LowHouseBuilder - 小破楼:Three
18:37:39.164 [main] INFO cn.cvzhanshi.design.builder.Client - -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
18:37:39.164 [main] INFO cn.cvzhanshi.design.builder.HighHouseBuilder - 建高楼咯:One
18:37:39.164 [main] INFO cn.cvzhanshi.design.builder.HighHouseBuilder - 建高楼咯:Two
18:37:39.164 [main] INFO cn.cvzhanshi.design.builder.HighHouseBuilder - 建高楼咯:Three
7.3 建造者模式在JDK源码中的使用
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的抽象方法都实现了
abstract class AbstractStringBuilder implements Appendable, CharSequence {
@Override
public AbstractStringBuilder append(CharSequence s) {
if (s == null)
return appendNull();
if (s instanceof String)
return this.append((String)s);
if (s instanceof AbstractStringBuilder)
return this.append((AbstractStringBuilder)s);
return this.append(s, 0, s.length());
}
@Override
public AbstractStringBuilder append(CharSequence s, int start, int end) {
if (s == null)
s = "null";
if ((start < 0) || (start > end) || (end > s.length()))
throw new IndexOutOfBoundsException(
"start " + start + ", end " + end + ", s.length() "
+ s.length());
int len = end - start;
ensureCapacityInternal(count + len);
for (int i = start, j = count; i < end; i++, j++)
value[j] = s.charAt(i);
count += len;
return this;
}
@Override
public AbstractStringBuilder append(char c) {
ensureCapacityInternal(count + 1);
value[count++] = c;
return this;
}
}
StringBuilder中的Override并不是重写,它调用的还是父类AbstractStringBuilder的方法,所以他是个指挥者
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
}
源码中建造者模式的角色分析
7.4 建造者模式的注意事项和细节
- 客户端(使用程序)不需要知道产品内部组成的细节,将产品本身与产品的创建过程解耦,可以让相同的创建过程可以创建出不同的产品。
- 每一个具体建造者都相对独立,与其他的具体建造者都无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得不同的产品对象。
- 可以更加精细地控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
- 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭原则”。
- 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
- 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,因此在这种情况下,要考虑是否选择建造者模式。
抽象工厂模式 VS 建造者模式
- 抽象工厂模式实现对一个产品家族的创建;产品家族具有不同分类维度的产品组合,抽象工厂模式不需要关心构建过程,只关心产品由什么工厂生产。
- 建造者模式则是需要按照指定的产品生产过程来构建产品,它主要目的是通过组装零配件而产生一个新产品。