建造者模式

Post on Aug 10, 2021 by Wei Lin

介绍

​ 建造者模式(Builder Pattern)是一种创建型设计模式,它通过将一个复杂对象的构建过程分解为多个简单的步骤,并允许按照一定的顺序和规则逐步构建对象。这样可以使得同样的构建过程可以创建不同的表示。

​ 建造者模式的核心思想是将对象的构建与其表示分离开来。通过引入一个独立的建造者类,负责控制对象的构建过程,客户端只需要指定需要构建的类型和顺序,而无需关心具体的构建过程。

模式角色

深入理解设计模式(七):建造者模式

(1) 指挥者角色(Director)

​ 负责调用适当的ConcreteBuilder来组建产品。Director不直接与Product类进行操作,而是通过操作ConcreteBuilder对象实现对Product对象的参数设置。Director被用来封装程序中易变的部分,如Product对象各种不同的参数设置,在Director类中有不同的构造函数;


(2) 抽象建造者角色(Builder)

​ 引入抽象建造者是为了将建造的具体过程交给它的子类来实现,这样更容易扩展。Builder中至少会有两个抽象方法,一个用来设置产品属性(即组建产品),一个是用来返回(并建造)产品。


(3) 具体建造者角色(ConcreteBuilder)

​ 实现Builder接口,一般是两项任务:组建产品;返回组建好的产品。


(4) 产品角色(Product)

​ 一般是一个较为复杂的对象,也就是说创建对象的过程比较复杂,一般会有比较多的代码量。在本类图中,产品类是一个具体的类,而非抽象类。实际编程中,产品类可以是由一个抽象类与它的不同实现组成,也可以是由多个抽象类与他们的实现组成。

建造者模式.png

完整的建造者模式

《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);