设计模式之创建型模式---建造者模式

建造者模式概述

建造者模式是一种广泛使用的设计模式,在三方开源库和各种SDK中经常见到。建造者设计模式在四人帮的经典著作《设计模式:可复用面向对象软件基础》中被提及,它的定义为,将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式目前主要有两种,一种是经典的建造者模式,另外一种是变种的建造者模式。本文就是介绍建造者模式的两种形态的Java实现

经典的建造者模式

我们学过Java都知道,当我们需要一个对象的时候,直接new一个就行了,我们new对象的时候需要给其传递一些属性,最后就能得到一个可以使用的对象啦,比如构建一个简单的车轮子对象,我们只需要new 车轮子(属性)就行,但是当我们要构造一个车的对象时就没那么简单了,因为一辆车的这个大对象又包含了车轮子,引擎,车身等小对象,这些对象需要我们单独new出了,然后再组合到一起,最终形成一辆车。这个过程如果还是使用我们程序员手动new就会很麻烦了,所以出现了经典的建造者模式。

经典的建造者模式主要有四个参与者:

  1. 复杂对象:复杂对象就是被构造的的对象,例如一辆车:Car,实现抽象建造者接口的时候会引用这个对象,然后定义其构造的过程
  2. 抽象建造者接口: 定义创建复杂对象的各个组成部件的操作,例如CarBuilder,定义构建车的各个组成部件的操作
  3. 抽象建造者接口的具体实现 :可以定义多个,这个实现是实际构建复杂对象的地方,在这个类中会提供一个方法返回构建的复杂对象
  4. 抽象建造者接口的构造者和使用者 : 在这里面调用对应的建造者接口方法构造组装得到复杂对象。

接下来,我们使用一个造车的简单例子介绍经典的建造者模式 首先我们需要确定要构造的复杂对象,这里的复杂对象就是我们要造的车,我们定义一个类表示要造的车。

public class Car {
    private String wheel;
    private String body;
    private String engine;
    private String color;
    private String name;
    public Car() {
    }
    public String getWheel() {
        return wheel;
    }
    public void setWheel(String wheel) {
        this.wheel = wheel;
    }
    public String getBody() {
        return body;
    }
    public void setBody(String body) {
        this.body = body;
    }
    public String getEngine() {
        return engine;
    }
    public void setEngine(String engine) {
        this.engine = engine;
    }
    public String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Car{"  
                "wheel='"   wheel   '''  
                ", body='"   body   '''  
                ", engine='"   engine   '''  
                ",''  
                ", name='"   name   '''  
                '}';
    }
}

接下来我们需要定义一个建造者接口,定义造车的各个操作,即定义抽象建造者接口。代码如下所示:

public interface CarBuilder {
     // 构建轮子
     void buildWheel();
     // 构建车身
     void buildBody();
     // 构建引擎
     void buildEngine();
     // 构建车身颜色
     void buildColor();
     // 构建名字
     void buildName();
     // 返回造好的车
     Car getCar();
}

定义好造车的操作后,我们就可以准备开始造车了,我们接下来分别定义造一辆奔驰和一辆宝马,这两种车的大致部件其实都差不多,无非都是轮子,车身,引擎,颜色,名字等,但是不同的就是各个部件中包含的技术,所以我们需要定义不同的类表示当前造的车,代码如下所示: 首先是宝马类:

public class BMWBuilder implements CarBuilder{
    private final Car mCar = new Car();
    @Override
    public void buildWheel() {
        System.out.println("构建宝马的轮子");
        mCar.setWheel("宝马轮子");
    }
    @Override
    public void buildBody() {
        System.out.println("构建宝马的车身");
        mCar.setBody("宝马车身");
    }
    @Override
    public void buildEngine() {
        System.out.println("构建宝马的引擎");
        mCar.setEngine("宝马引擎");
    }
    @Override
    public void buildColor() {
        System.out.println("构建宝马的颜色");
        mCar.setColor("黑色");
    }
    @Override
    public void buildName() {
        System.out.println("构建宝马的名字");
        mCar.setName("宝马X5");
    }
    @Override
    public Car getCar() {
        return mCar;
    }
}

奔驰车类:

public class BenzBuilder implements CarBuilder{
    private final Car mCar = new Car();
    @Override
    public void buildWheel() {
        System.out.println("构建奔驰的轮子");
        mCar.setWheel("奔驰轮子");
    }
    @Override
    public void buildBody() {
        System.out.println("构建奔驰的车身");
        mCar.setBody("奔驰车身");
    }
    @Override
    public void buildEngine() {
        System.out.println("构建奔驰的引擎");
        mCar.setEngine("奔驰引擎");
    }
    @Override
    public void buildColor() {
        System.out.println("构建奔驰的颜色");
        mCar.setColor("粉色");
    }
    @Override
    public void buildName() {
        System.out.println("构建奔驰的名字");
        mCar.setName("奔驰");
    }
    @Override
    public Car getCar() {
        return mCar;
    }
}

构造完上面的对象后,我们需要提供一个类来将这些构造的操作步骤组合到一起最终形成我们要构建的复杂对象,代码如下所示:

public class Director {
    private CarBuilder carBuilder;
    public Director(CarBuilder carBuilder){
        this.carBuilder = carBuilder;
    }
    public Car buildCar(){
        carBuilder.buildName();
        carBuilder.buildColor();
        carBuilder.buildWheel();
        carBuilder.buildBody();
        carBuilder.buildEngine();
        return carBuilder == null ? null : carBuilder.getCar();
    }
}

有了Director类,使用方只需要new一个Director对象,传入想要构建的车的Builder,调用buildCar方法就可以得到一个车的对象了。使用方法如下所示:

public class Client {
    public static void main(String[] args) {
        BMWBuilder bmwBuilder = new BMWBuilder();
        Director bmwDirector = new Director(bmwBuilder);
        Car bmwCar = bmwDirector.buildCar();
        System.out.println("构建了一辆宝马: "   bmwCar.toString());
        BenzBuilder benzBuilder = new BenzBuilder();
        Director benzDirector = new Director(benzBuilder);
        Car benzCar = benzDirector.buildCar();
        System.out.println("构建了一辆奔驰: "   benzCar.toString());
        }
}

运行结果:

设计模式之创建型模式---建造者模式

建造者模式的变种

建造者模式的变种是目前用得比较多的,各大SDK初始化的时候基本都会使用建造者模式。经典的建造者模式重点在于抽象出对象创建的步骤,并通过调用不同的具体实现从而得到不同的结果。而变种的建造者模式目的在于减少对象在创建过程中引入的多个重载构造函数,可选参数以及Set方法的过度使用导致的不必要复杂性,变种的建造者模式更加适用于参数多的对象,并且这些参数中有部分参数是可选的,有部分参数是必传的。下面我们就以构建一个人(Person)的例子介绍变种的建造者模式。

public class Person {
    private final String mName; // 姓名,必填
    private final String mGender; // 性别,必填
    private final int mAge; // 年龄,可选
    private final String mPhoneNo; // 电话,可选
    private final String mWeight;// 体重,,可选
    public Person(String mName,
                  String mGender,
                  int mAge,
                  String mPhoneNo,
                  String mWeight) {
        this.mName = mName;
        this.mGender = mGender;
        this.mAge = mAge;
        this.mPhoneNo = mPhoneNo;
        this.mWeight = mWeight;
    }
    public Person(String mName,
                  String mGender,
                  int mAge,
                  String mPhoneNo) {
        this(mName, mGender, mAge, mPhoneNo, "");
    }
    public Person(String mName,
                  String mGender,
                  int mAge) {
        this(mName, mGender, mAge, "", "");
    }
}

在上面的类中,我们的姓名和性别是必选项,其他是可选项,为了能够实现必选和可选的功能,我们需要在类中定义多个重载构造函数,来满足我们的需求,或者是给提供set和get方法,让必选项的参数通过构造函数传递,但是参数很多的时候就会很复杂了,传递参数的时候特别头痛。所以出现了变种的建造者模式。代码如下:

public class PersonB {
    private final String mName; // 姓名
    private final String mGender; // 性别
    private final int mAge; // 年龄
    private final String mPhoneNo; // 电话
    private final String mWeight;// 体重
    private PersonB(PersonBuilder builder){
        mName = builder.name;
        mGender = builder.gender;
        mAge = builder.age;
        mPhoneNo = builder.phoneNo;
        mWeight = builder.weight;
    }
    public String getName() {
        return mName;
    }
    public String getGender() {
        return mGender;
    }
    public int getAge() {
        return mAge;
    }
    public String getPhoneNo() {
        return mPhoneNo;
    }
    public String getWeight() {
        return mWeight;
    }
    @Override
    public String toString() {
        return "PersonB{"  
                "mName='"   mName   '''  
                ", mGender='"   mGender   '''  
                ", mAge="   mAge  
                ", mPhoneNo='"   mPhoneNo   '''  
                ", mWeight='"   mWeight   '''  
                '}';
    }
    public static class PersonBuilder {
        private final String name; // 姓名
        private final String gender; // 性别
        private int age; // 年龄
        private  String phoneNo; // 电话
        private String weight;// 体重
        public PersonBuilder(String name,String gender){
            this.name = name;
            this.gender = gender;
        }
        public PersonBuilder age(int age){
            this.age = age;
            return this;
        }
        public PersonBuilder phone(String phoneNo){
            this.phoneNo = phoneNo;
            return this;
        }
        public PersonBuilder weight(String weight){
            this.weight = weight;
            return this;
        }
        public PersonB build(){
            return new PersonB(this);
        }
    }
}

观察上面的类我们可以发现,首先我们需要构造的对象PersonB的构造函数是私有的,意味着调用者无法直接通过实例化获取PersonB 的对象,其次PersonB类里面的属性是final的并且在构造函数中设置,对外只提供get方法,即调用者只能用,不能改。最后通过PersonBuilder的构造函数接收必选参数,其他的属性可以通过PersonBuilder提供的方法设置,其中每个方法都会返回当前的PersonBuilder对象,这样可以让我们设置参数的时候使用链式调用,如下所示:

  public PersonBuilder age(int age){
            this.age = age;
            return this;
        }

最后,我们可以看下如何使用变种的建造者模式:

public class Client {
    public static void main(String[] args) {
        // 构建一个Person
       PersonB personB =  new PersonB.PersonBuilder("职场007","male")
                .age(28)
                .phone("1234567890")
                .weight("83kg")
                .build();
        System.out.println("构建者模式构建出的对象: "   personB);
    }
}

运行结果:

设计模式之创建型模式---建造者模式

总结

以上就是建造者模式的内容,本文分别介绍了两种形式的建造者模式以及Java的实现,其中用得最多的就是变种的建造者模式,建造者模式其实有个缺点就是需要编写很多的样板代码,但是我认为尽管这样还是不影响建造者模式的优雅,并且在Android Studio也有自动化生成变种建造者模式的插件,但是我不建议使用,除非读者已经对建造者模式很了解了。不然还是得仔细的看下建造者模式的设计思想,这样在看优秀库的源码时才会感觉到事半功倍。