概念
建造者模式又叫做生成器模式,是一个对象创建型的设计模式,主要是针对复杂对象的创建。
建造者模式可以将对象的构建与表示分离,使得同样的创建过程可以创建不同的表示。
Builder模式中的角色
- 抽象建造者(IBuilder):规定实现复杂产品对象哪些部分的创建,定义接口但并不涉及具体对象部件的创建。
- 具体建造者(Builder):实现抽象接口,完成复杂产品对象各部分部件的创建。在构建完成后提供产品实例。
- 产品类(product):要创建的复杂对象。
- 指挥者(Director):调用建造者指导完成复杂产品对象的创建,指导过程中不涉及到具体的产品信息,只保证对象各部分完整创建,或产品按照一定顺序创建。
Code
emm,产品是房子,Builder是工人,指挥者是监工,以此类比感觉挺贴切的,设计模式也是源于生活啊~
1、首先定义产品类
public class House {
private String floor;
private String wall;
private String roof;
public String getFloor() {
return floor;
}
public void setFloor(String floor) {
this.floor = floor;
}
public String getWall() {
return wall;
}
public void setWall(String wall) {
this.wall = wall;
}
public String getRoof() {
return roof;
}
public void setRoof(String roof) {
this.roof = roof;
}
@Override
public String toString() {
return "House{" +
"floor='" + getFloor() + '\'' +
", wall='" + getWall() + '\'' +
", roof='" + getRoof() + '\'' +
'}';
}
}
2、抽象建造者
public interface HouseBuilder {
void makeFloor();
void makeWall();
void makeRoof();
House create();// Product
}
3、具体的建造者
假如需要修建平房,可以直接写个HouseBuilder的实现类:
/**
* 平房修建者
*/
public class BungalowBuilder implements HouseBuilder {
private House house = new House();
@Override
public void makeFloor() {
house.setFloor("平房地板");
}
@Override
public void makeWall() {
house.setWall("平房墙");
}
@Override
public void makeRoof() {
house.setRoof("平房屋顶");
}
/**
* 外界使用
* */
@Override
public House create() {
return house;
}
}
4、指导者
指导者负责指导Builder完成工作
public class HouseDirector {
// 提供builder对象引用
private HouseBuilder houseBuilder;
public HouseDirector(HouseBuilder houseBuilder) {
this.houseBuilder = houseBuilder;
}
/**
* 指导建造者完成工作。如这里可以切换makeWall、makeRoof、makeFloor的顺序。
* 虽然顺序在本例中没有啥意义但是指导者实现了创建与装配算法的分离。
* */
public void makeHouse(){
houseBuilder.makeWall();
houseBuilder.makeRoof();
houseBuilder.makeFloor();
}
}
5、测试一下
/**
* 使用builder模式
*/
private static void test() {
HouseBuilder builder = new BungalowBuilder();// 平房建造者
HouseDirector houseDirector = new HouseDirector(builder);// 指导师指导完成
houseDirector.makeHouse();// 指导师干活
// 用户查看 修建的房子(产品)
House house = builder.getHouse();//其实House类应该隐藏 不能让用户直接使用(new),通过相关的builder提供实例
System.out.println(house.toString());
}
这时只要我们想要创建什么样的房子,我们就创建相应的建造者就行了,比如我们再创建个公寓,那就创建公寓建造者类。
public class FlatBuilder implements HouseBuilder {
private House house = new House();
@Override
public void makeFloor() {
house.setFloor("公寓地板");
}
@Override
public void makeWall() {
house.setWall("公寓墙");
}
@Override
public void makeRoof() {
house.setRoof("公寓屋顶");
}
@Override
public House create() {
return house;
}
}
6、uml
优缺点
1、优点
- 良好的封装性,此设计模式中一般产品了和建造者类比较稳定,而将业务逻辑都封装在指挥者类中对整体而言可以取得较好的稳定性。
- 隐藏了复杂对象的创建过程,客户端不必知道产品的具体细节,因此这使得产品本身与产品的创建过程解耦,可以相同的创建流程而产生不同的对象。
- 符合开闭原则,不会对原有功能引入风险。新需求来时可以通过创建新的创建者实现类来完成,基本上不用修改之前测试过的代码。
2、缺点
- 产品有类似的共同点,其组成部分相似若是产品之间的差异加大不建议使用建造者。这也是
链式改造
一顿操作猛如虎,把建造者刷了一遍,虽然优点很多,但是总感觉使用起来有点复杂:设计的角色众多、有时感觉那个指导者没啥卵用啊、直接new 产品多清晰 😝
的确建造者使用起来感觉角色挺多的,上述是标准的建造者,优点还是有的。不过针对如下场景一般使用链式改造的,使用起来更舒服些,逻辑看着更加清晰~
在开发中经常碰到这种场景:字段的构造注入,当构造器中有很多参数时,创建这个类的实例会让代码可读性非常差,而且很容易引入错误,这时可以使用链式改造的建造者模式。
链式改造方式上去除了指导者,增加了链式调用~
安卓的Dialog源码设计就是采取的这种方式~ 接下来我们也模拟下
/**
* Create by SunnyDay 2022/11/07 21:44:41
**/
public class AlertDialog {
private String title;
private String subTitle;
private String msg;
private String style;
private String sureListener;
private String cancelListener;
/**
* 1、私有构造:不让外部使用了
* 注意安卓源码中使用的为protected,这样其他包中也不能直接new AlertDialog。
* */
private AlertDialog(Builder builder){
this.title = builder.title;
this.subTitle = builder.subTitle;
this.msg = builder.msg;
this.style = builder.style;
this.sureListener = builder.sureListener;
this.cancelListener = builder.cancelListener;
}
/**
* 3、通过setXXX方法进行初始化。留意返回值。
* */
public static final class Builder{
public Builder setTitle(String title) {
this.title = title;
return this;
}
public Builder setSubTitle(String subTitle) {
this.subTitle = subTitle;
return this;
}
public Builder setMsg(String msg) {
this.msg = msg;
return this;
}
public Builder setStyle(String style) {
this.style = style;
return this;
}
public Builder setSureListener(String sureListener) {
this.sureListener = sureListener;
return this;
}
public Builder setCancelListener(String cancelListener) {
this.cancelListener = cancelListener;
return this;
}
/**
* 2、AlertDialog 的属性都copy到builder中
* */
private String title;
private String subTitle;
private String msg;
private String style;
private String sureListener;
private String cancelListener;
/**
* 4、暴漏一个方法提供Dialog引用。
* 注意方法返回值
* 注意传递的参数
* */
public AlertDialog create(){
return new AlertDialog(this);
}
}
public void show(){
StringBuilder sb = new StringBuilder();
sb.append("AlertDialog{").append("\n")
.append("title=").append(title).append("\n")
.append("subTitle=").append(subTitle).append("\n")
.append("msg=").append(msg).append("\n")
.append("style=").append(style).append("\n")
.append("sureListener=").append(sureListener).append("\n")
.append("cancelListener=").append(cancelListener).append("\n")
.append("}");
System.out.println(sb);
}
}
简单测试
/**
* Create by SunnyDay 2022/11/07 22:04:41
**/
public class Test {
public static void main(String[] args) {
AlertDialog dialog1 = new AlertDialog.Builder()
.setTitle("Dialog1标题")
.setSubTitle("Dialog1字标题")
.setMsg("Dialog1文案")
.setStyle("Dialog1样式")
.setSureListener("Dialog1确认按钮监听ing")
.setCancelListener("Dialog1取消按钮监听ing")
.create();
dialog1.show();
System.out.println("---------------------------");
AlertDialog dialog2 = new AlertDialog.Builder()
.setTitle("Dialog2标题")
.setSubTitle("Dialog2字标题")
.setMsg("Dialog2文案")
.setStyle("Dialog2样式")
.setSureListener("Dialog2确认按钮监听ing")
.setCancelListener("Dialog2取消按钮监听ing")
.create();
dialog2.show();
}
}
AlertDialog{
title=Dialog1标题
subTitle=Dialog1字标题
msg=Dialog1文案
style=Dialog1样式
sureListener=Dialog1确认按钮监听ing
cancelListener=Dialog1取消按钮监听ing
}
---------------------------
AlertDialog{
title=Dialog2标题
subTitle=Dialog2字标题
msg=Dialog2文案
style=Dialog2样式
sureListener=Dialog2确认按钮监听ing
cancelListener=Dialog2取消按钮监听ing
}
使用场景
建造者模式适合复杂对象的创建。
复杂对象的属性较多,复杂对象的各个属性经常面临着剧烈变化,但复杂对象的属性组合起来的算法相对稳定时这时可以选择建造者模式。