建造者设计模式(Builder)

都说设计模式源于生活实践,是我们平时写代码时不断完善总结的结晶,初学者怎么学习呢?作者这认为平时开发中碰见了,我们就结合情景学习最容易理解。作者就是如此,最近想吧okhttp框架封装下,看到其源码设计运用到了Builder模式。于是在这总结下builder的相关使用,本文采用案例驱动的方式,通俗易懂的总结一下。

一、相关介绍

1、名字

名字: Builer模式也叫作建造者模式,或者生成器模式。

2 、特点
  • 是一种对象创建型设计模式
  • 用来隐藏复合对象的创建过程,他把复合对象的创建过程加以抽象,
    通过子类继承和重载的方式动态的创建具有复合属性的对象。
3、应用场景

建造者设计模式(Builder)_第1张图片

ps: 开头我们就说了设计模式源于生活实践,重要的是得到这个设计模式实践过程,所以直接来了上面的2,3概念我们从未见过这种模式时,给出概念我们也不易接受,读者可以先忽略2,3概念,等到实践了案例在回头就理解了。

二 生活案例

建造者设计模式(Builder)_第2张图片

首先提供一个房子类(House) 和测试类(Test)

package pattern_builder;

/**
 * Create by SunnyDay on 2019/03/03
 *
 * 房子(属性:地板、墙、屋顶)
 */
public class House {

    private String floor;
    private String wall;
    private String roof;

    public String getFloor() {
        return floor;
    }

    public void setFloor(String floor) {
        this.floor = floor;
    }

    public String getWall() {
        return wall;
    }

    public void setWall(String wall) {
        this.wall = wall;
    }

    public String getRoof() {
        return roof;
    }

    public void setRoof(String roof) {
        this.roof = roof;
    }

    @Override
    public String toString() {
        return "House{" +
                "floor='" + getFloor() + '\'' +
                ", wall='" + getWall() + '\'' +
                ", roof='" + getRoof() + '\'' +
                '}';
    }

}

package pattern_builder;

/**
 * Create by SunnyDay on 2019/03/03
 * 测试类,用来测试我们的代码
 */
public class Test {
    public static void main(String[] args) {
     
   
          }
    }
1、盖房子的案例普通写法(不使用设计模式)

这种就so easy了 直接测试类中调用:

  /**
     * 普通模式(不使用设计模式时)
     * 

* 客户直接修建房子 */ private static void normalMode() { House house = new House(); house.setRoof("屋顶"); house.setWall("墙"); house.setFloor("地板"); System.out.println(house.toString()); } // syso: House{floor='地板', wall='墙', roof='屋顶'}

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

2、不完善的建造者写法

有了上文的缺点我们就可以设计类了,我们可以创建不同的类(不同的建造者),实现不同的功能(比如盖平房(平房建造者),盖公寓(公寓建造者))这样客户直接就可以获得产品。客户就做的事情少了 很符合面向对象哈,也很符合单一职责哈。我的最终目的就是得到房子。

首先创建接口(不同的建造者实现就行了)

package pattern_builder;

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

这时我们先来建造平房,创建平房建造者

package pattern_builder;

/**
 * Create by SunnyDay on 2019/03/03
 * 

* 平房修建者 */ 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());

    
    }

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

3 完善的建造者写法

有了上文的分析我们知道我们还需要个指导者,指导建造者完成工作

于是搞个指导者如下:


package pattern_builder;

/**
 * Create by SunnyDay on 2019/03/03
 * 

* 设计者 * 功能:指导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;

/**
 * Create by SunnyDay on 2019/03/03
 */
public class FlatBuilder 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;
    }
}

测试类中调用:

 private static void flat() {
        HouseBuilder builder = new FlatBuilder();//建造者
        HouseDirector director = new HouseDirector(builder);//指导者
        director.makeHouse();//指导者工作
        House house = builder.getHouse();
        System.out.println(house.toString());
    }

so easy 这样我们就学会了建造者的写法,这样我们回去看前面的概念就明白了吧。于是我们趁热打铁,来熟悉下建造者uml类图(摘抄网络)

建造者设计模式(Builder)_第3张图片

类图分析:
1、 Product 就是我们的产品类,对应我们的上文的House
2、Builder就是一个接口,builderPart()就是我们接口中定义的方法,对应上文盖房子的不同分工–墙,屋顶,地板。
3、ConcreteBuilder就是接口实现类,对应上文中我们的不同建造者,实现接口方法,再获得一个产品(house)对象,进行建造产品。
4、 建造者需要指导者指导完成工作,所以需要Director,Director中有了建造者才能使用建造者,所以通过构造获得建造者。

4 、链式调用写法

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

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

package pattern_builder;

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

package pattern_builder;

/**
 * Create by SunnyDay on 2019/03/03
 * 

* 平房修建者 */ 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; } }

于是测试类中(我们调用时顺序就自己定了,看着还简洁):

 BungalowBuilder builder = new BungalowBuilder();
        House house = builder
                .makeFloor()
                .makeRoof()
                .makeWall()
                .getHouse();
        System.out.println("链式调用:"+house.toString());

tip: 然而指导者角色并非多余,他可以把复杂的产品创建过程对外隐藏,使建造者部件和创建过程分离,各方易于扩展,降低了耦合度。且链式调用方式,不仅仅是用在这里,在我们的日常开发中,往往需要对一个对象,设置很多属性,此时就能方便的使用链式调用来提高编码速度和代码可读性。

三、小结

这样就把建造者的创建过程详细的总结了一遍,至于使用链式,使用完全的建造者,使用不完全的建造者,在开发中我们自己而定,反正设计模式也是复合解决大多数问题的一种解决思路,对于特定的问题我们也可以灵活运用吧,不要死板运用。同时也希望小伙伴们能够读完此文有所收获,如有不足之处希望大家指教哈。

源码下载连接

The end

你可能感兴趣的:(设计模式)