都说设计模式源于生活实践,是我们平时写代码时不断完善总结的结晶,初学者怎么学习呢?作者这认为平时开发中碰见了,我们就结合情景学习最容易理解。作者就是如此,最近想吧okhttp框架封装下,看到其源码设计运用到了Builder模式。于是在这总结下builder的相关使用,本文采用案例驱动的方式,通俗易懂的总结一下。
名字: Builer模式也叫作建造者模式,或者生成器模式。
ps: 开头我们就说了设计模式源于生活实践,重要的是得到这个设计模式实践过程,所以直接来了上面的2,3概念我们从未见过这种模式时,给出概念我们也不易接受,读者可以先忽略2,3概念,等到实践了案例在回头就理解了。
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) {
}
}
这种就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 房子还需要用户直接手动搭建。
有了上文的缺点我们就可以设计类了,我们可以创建不同的类(不同的建造者),实现不同的功能(比如盖平房(平房建造者),盖公寓(公寓建造者))这样客户直接就可以获得产品。客户就做的事情少了 很符合面向对象哈,也很符合单一职责哈。我的最终目的就是得到房子。
首先创建接口(不同的建造者实现就行了)
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());
}
缺点:
用户还需要指导工程队来修,再自己查看产品,这还是不太符合我们的要求,我们的要求是用户直接到房子,然而用户还是不能一下得到产品,还需要指导者使用户多做了工作,还不太符合我们的要求哈。
有了上文的分析我们知道我们还需要个指导者,指导建造者完成工作
于是搞个指导者如下:
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类图(摘抄网络)
类图分析:
1、 Product 就是我们的产品类,对应我们的上文的House
2、Builder就是一个接口,builderPart()就是我们接口中定义的方法,对应上文盖房子的不同分工–墙,屋顶,地板。
3、ConcreteBuilder就是接口实现类,对应上文中我们的不同建造者,实现接口方法,再获得一个产品(house)对象,进行建造产品。
4、 建造者需要指导者指导完成工作,所以需要Director,Director中有了建造者才能使用建造者,所以通过构造获得建造者。
通过上文观察,我们的建造者在建造房子时,屋顶,墙,地板的建造顺序是固定的,如果我们想要在测试类中更改这些方法的顺序是不可能的(指导者内固定了顺序),这时我们感觉指导者是不是多余?如果不使用指导者的话我们试试。
我们修改接口,和实现类使其具有返回值,供我们链式调用:
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: 然而指导者角色并非多余,他可以把复杂的产品创建过程对外隐藏,使建造者部件和创建过程分离,各方易于扩展,降低了耦合度。且链式调用方式,不仅仅是用在这里,在我们的日常开发中,往往需要对一个对象,设置很多属性,此时就能方便的使用链式调用来提高编码速度和代码可读性。
这样就把建造者的创建过程详细的总结了一遍,至于使用链式,使用完全的建造者,使用不完全的建造者,在开发中我们自己而定,反正设计模式也是复合解决大多数问题的一种解决思路,对于特定的问题我们也可以灵活运用吧,不要死板运用。同时也希望小伙伴们能够读完此文有所收获,如有不足之处希望大家指教哈。
源码下载连接