1.盖房项目实际需求
- 需要建房子:这一过程为打桩、 砌墙、封顶
- 房子有各种各样的,比如普通房,高楼,别墅,各种房子的过程虽然一样,但是各自实现的细节不同
代码实现:
AbstractHouse:
房子的抽象父类,指定建造房子的规范,以及建造房子的具体流程
/** 建造者模式 :传统方式
* @author compass
* @version 1.0
* @date 2021-07-07 11:13
*/
public abstract class AbstractHouse {
/**
* 打地基
*/
public abstract void buildBasic();
/**
* 砌墙
*/
public abstract void buildWalls();
/**
* 封顶
*/
public abstract void roofed();
public void build(){
buildBasic();
buildWalls();
roofed();
}
}
CommonHouse:
普通房子,继承 AbstractHouse 类,实现了建造房子中各个步骤的具体细节
/**
* @author compass
* @version 1.0
* @date 2021-07-07 11:16
*/
public class CommonHouse extends AbstractHouse {
/**
* 打地基
*/
@Override
public void buildBasic() {
System.out.println("普通房顶打地基");
}
/**
* 砌墙
*/
@Override
public void buildWalls() {
System.out.println("普通房顶打地基");
}
/**
* 封顶
*/
@Override
public void roofed() {
System.out.println("普通房顶打封顶");
}
}
HighBuilding:
高楼大厦,继承 AbstractHouse 类,实现了建造房子中各个步骤的具体细节
/**
* @author compass
* @version 1.0
* @date 2021-07-07 11:26
*/
public class HighHouse extends AbstractHouse{
/**
* 打地基
*/
@Override
public void buildBasic() {
System.out.println("高级房子打地基");
}
/**
* 砌墙
*/
@Override
public void buildWalls() {
System.out.println("高级房子砌墙");
}
/**
* 封顶
*/
@Override
public void roofed() {
System.out.println("高级房子封顶");
}
}
传统方式优缺点分析
- 优点是比较好理解,简单易操作。
- 设计的程序结构,过于简单,没有设计缓存层对象,程序的扩展和维护不好,也就是说,这种设计方案,把产品(即:房子) 和创建产品的过程(即:建房子流程) 封装在一起,代码耦合性增强了。
- 解决方案:将产品和产品建造过程解耦 --> 建造者模式
2.建造者模式基本介绍
- 建造者模式(Builder Pattern) 又叫生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。
- 建造者模式是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。
- 实际应用场景:建造房子、组装车辆
3.建造者模式的四个角色
Product(产品角色)
: 一个具体的产品对象Builder(抽象建造者)
: 创建一个Product对象的抽象接口(或抽象类),抽象建造者主要负责规范建造的流程,不关心具体的建造细节
3.ConcreteBuilder(具体建造者)
: 实现接口,构建和装配各个部件,具体建造者负责实现具体的建造细节Director(指挥者)
: 构建一个使用Builder接口的具体实现类的对象。它主要是用于创建一个复杂的对象。它主要有两个作用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程
建造者模式UML类图
Product(产品类)
:一个具体的产品Builder(抽象建造者)
:Builder 中组合了一个 Product 实例ConcreteBuilder(具体建造者)
:实现了 Builder 中的抽象方法Director(指挥者)
:将 Builder 的具体实现类聚合到 Director 中,在 Director 中调用具体的 Builder 完成具体产品的制造
4.建造者模式解决盖房问题
案例需求
需要建房子:这一过程为打桩、 砌墙、封顶。不管是普通房子也好,别墅也好都需要经历这些过程, 下面我们使用建造者模式(Builder Pattern)
来完成
代码实现
House:
产品类
/** 建造者模式
* @author compass
* @version 1.0
* @date 2021-07-07 15:17
*/
public class House {
private String base;
private String wall;
private String roofed;
public House(String base, String wall, String roofed) {
this.base = base;
this.wall = wall;
this.roofed = roofed;
}
public House() {
}
public String getBase() {
return base;
}
public void setBase(String base) {
this.base = base;
}
public String getWall() {
return wall;
}
public void setWall(String wall) {
this.wall = wall;
}
public String getRoofed() {
return roofed;
}
public void setRoofed(String roofed) {
this.roofed = roofed;
}
@Override
public String toString() {
return "House{" +
"base='" + base + '\'' +
", wall='" + wall + '\'' +
", roofed='" + roofed + '\'' +
'}';
}
}
HouseBuilder:
抽象建造者,规定制造房子的规范,并提供 buildHouse() 方法返回制造好的房子(产品)
/**
* @author compass
* @version 1.0
* @date 2021-07-07 15:18
*/
public abstract class HouseBuilder {
private House house =new House();
/**
* 打地基
*/
public abstract void buildBasic ();
/**
* 砌墙
*/
public abstract void buildWall();
/**
* 封顶
*/
public abstract void roofed();
/**
* 将House 构建好之后就返回
* @return
*/
public House build(){
return house;
}
}
CommonHouse:
具体建造者,负责建造普通房子,重写父类 HouseBuilder 中的抽象方法来指定普通房子的建造细节
/**
* @author compass
* @version 1.0
* @date 2021-07-07 15:21
*/
public class CommonHouse extends HouseBuilder {
/**
* 打地基
*/
@Override
public void buildBasic() {
System.out.println("普通房子打地基");
}
/**
* 砌墙
*/
@Override
public void buildWall() {
System.out.println("普通房子砌墙");
}
/**
* 封顶
*/
@Override
public void roofed() {
System.out.println("普通房子封顶");
}
}
HighBuilding:
具体建造者,负责建造高楼大厦,重写父类 HouseBuilder 中的抽象方法来指定高楼大厦的建造细节
/**
* @author compass
* @version 1.0
* @date 2021-07-07 15:30
*/
public class HighBuilding extends HouseBuilder {
/**
* 打地基
*/
@Override
public void buildBasic() {
System.out.println("高楼房子打地基");
}
/**
* 砌墙
*/
@Override
public void buildWall() {
System.out.println("高楼房子砌墙");
}
/**
* 封顶
*/
@Override
public void roofed() {
System.out.println("高楼房子封顶");
}
}
Client:
客户端,发出建造房子的命令
/**
* @author compass
* @version 1.0
* @date 2021-07-07 15:18
*/
public class Client {
public static void main(String[] args) {
CommonHouse commonHouse = new CommonHouse();
HouseDirector director = new HouseDirector(commonHouse);
director.construct();
HighBuilding building = new HighBuilding();
HouseDirector director2 = new HouseDirector(building);
director2.construct();
}
}
HouseDirector:
指挥者,指挥具体的 Builder 对象制造产品,可指定制造产品的流程
/** 动态的去指定制作流程
* @author compass
* @version 1.0
* @date 2021-07-07 15:22
*/
public class HouseDirector {
private HouseBuilder houseBuilder = null;
public HouseDirector(HouseBuilder houseBuilder) {
this.houseBuilder = houseBuilder;
}
public void setHouseBuilder(HouseBuilder houseBuilder) {
this.houseBuilder = houseBuilder;
}
// 将构造房子交给指挥者
public House construct() {
houseBuilder.buildBasic();
houseBuilder.buildWall();
houseBuilder.roofed();
return houseBuilder.build();
}
}
总结
-
Housebuilder
是抽象建造者,提供需要实现的功能,并且组合了产品House
-
CommonHouse和HighHouse继承自HouseBuilder,并且实现父类的方法
-
HouseDirector
中聚合了Housebuilder
,也就是说只要是Housebuilder的子类
,他都可以进行指挥调用。 -
Client
只需要去调用HouseDirector(传入具体的建造者,让指挥者去调用建造者即可)
5.JDK StringBuilder
StringBuilder的 append() 方法:调用父类
AbstractStringBuilder 的 append() 方法
AbstractStringBuilder
的 append() 方法是由Appendable
接口定义的规范
Appendable
接口:定义了 append() 方法的规范,相当于是一个抽象的建造者
源码中建造者模式角色分析
- Appendable 接口定义了多个 append() 方法(抽象方法),即
Appendable 为抽象建造者
,定义了制造产品的抽象方法(规范),抽象定义了 append方法能做的事情
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 接口方法,这里的 AbstractStringBuilder 已经是
建造者
,只是不能实例化
abstract class AbstractStringBuilder implements Appendable, CharSequence {
/**
* The value is used for character storage.
*/
char[] value;
/**
* The count is the number of characters used.
*/
int count;
/**
* This no-arg constructor is necessary for serialization of subclasses.
*/
AbstractStringBuilder() {
}
/**
* Creates an AbstractStringBuilder of the specified capacity.
*/
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
/**
* Returns the length (character count).
*
* @return the length of the sequence of characters currently
* represented by this object
*/
@Override
public int length() {
return count;
}
public AbstractStringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
// Documentation in subclasses because of synchro difference
public AbstractStringBuilder append(StringBuffer sb) {
if (sb == null)
return appendNull();
int len = sb.length();
ensureCapacityInternal(count + len);
sb.getChars(0, len, value, count);
count += len;
return this;
}
/**
* @since 1.8
*/
AbstractStringBuilder append(AbstractStringBuilder asb) {
if (asb == null)
return appendNull();
int len = asb.length();
ensureCapacityInternal(count + len);
asb.getChars(0, len, value, count);
count += len;
return this;
}
// Documentation in subclasses because of synchro difference
@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());
}
private AbstractStringBuilder appendNull() {
int c = count;
ensureCapacityInternal(c + 4);
final char[] value = this.value;
value[c++] = 'n';
value[c++] = 'u';
value[c++] = 'l';
value[c++] = 'l';
count = c;
return this;
}
public AbstractStringBuilder append(char[] str) {
int len = str.length;
ensureCapacityInternal(count + len);
System.arraycopy(str, 0, value, count, len);
count += len;
return this;
}
public AbstractStringBuilder append(boolean b) {
if (b) {
ensureCapacityInternal(count + 4);
value[count++] = 't';
value[count++] = 'r';
value[count++] = 'u';
value[count++] = 'e';
} else {
ensureCapacityInternal(count + 5);
value[count++] = 'f';
value[count++] = 'a';
value[count++] = 'l';
value[count++] = 's';
value[count++] = 'e';
}
return this;
}
@Override
public AbstractStringBuilder append(char c) {
ensureCapacityInternal(count + 1);
value[count++] = c;
return this;
}
public AbstractStringBuilder append(int i) {
if (i == Integer.MIN_VALUE) {
append("-2147483648");
return this;
}
int appendedLength = (i < 0) ? Integer.stringSize(-i) + 1
: Integer.stringSize(i);
int spaceNeeded = count + appendedLength;
ensureCapacityInternal(spaceNeeded);
Integer.getChars(i, spaceNeeded, value);
count = spaceNeeded;
return this;
}
public AbstractStringBuilder append(long l) {
if (l == Long.MIN_VALUE) {
append("-9223372036854775808");
return this;
}
int appendedLength = (l < 0) ? Long.stringSize(-l) + 1
: Long.stringSize(l);
int spaceNeeded = count + appendedLength;
ensureCapacityInternal(spaceNeeded);
Long.getChars(l, spaceNeeded, value);
count = spaceNeeded;
return this;
}
public AbstractStringBuilder append(float f) {
FloatingDecimal.appendTo(f,this);
return this;
}
public AbstractStringBuilder append(double d) {
FloatingDecimal.appendTo(d,this);
return this;
}
}
- StringBuilder 既充当了
指挥者角色,同时充当了具体的建造者
, 因为建造方法的实现是由 AbstractStringBuilder 完成,而 StringBuilder 继承了AbstractStringBuilder
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
@Override
public StringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
public StringBuilder append(StringBuffer sb) {
super.append(sb);
return this;
}
@Override
public StringBuilder append(CharSequence s) {
super.append(s);
return this;
}
@Override
public StringBuilder append(CharSequence s, int start, int end) {
super.append(s, start, end);
return this;
}
@Override
public StringBuilder append(char[] str) {
super.append(str);
return this;
}
}
- 建造者模式的注意事项和细节
- 客户端(使用程序)不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象
- 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者, 用户使用不同的具体建造者即可得到不同的产品对象
- 可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程
增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合 “开闭原则” - 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制
如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大, 因此在这种情况下,要考虑是否选择建造者模式
抽象工厂模式 和 建造者模式的区别
- 抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式不需要关心构建过程,只关心什么产品由什么工厂生产即可
- 而建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品,可以这样理解:建造者模式制造产品需要有一个具体的流程,对于不同产品整体流程相差不大,但是每个流程的实现细节较大