Java---23种设计模式(四)------建造者模式
1.什么是建造者模式
建造者模式定义:
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
建造者模式包括的角色:
(1)Builder:给出一个抽象接口或抽象类,以规范产品的建造。这个接口规定要实现复杂对象的哪些部分的创建,并不涉及具体的对象部件的创建,一般由子类具体实现。
(2)ConcreteBuilder:Builder接口的实现类,并返回组建好对象实例。
(3)Director:调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建。
(4)Product:要创建的复杂对象,产品类。
建造者模式的使用场景:
(1)当产品有复杂的内部构造时(参数很多)。
(2)需要生产的产品的属性相互依赖,这些属性的赋值顺序比较重要时(因为在调用ConcreteBuilder的赋值方法时是有先后顺序的)。
建造者角色分析:
建造者模式主要包含四个角色:
*** 抽象建造者(Builder):*** 它声明为创建一个产品对象的各个部件指定的抽象接口,在该接口中一般声明两类方法,一类方法是buildPartX(),它们用于创建复杂对象的各个部件;另一类方法是getResult(),它们用于返回复杂对象。Builder既可以是抽象类,也可以是接口,
*** 具体建造者(ConcreteBuilder):*** 实现抽象建造者接口,构建和装配各个部件,定义并明确它所创建的复杂对象,也可以提供一个方法返回创建好的复杂产品对象。
*** 指挥者(Director):*** 它负责安排复杂对象的建造次序,指挥者与抽象建造者之间存在关联关系,可以在其construct()建造方法中调用建造者对象的部件构造与装配方法,完成复杂对象的建造。客户端一般只需要与指挥者进行交互,在客户端确定具体建造者的类型,并实例化具体建造者对象(也可以通过配置文件和反射机制) ,然后通过指挥者类的构造函数或者Setter方法将该对象传入指挥者类中。它主要是用于创建一个复杂的对象,它主要有两个作用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程。
*** 产品(Product):*** 产品角色,一个具体的产品对象。
2.建造者模式的使用案例
1.
java.lang.StringBuilder 中的建造者模式
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;
}
StringBuilder 中的 append 方法使用了建造者模式,不过装配方法只有一个,并不算复杂,append 方法返回的是 StringBuilder 自身
public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence {
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
// ...省略...
}
StringBuilder 的父类 AbstractStringBuilder 实现了 Appendable 接口
abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value;
int count;
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;
}
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
// ...省略...
}
我们可以看出,Appendable 为抽象建造者,定义了建造方法,StringBuilder 既充当指挥者角色,又充当产品角色,又充当具体建造者,建造方法的实现由 AbstractStringBuilder 完成,而 StringBuilder 继承了 AbstractStringBuilder
---------------------
java.lang.StringBuffer 中的建造者方法
StringBuffer 继承与实现关系如下
这分明就与 StringBuilder 一样嘛!
那它们有什么不同呢?
public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequence {
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
//...省略...
}
看 StringBuffer 的源码如上,它们的区别就是: StringBuffer 中的 append 加了 synchronized 关键字,所以StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的
StringBuffer 中的建造者模式与 StringBuilder 是一致的
---------------------
其他例子:
mybatis 中的建造者模式
我们来看 org.apache.ibatis.session
包下的 SqlSessionFactoryBuilder
类
3.建造者模式的类图
4.建造者模式的用法
需求:
比如在玩“极品飞车”这款游戏,那么每一关的地图会千变万化,简单的来说,地图会有晴天和阴天之分,那么创建地图时就要根据晴天或者阴天来对地图上的场景,比如:天空,树,房子,和路面进行渲染,这个过程是一个固定的,每创建一个新地图都要执行这几个渲染,这是针对高级配置的电脑来说的。
现在拥有低配置电脑的人不在少数,那么他们就不能玩游戏了吗?完全可以!只要将地图中占用资源比较高的渲染去掉就可以,比如带反射光影的树,这时候需要创建不同的地图,但地图的创建过程却是固定的,建造者模式完全可以应对这样的情况。
新建地图接口:
新建晴天地图类:
新建阴天地图类:
新建高画质builder建造者类:
新建低画质builder建造者类:
新建客户端运行类:
运行结果:
分析
建造者模式将不变的创建过程进行封装,创建的过程与main分法进行分离,这样内部的创建过程就和表示层的代码进行分开,有利于创建过程功能上的修改。另外可以发现,代码的设计和功能有些类似于facade外观模式,区别在于,建造者模式目的在于以相同的构建过程通过不同的建造者得到不同的结果,而外观模式并不需要不同的建造者,也不希望得到不同的结果,只是简单的将几个接口合并成高级的一个接口,不影响原有的结果,目的是使调用变得更加容易。
本程序中有2个建造者:高画者建造者和低画质建造者,它们都封装了创建地图的过程,这个过程很固定,但通过不同的建造者类可以返回不样式的地图,建造者规定了对象创建的过程,比如高画质的建造者的创建过程为:
map_interface_ref.create_weather(); map_interface_ref.create_house(); map_interface_ref.create_tree(); map_interface_ref.create_way(); |
必须得执行4个方法才可以创建一个高画质的地图,如果不使用建造者模式,直接调用map类的create_xxxx方法,如果create_xxxx方法有几十个,那么很有可能就把其中的某些方法忘记调用而影响最终地图的效果了,所以我们要使用建造者模式来规定地图创建的过程,这就是一个“行为守则”。
5建造者模式的优点与缺点
*** 优点:***
(1) 在建造者模式中,客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
(2) 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。由于指挥者类针对抽象建造者编程,增加新的具体建造者无须修改原有类库的代码,系统扩展方便,符合“开闭原则”
(3) 可以更加精细地控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
*** 缺点:***
(1) 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,例如很多组成部分都不相同,不适合使用建造者模式,因此其使用范围受到一定的限制。
(2) 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,增加系统的理解难度和运行成本。
*** 使用场景:***
(1) 需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性。
(2) 需要生成的产品对象的属性相互依赖,需要指定其生成顺序。
(3) 对象的创建过程独立于创建该对象的类。在建造者模式中通过引入了指挥者类,将创建过程封装在指挥者类中,而不在建造者类和客户类中。
(4) 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。