建造者模式
定义:
GOF 给建造模式的定义为:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示.
- 这句话说得很抽象,不好理解,其实它的意思可以理解为:将构造复杂对象的过程和组成对象的部件解耦
组成:
案例一
需求:
- 地形模型对象 Terrain,
- 其中地形由城墙Wall, 堡垒Fort, 地雷Mine组成
- 每一个构建对象,又有x, y, w, h四个方向指标属性
产品角色 Terrain:
public class Terrain {
Wall w; //城墙
Fort f; //堡垒
Mine m; //地雷
@Override
public String toString() {
return "Terrain{" +
"w=" + w +
", f=" + f +
", m=" + m +
'}';
}
}
class Wall {
int x, y, w, h;
public Wall(int x, int y, int w, int h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
@Override
public String toString() {
return "Wall{" +
"x=" + x +
", y=" + y +
", w=" + w +
", h=" + h +
'}';
}
}
class Fort {
int x, y, w, h;
public Fort(int x, int y, int w, int h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
@Override
public String toString() {
return "Fort{" +
"x=" + x +
", y=" + y +
", w=" + w +
", h=" + h +
'}';
}
}
class Mine {
int x, y, w, h;
public Mine(int x, int y, int w, int h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
@Override
public String toString() {
return "Mine{" +
"x=" + x +
", y=" + y +
", w=" + w +
", h=" + h +
'}';
}
}
抽象建造者角色 TerrainBuilder:
public interface TerrainBuilder {
TerrainBuilder buildWall();
TerrainBuilder buildFort();
TerrainBuilder buildMine();
Terrain build();
}
具体建造者角色 ComplexTerrainBuilder
public class ComplexTerrainBuilder implements TerrainBuilder {
Terrain terrain = new Terrain();
@Override
public TerrainBuilder buildWall() {
terrain.w = new Wall(10, 10, 10, 10);
return this;
}
@Override
public TerrainBuilder buildFort() {
terrain.f = new Fort(20, 20, 20, 20);
return this;
}
@Override
public TerrainBuilder buildMine() {
terrain.m = new Mine(50, 50, 50, 50);
return this;
}
@Override
public Terrain build() {
return terrain;
}
}
指导者角色 Main
- 指导者角色:调用具体建造者角色以创建产品对象.
public class Main {
public static void main(String[] args) {
TerrainBuilder builder = new ComplexTerrainBuilder();
Terrain t = builder.buildFort().buildMine().buildWall().build();
System.out.println(t);
}
}
链式写法
注意:每个创建部分产品最后的返回值返回的都是this,这样就可以实现链式写法.
不用和之前一样一行行去调用,直接通过链式写法一直往下build,最终调用build()方法返回完整的产品对象。
StringBuilder链式写法
案例二
- Person类 有很多属性,现在我们创建时,需要有针对的对某些属性赋值
Person
public class Person {
int id; //id
String name; //姓名
int age; //年级
double weight; //体重
int score; //考试分数
Location loc; //家庭住址
private Person() {}
public static class PersonBuilder {
Person p = new Person();
public PersonBuilder basicInfo(int id, String name, int age) {
p.id = id;
p.name = name;
p.age = age;
return this;
}
public PersonBuilder weight(double weight) {
p.weight = weight;
return this;
}
public PersonBuilder score(int score) {
p.score = score;
return this;
}
public PersonBuilder loc(String street, String roomNo) {
p.loc = new Location(street, roomNo);
return this;
}
public Person build() {
return p;
}
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", weight=" + weight +
", score=" + score +
", loc=" + loc +
'}';
}
}
class Location {
String street; //街道
String roomNo; //门牌号码
public Location(String street, String roomNo) {
this.street = street;
this.roomNo = roomNo;
}
@Override
public String toString() {
return "Location{" +
"street='" + street + '\'' +
", roomNo='" + roomNo + '\'' +
'}';
}
}
1. 私有化 Person构造方法
2. 把构建者PersonBuilder 写成静态内部类形式,
测试 Main
public class Main {
public static void main(String[] args) {
Person p = new Person.PersonBuilder()
.basicInfo(1, "慧慧", 28)
//.score(20)
.weight(135)
.loc("冬瓜山", "23")
.build();
System.out.println(p);
}
}
建造者模式—在源码中的应用
Netty中怎么使用Builder设计模式:
- 如下图所示,大家还记得,在服务端启动的时候有个启动辅助类ServerBootStrap,我们调用group方法、channel方法设置参数。这里面也使用了链式编程来设置相关参数。
- 我们到源码中看一下group方法。调用完设置参数后,返回this对象。
- 我们再看看其他方法,比如childOption方法,最后设置完参数依然返回this。
其他方法依然如此。这样设计的目的在于可以自由选择设置相关参数而不强求绑定。这样可以根据使用场景进行自由的设置参数。但是缺点也是明显的,那就是使用者自己要清楚设置哪些参数。
在MyBatis源码中的应用:
MyBatis 中 SqlSessionFactoryBuiler 类用到了建造者模式
在 MyBatis 中 SqlSessionFactory是由 SqlSessionFactoryBuilder 产生的.
上面就是构建一个SqlSessionFactory对象的方法,我们看到里面的XMLConfigBuilder其实就是一个具体建造者角色,而其父类BaseBuilder就是一个抽象建造者角色。
- XMLConfigBuilder----- 具体建造者角色
- 父类BaseBuilder----- 抽象建造者角色
下图就是BaseBuilder和其部分子类(为了方便截图,没有显示出所有子类):
总结:
适用场景:
建造者模式适用于一个具有较多的零件的复杂产品创建过程,而且产品的各个组成零件还会经常发生变化或者说需要支持动态变化,但是零件的种类却总体稳定的场景:
- 相同的方法,不同的执行顺序需要产生不同的执行结果
- 产品类非常复杂,调用不同的零件或者按照不同的顺序来组装产品后需要得到不同的产品
- 当初始化一个**对象非常复杂,而且很多参数都具有默认值**
优点:
- 封装性好,创建和使用分离
- 扩展性好,建造类之间独立,一定程度上实现了解耦:
缺点:
- 产生多余的Builder对象
- 产品内部发生变化时,建造者都需要修改,成本较大
建造者模式和工厂模式区别:
建造者模式优点类似于工厂模式,都是用来创建一个对象,但是他们还是有很大的区别,主要区别如下:
- 建造者模式更加注重方法的调用顺序,工厂模式注重于创建完整对象
- 建造者模式根据不同的产品零件和顺序可以创造出不同的产品,而工厂模式创建出来的产品都是一样的
- 造者模式使用者需要知道这个产品有哪些零件组成,而工厂模式的使用者不需要知道,直接创建就行
创建模式着重于逐步将组件装配成一个成品并向外提供成品,
而抽象工厂模式着重于得到产品族中相关的多个产品对象