AnthonyZero's Bolg

设计模式:建造者模式

前言

在开发中,经常用到builder设计模式,但感觉最常见的应用场景就是构造对象参数较多的时候,最近又用到了建造者模式,故在此梳理总结一下。

定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。首先这是一个复杂的对象,也就是说对象的创建过程比较复杂,可能需要N多个组件来完成整个对象的创建,这个时候为了符合“单一职责”,我们将对象的构建过程分离出来,通过建造者来完成对象组件的创建,再通过一个指挥者类完成组装生成一个完整的产品。

使用场景

  1. 如果一个对象有非常复杂的内部结构「这些产品通常有很多属性」,那么使用建造者模式
  2. 如果想把复杂对象的创建和使用分离开来,那么使用建造者模式使用相同的创建步骤可以创建不同的产品

UML图

Alt text

说明:

  • Builder: 抽象类,用于规范子类建造者的创建产品对象的各个组件
  • ConcreteBuilder: 具体建造者,实现抽象类定义的所有方法。
  • Product: 产品类,一般是一个比较复杂的对象
  • Director: 负责安排已有模块的顺序,然后告诉Builder开始建造。

经典写法

  1. 产品类:房子对象
public class House {
    // 打地基
    private String foundation ;
    // 盖框架
    private String frame ;
    // 浇筑
    private String pouring ;
    ... 省略 setter 和 getter 
}
  1. 抽象建造者「包工头」–Builder
public abstract class HouseBuilder {
    private House house = new House();
    // 打地基
    public abstract void doFoundation();
    // 盖框架
    public abstract void doFrame() ;
    // 浇灌
    public abstract void dpPouring() ;
    // 房子建成
    public House createHouse() {
        return house;
    }
}
  1. 具体建造者「工人」–盖平房
public class PingFangBuilder extends  HouseBuilder {
    @Override
    public void doFoundation() {
        house.setFoundation("盖平房的地基");
    }

    @Override
    public void doFrame() {
        house.setFrame("盖平房的框架");
    }

    @Override
    public void dpPouring() {
        house.setPouring("盖平房的浇灌");
    }
}
  1. 具体建造者「工人」–盖楼房
public class LouFangBuilder extends  HouseBuilder {
    @Override
    public void doFoundation() {
        house.setFoundation("盖楼房的地基");
    }

    @Override
    public void doFrame() {
        house.setFrame("盖楼房的框架");
    }

    @Override
    public void dpPouring() {
        house.setPouring("盖楼房的浇灌");
    }
}
  1. 指挥者
public class HouseDirector {
    // 指挥包工头
    public House buildHouse(HouseBuilder houseBuilder){
        houseBuilder.doFoundation();
        houseBuilder.doFrame();
        houseBuilder.dpPouring();
        return houseBuilder.createHouse();
    }
}
  1. 测试 – 盖楼房
public class Client {

    public static void main(String[] args) {
        HouseBuilder builder = new LouFangBuilder();
        HouseDirector director = new HouseDirector();
        House house = director.buildHouse(builder);
    }
}

日常写法-内部Builder

上面为builder模式经典的写法,很少写的那么标准或者那么复杂,大多数情况下builder模式主要是为了防止在构建对象时传递太多的参数。

public class Person {

    private String name;
    private int age;
    private String sex;
    private String country;

    private Person(String name, int age, String sex, String country) {
        this.name = name;
        this.age = age;
        this.sex = sex;
        this.country = country;
    }

    public static class Builder {
        private String name;
        private int age;
        private String sex;
        private String country;

        public Builder setName(String name) {
            this.name = name;
            return this;
        }

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

        public Builder setSex(String sex) {
            this.sex = sex;
            return this;
        }

        public Builder setCountry(String country) {
            this.country = country;
            return this;
        }

        public Person builder() {
            return new Person(name, age, sex, country);
        }
    }
}

//使用
public class Client {
    public static void main(String[] args) {
        Person person = new Person.Builder()
                    .setAge(18)
                    .setCountry("中国")
                    .setName("张三")
                    .setSex("男")
                    .builder();
    }
}

这种模式是建造者模式的简化变种,将Builder类置于Product类的内部,通过builder()方法完成产品的组装。虽然代码简化只有一个类,但依旧符合建造者模式的思想。

总结

建造者模式将一个复杂对象的构建与它的表示分离,使得客户端通过创建不同的建造者即可创建出不同的对象。且通过分离的方式,将复杂的创建过程分离出来,符合“单一职责”规范。新的不同对象可以通过新增建造者类生成,符合“开闭原则”。但建造者类的使用范围也有一定的限制,建造者模式创建的产品都有一定的相似性,如果所创建的产品差异性很大,则不适合建造者模式。