设计模式系列-建造者模式

146 阅读6分钟

Builder模式

经典Builder模式(GOF-Builder)

GOF-Builder模式是一种对象创造型设计模式,用来隐藏复合对象的创建过程。他把复合对象的创造过程加以抽象

核心类

  • Director:统一组建过程
  • Builder:规范产品的组建,一般由子类实现具体的组建过程
  • ConcreteBuilder:具体的Builder类,具体的创建对象的类
  • Product:抽象产品类
  • ConcreteProduct:具体的产品类

图解

这当中有两种方法

优点

  • 使用建造者模式可以使客户端不必知道产品内部组成的细节。
  • 具体的建造者类之间是相互独立的,这有利于系统的扩展。
  • 具体的建造者相互独立,因此可以对建造的过程逐步细化,而不会对其他模块产生任何影响

缺点

  • 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
  • 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。

适用场景

  • 对象的创建
  • 被创建的对象是一个具有符合属性的对象
  • 关注对象创建的各部分创建过程----不同的工厂(builder生成器)对产品属性有不同的创建方法

使用案例

盖房子

在这里插å
¥å›¾ç‰‡æè¿°

普通写法

  /**
     * 普通模式(不使用设计模式时)
     * 客户直接修建房子
     */
    private static void normalMode() {
        House house = new House();
        house.setRoof("屋顶");
        house.setWall("墙");
        house.setFloor("地板");
        System.out.println(house.toString());
    }

缺点

  • 当用户想要不同类型的房子如平房、公寓等等。 用户需要手动修改三个属性,才能把房子变成平房,或者公寓。
  • 房子还需要用户直接手动搭建。

不完善建造者写法

首先创造接口

package pattern_builder;

/**
 * 房子建造者(由实现类实现不同的建造者)
 *
 */
public interface HouseBuilder {
    void makeFloor();
    void makeWall();
    void makeRoof();
    House getHouse();//  Product
}

创建平房建造者

package pattern_builder;

/**
 * 平房修建者
 */
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 getHouse() {
        return house;
    }
}

盖房子

 /**
     * 使用builder模式案例
     */
    private static void builderModeDemo() {

        HouseBuilder builder = new BungalowBuilder();// 平房工程队
        // 让工程队开始修建
        builder.makeFloor();
        builder.makeRoof();
        builder.makeWall();
        // 用户查看 修建的房子(产品)
        House house = builder.getHouse();//其实House类应该隐藏 不能让用户直接使用(new),通过相关的builder提供实例
        System.out.println(house.toString());
    
    }

缺点

用户还需要指导工程队来修,再自己查看产品,这还是不太符合我们的要求,我们的要求是用户直接到房子,然而用户还是不能一下得到产品,还需要指导者使用户多做了工作

完善的建造者写法

创建指导者

package pattern_builder;

/**
 * 设计者
 * 功能:指导builder
 * 没有本类时,用户让builder工作并不能一部的到产品 还需要调用工程队修
 */
public class HouseDirector {
    // 提供builder对象引用(参看uml类图)
    private HouseBuilder houseBuilder;

    public HouseDirector(HouseBuilder houseBuilder) {
        this.houseBuilder = houseBuilder;
    }

    /**
     * 建造房子
     *
     * 让设计师指导完成
     * */
    public void makeHouse(){
        houseBuilder.makeWall();
        houseBuilder.makeRoof();
        houseBuilder.makeFloor();
    }
}

盖房子

/**
    * 使用builder模式
    */
   private static void builderMode() {

       HouseBuilder builder = new BungalowBuilder();// 平房工程队
       HouseDirector houseDirector = new HouseDirector(builder);//  设计指导师指导完成
       houseDirector.makeHouse();// 指导师干活
       // 用户查看 修建的房子(产品)
       House house = builder.getHouse();//其实House类应该隐藏 不能让用户直接使用(new),通过相关的builder提供实例
       System.out.println(house.toString());
       
   }

链式调用写法

通过上文观察,我们的建造者在建造房子时,屋顶,墙,地板的建造顺序是固定的,如果我们想要在测试类中更改这些方法的顺序是不可能的(指导者内固定了顺序),这时我们感觉指导者是不是多余?如果不使用指导者的话我们试试。

我们修改接口,和实现类使其具有返回值,供我们链式调用:

package pattern_builder;

/**
 * 房子建造者(由实现类实现不同的建造者)
 *
 */
public interface HouseBuilder {
    HouseBuilder makeFloor();
    HouseBuilder makeWall();
    HouseBuilder makeRoof();
    House getHouse();//  Product
}
package pattern_builder;

/**
 * 平房修建者
 */
public class BungalowBuilder implements HouseBuilder {
    private House house = new House();

    @Override
    public HouseBuilder makeFloor() {
      house.setFloor("平房地板");
      return this;
    }

    @Override
    public HouseBuilder makeWall() {
    house.setWall("平房墙");
        return this;
    }

    @Override
    public HouseBuilder makeRoof() {
     house.setRoof("平房屋顶");
        return this;
    }

    /**
     * 外界使用
     * */
    @Override
    public House getHouse() {
        return house;
    }
}

Java Builder模式

使用场景

​ 在一些类中,有的参数是必要的,有的参数是不必要的。

public class User {

    private final String firstName;     // 必传参数
    private final String lastName;      // 必传参数
    private final int age;              // 可选参数
    private final String phone;         // 可选参数
    private final String address;       // 可选参数
}

​ 如果采用传统的构造函数的话,就会需要写非常多的构造函数,就会让代码非常冗余。而且使用起来非常不方便。

    public User(String firstName, String lastName) {
        this(firstName, lastName, 0);
    }

    public User(String firstName, String lastName, int age) {
        this(firstName, lastName, age, "");
    }

    public User(String firstName, String lastName, int age, String phone) {
        this(firstName, lastName, age, phone, "");
    }

    public User(String firstName, String lastName, int age, String phone, String address) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
        this.phone = phone;
        this.address = address;
    }

​ 如果采用set方法来一个属性一个属性来设定的话,就会在低效的同时使得所有属性都是可变的了。

public class User {

    private String firstName;     // 必传参数
    private String lastName;      // 必传参数
    private int age;              // 可选参数
    private String phone;         // 可选参数
    private String address;       // 可选参数

    public User() {
    }

    public String setFirstName() {
        return firstName;
    }

    public String setLastName() {
        return lastName;
    }
	
    ......
}

基于上述原因,就有人提出了Builder模式来解决上述问题。

使用示例

​ 由于这个User对象是不可变的,所以毫无疑问我们给他的所有属性都加了final修饰,当然如果没有不可变的需求也是可以不加的,然后在User类中定义一个内部类Builder,这个Builder内部类中的属性要和User中的相同,并且必须有的属性要用final修饰,防止这些属性没有被赋值,其他非必须的属性不能用final,因为如果加了final,就必须对其进行初始化,这样这些非必须的属性又变成必须的。然后内部类中定义了一个构造方法,传入必须有的属性。其他非必须的属性都通过方法设置,每个方法都返回Builder对象自身。最后定义了一个build方法,将Builder对象传入User的私有构造方法,最终返回一个对象。

public class User {

    private final String firstName;     // 必传参数
    private final String lastName;      // 必传参数
    private final int age;              // 可选参数
    private final String phone;         // 可选参数
    private final String address;       // 可选参数

    private User(UserBuilder builder) {
        this.firstName = builder.firstName;
        this.lastName = builder.lastName;
        this.age = builder.age;
        this.phone = builder.phone;
        this.address = builder.address;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public int getAge() {
        return age;
    }

    public String getPhone() {
        return phone;
    }

    public String getAddress() {
        return address;
    }

    public static class UserBuilder {
        private final String firstName;
        private final String lastName;
        private int age;
        private String phone;
        private String address;

        public UserBuilder(String firstName, String lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        }

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

        public UserBuilder phone(String phone) {
            this.phone = phone;
            return this;
        }

        public UserBuilder address(String address) {
            this.address = address;
            return this;
        }

        public User build() {
            return new User(this);
        }
    }
}

创建一个对象

User user=new User.UserBuilder("王", "小二")
                .age(20)
                .phone("123456789")
                .address("亚特兰蒂斯大陆")
                .build();

参考网址

www.jianshu.com/p/e2a2fe355…

www.jianshu.com/p/afe090b2e…

blog.csdn.net/qq_38350635…