介绍
建造者模式(Builder Pattern)是一种创建型设计模式,它通过将一个复杂对象的构建过程分解为多个简单的步骤,并允许按照一定的顺序和规则逐步构建对象。这样可以使得同样的构建过程可以创建不同的表示。
建造者模式的核心思想是将对象的构建与其表示分离开来。通过引入一个独立的建造者类,负责控制对象的构建过程,客户端只需要指定需要构建的类型和顺序,而无需关心具体的构建过程。
模式角色
(1) 指挥者角色(Director)
负责调用适当的ConcreteBuilder来组建产品。Director不直接与Product类进行操作,而是通过操作ConcreteBuilder对象实现对Product对象的参数设置。Director被用来封装程序中易变的部分,如Product对象各种不同的参数设置,在Director类中有不同的构造函数;
(2) 抽象建造者角色(Builder)
引入抽象建造者是为了将建造的具体过程交给它的子类来实现,这样更容易扩展。Builder中至少会有两个抽象方法,一个用来设置产品属性(即组建产品),一个是用来返回(并建造)产品。
(3) 具体建造者角色(ConcreteBuilder)
实现Builder接口,一般是两项任务:组建产品;返回组建好的产品。
(4) 产品角色(Product)
一般是一个较为复杂的对象,也就是说创建对象的过程比较复杂,一般会有比较多的代码量。在本类图中,产品类是一个具体的类,而非抽象类。实际编程中,产品类可以是由一个抽象类与它的不同实现组成,也可以是由多个抽象类与他们的实现组成。
完整的建造者模式
《Android源码设计模式解析与实战》 第3章
计算机的组装过程较为复杂,并且组装顺序是不固定的,为了易于理解,我们把计算机组装的过程简化为构建主机(包含CPU和RAM,是必要组件)、其它组件(如主板、显示器、GPU),然后通过Director操作具体的Builder类来构建不同的计算机对象(lenovo和mac)。
代码示例:
package com.creational;
/**
* 使用建造者模式模拟构建一台电脑
*/
public class BuilderPattern {
public static void main(String[] args) {
//创建一个实际建造类,传入必要参数
ComputerBuilder lenovoBuilder = new LenovoComputerBuilder("Intel i7", "三星16G");
//导演类构造一个使用Builder接口的对象,与实际Builder类直接交互
ComputerDirector director = new ComputerDirector(lenovoBuilder);
//导演类通过持有的Builder引用,对实际建造者类进行操作(即可选组件的构建)
director.construct("微星", "2K", "3080");
//实际建造者类返回实例
ComputerProduct lenovoComputer = lenovoBuilder.buildComputer();
System.out.println("Lenovo Computer:" + lenovoComputer.toString());
ComputerBuilder macBuilder = new MacComputerBuilder("Apple M1", "海力士8G");
director = new ComputerDirector(macBuilder);
director.construct();
ComputerProduct macComputer = macBuilder.buildComputer();
System.out.println("Mac Computer:" + macComputer.toString());
}
}
/**
* Director被用来封装程序中易变的部分,
* 如Product对象各种不同的参数设置,在Director类中有不同的构造函数
*/
class ComputerDirector {
ComputerBuilder mBuilder;
//可选属性的默认参数
String mBoard = "华硕";
String mDisplay = "LG";
String mGpu = "3060Ti";
public ComputerDirector(ComputerBuilder builder) {
mBuilder = builder;
}
public void construct(String board, String display, String gpu) {
mBuilder.setBoard(board);
mBuilder.setDisplay(display);
mBuilder.setGpu(gpu);
}
public void construct(String mainboard, String display) {
this.construct(mainboard, display, mGpu);
}
public void construct() {
this.construct(mBoard, mDisplay, mGpu);
}
}
/**
* 具体的产品类,通常在建造者模式中是一个较为复杂的对象,创建过程也比较复杂
* 此处进行了一些简化
*/
class ComputerProduct {
private String cpu;//必须
private String ram;//必须Computer
private String board;//可选
private String display;//可选
private String gpu;//可选
public ComputerProduct(String cpu, String ram) {
this.cpu = cpu;
this.ram = ram;
}
public void setBoard(String board) {
this.board = board;
}
public void setDisplay(String display) {
this.display = display;
}
public void setGpu(String gpu) {
this.gpu = gpu;
}
public String toString() {
return "Computer{" +
"cpu=" + cpu +
", ram=" + ram +
", board=" + board +
", display=" + display +
", gpu=" + gpu +
'}';
}
}
/**
* 引入抽象建造者是为了将建造的具体过程交给它的子类来实现,这样更容易扩展
*/
interface ComputerBuilder {
void setBoard(String board);
void setDisplay(String display);
void setGpu(String gpu);
ComputerProduct buildComputer();
}
class LenovoComputerBuilder implements ComputerBuilder {
private ComputerProduct computer;
public LenovoComputerBuilder(String cpu, String ram) {
computer = new ComputerProduct(cpu, ram);
}
@Override
public void setBoard(String str) {
computer.setBoard(str);
}
@Override
public void setDisplay(String str) {
computer.setDisplay(str);
}
@Override
public void setGpu(String str) {
computer.setGpu(str);
}
@Override
public ComputerProduct buildComputer() {
return computer;
}
}
/**
* 某个具体产品MacComputer的建造类
*/
class MacComputerBuilder implements ComputerBuilder {
private ComputerProduct computer;
public MacComputerBuilder(String cpu, String ram) {
computer = new ComputerProduct(cpu, ram);
}
@Override
public void setBoard(String str) {
computer.setBoard(str);
}
@Override
public void setDisplay(String str) {
computer.setDisplay(str);
}
@Override
public void setGpu(String str) {
computer.setGpu(str);
}
@Override
public ComputerProduct buildComputer() {
return computer;
}
}
简化的建造者模式
在现实开发过程中,Director角色经常会被省略。而直接使用一个Builder来进行对象的组装,这个Builder通常为链式调用,每个set方法都返回this。通过这种形式不仅去除了 Director角色,整个结构也更加简单,也能对Product对象的组装过程有更精细的控制。
package com.creational.builder;
/**
* 简化后的建造者模式,不再使用导演类
*/
public class SimpleBuilder {
public static void main(String[] args) {
ProductBuilder mProductBuilder = new ConcreteProductBuilder("Intel i7", "三星16G");
mProductBuilder.setBoard("微星")
.setDisplay("2K")
.setGpu("3080");
Product product = mProductBuilder.buildProduct();
System.out.println("Product:" + product);
}
}
/**
* 具体的产品类,通常在建造者模式中是一个较为复杂的对象,创建过程也比较复杂
* 此处进行了一些简化
*/
class Product {
private String cpu;//必须
private String ram;//必须Computer
private String board;//可选
private String display;//可选
private String gpu;//可选
public Product(String cpu, String ram) {
this.cpu = cpu;
this.ram = ram;
}
public void setBoard(String board) {
this.board = board;
}
public void setDisplay(String display) {
this.display = display;
}
public void setGpu(String gpu) {
this.gpu = gpu;
}
public String toString() {
return "Computer{" +
"cpu=" + cpu +
", ram=" + ram +
", board=" + board +
", display=" + display +
", gpu=" + gpu +
'}';
}
}
/**
* 引入抽象建造者是为了将建造的具体过程交给它的子类来实现,这样更容易扩展
*/
interface ProductBuilder {
ProductBuilder setBoard(String board);
ProductBuilder setDisplay(String display);
ProductBuilder setGpu(String gpu);
Product buildProduct();
}
class ConcreteProductBuilder implements ProductBuilder {
private Product product;
public ConcreteProductBuilder(String cpu, String ram) {
product = new Product(cpu, ram);
}
@Override
public ProductBuilder setBoard(String str) {
product.setBoard(str);
return this;
}
@Override
public ProductBuilder setDisplay(String str) {
product.setDisplay(str);
return this;
}
@Override
public ProductBuilder setGpu(String str) {
product.setGpu(str);
return this;
}
@Override
public Product buildProduct() {
System.out.println("创建产品!");
return product;
}
}
应用场景
(1) 创建复杂对象
当需要创建的对象具有复杂的内部结构,并且需要按照一定的顺序和规则进行创建时,可以使用建造者模式。建造者模式可以将对象的创建过程分解为多个步骤,并且允许在每个步骤中逐步构建对象的不同部分。
(2) 需要创建多个相似对象
当需要创建多个具有相似属性但不同配置的对象时,可以使用建造者模式。通过定义一个通用的建造者接口和多个具体的建造者实现类,可以根据需要使用不同的建造者来构建不同的对象。
(3) 需要隔离复杂对象的构建和表示
当需要将对象的构建过程与其最终表示分离开来,并且希望通过统一的方式创建不同的表示时,可以使用建造者模式。建造者模式允许在构建过程中逐步组装对象,而最终的表示可以是不同的形式,如XML、JSON等。
(4) 需要灵活地扩展和修改对象的构建过程
当需要根据需求动态地修改对象的构建过程,并且希望能够方便地扩展和添加新的构建步骤时,可以使用建造者模式。建造者模式的构建过程是逐步的,可以根据需要灵活地添加、修改或删除构建步骤。
优缺点
(1) 优点
- 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象;
- 每一个具体建造者都独立,因此可以方便地替换具体建造者或增加新的具体建造者, 用户使用不同的具体建造者即可得到不同的产品对象;
- 可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程;
- 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭”。
(2) 缺点
- 当建造者过多时,会产生很多类,难以维护。
- 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,若产品之间的差异性很大,则不适合使用该模式,因此其使用范围受到一定限制。
- 若产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
Android中的应用
使用Notification和AlertDialog控件,在构建时使用的就是建造者模式。
如Notification的构建过程如下:
PendingIntent pendingIntent = getActivity();
//获取一个Notification构造器
NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext, channelId);
//设置Notification的样式
builder.setContentTitle("标题")
.setContentText("内容")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
.setDefaults(Notification.DEFAULT_ALL)
.setLargeIcon(BitmapFactory.decodeResource(mContext.getResources(), R.mipmap.ic_launcher))
.setContentIntent(pendingIntent) //传入Intent
.setAutoCancel(true) //点击通知后自动消失
.setOngoing(true)
;
//获取构建好的Notification
Notification notification = builder.build();
//id标识该notification
notificationManager.notify(notificationId, notification);