建造者模式(Builder Pattern)
定义
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
只把复杂对象(产品product)定义出来,定义它需要的各种属性,但属性的赋值交给另一个对象(builder),且在builder内决定赋值顺序,并把产品返回(个人理解)
模式总结过程
- 有一个村子,风景秀丽,而你是村长,有一天,一个人来找你,叫你帮给他出一个游玩的方案
public class Scheme {
// 游玩时间
private String time;
// 住哪
private String hetol;
// 特色美食
private List<String> foodList;
// 游玩项目
private List<String> playList;
// getters、setters and toString
}
public class Client {
public static void main(String[] args) {
Scheme scheme = new Scheme();
scheme.setTime("一天");
scheme.setHetol("不需要");
scheme.setFoodList(Arrays.asList("红烧茄子", "家常豆腐"));
scheme.setPlayList(Arrays.asList("百年老屋", "青山绿水"));
System.out.println(scheme);
}
}
/*
方案:《游玩时间》:一天, 《住宿》:不需要, 《特色美食》:[红烧茄子, 家常豆腐], 《游玩项目》:[百年老屋, 青山绿水]
*/
- 几天后,又来了一个人,他需要的方案也一样,你又需要重复做上一个方案
public class Client {
public static void main(String[] args) {
Scheme scheme = new Scheme();
scheme.setTime("一天");
scheme.setHetol("不需要");
scheme.setFoodList(Arrays.asList("红烧茄子", "家常豆腐"));
scheme.setPlayList(Arrays.asList("百年老屋", "青山绿水"));
System.out.println(scheme);
}
}
- 这样,每来一个人,你都需要重复做重复的事情,所以,你把重复的事情抽出来
// 在Scheme类中,加构造方法
public Scheme() {
this.time = "一天";
this.hetol = "不需要";
this.foodList = Arrays.asList("红烧茄子", "家常豆腐");
this.playList = Arrays.asList("百年老屋", "青山绿水");
}
// 这样,就轻松多了
public class Client {
public static void main(String[] args) {
Scheme scheme = new Scheme();
System.out.println(scheme);
}
}
/*
方案:《游玩时间》:一天, 《住宿》:不需要, 《特色美食》:[红烧茄子, 家常豆腐], 《游玩项目》:[百年老屋, 青山绿水]
*/
- 但是,不是每一个的需求是完全一样的,有些人需要住宿,有些人只想游玩(自带食物),所以,得把设值代码再抽取出来,相当于制作好几个模板,客户需要什么就打印什么(也就是说,提供几套现成的方案,让客户去选择)
// 产品
public class Scheme {
// 游玩时间
private String time;
// 住哪
private String hetol;
// 特色美食
private List<String> foodList;
// 游玩项目
private List<String> playList;
}
// 抽象建造者
public abstract class Builder {
protected Scheme scheme = new Scheme();
public abstract void buildTime();
public abstract void buildHotel();
public abstract void buildFoodList();
public abstract void buildPlayList();
public Scheme getResult() {
buildTime();
buildHotel();
buildFoodList();
buildPlayList();
return scheme;
}
}
// 具体建造者A
public class SchemeBuilderA extends Builder {
@Override
public void buildTime() {
scheme.setTime("一天");
}
@Override
public void buildHotel() {
scheme.setHetol("不需要");
}
@Override
public void buildFoodList() {
scheme.setFoodList(Arrays.asList("红烧茄子", "家常豆腐"));
}
@Override
public void buildPlayList() {
scheme.setPlayList(Arrays.asList("百年老屋", "青山绿水"));
}
}
// 具体建造者B
public class SchemeBuilderB extends Builder {
@Override
public void buildTime() {
scheme.setTime("三天");
}
@Override
public void buildHotel() {
scheme.setHetol("农民房");
}
@Override
public void buildFoodList() {
scheme.setFoodList(Arrays.asList("走地鸡", "自养鸭"));
}
@Override
public void buildPlayList() {
scheme.setPlayList(Arrays.asList("百年老屋", "民族舞"));
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Scheme scheme1 = new SchemeBuilderA().getResult();
Scheme scheme2 = new SchemeBuilderB().getResult();
System.out.println(scheme1);
System.out.println(scheme2);
}
}
/*
方案:《游玩时间》:一天, 《住宿》:不需要, 《特色美食》:[红烧茄子, 家常豆腐], 《游玩项目》:[百年老屋, 青山绿水]
方案:《游玩时间》:三天, 《住宿》:农民房, 《特色美食》:[走地鸡, 自养鸭], 《游玩项目》:[百年老屋, 民族舞]
*/
这样,每有不同种类的需求,就增加一个具体建造者类即可
- 到这里,在我的理解中,建造者模式就完了,没有drector指导者这个角色,我觉得这是多此一举的。指导者只是把赋值过程抽象到一个新类中,完全没必要,如果建造过程有不一样的,那就与模式的定义不符合了
定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。是指同样的构建过程得到不同的表示,这里强调的是同样的构建过程!
// 指导者做的工作,顺序调用具体构建者的方法,完全可以放在抽象类中完成
buildTime();
buildHotel();
buildFoodList();
buildPlayList();
- 就算有不同的构建过程,也可以在抽象建造者与具体建造者中再加一层抽象,中间抽象层定义不同的建造过程,具体建造者再继承中间抽象层即可
角色
- 产品(product):客户端需要的对象,这个对象只声明自己有哪些属性即可(这里不讨论方法)
比如,定义机器人对象,只需要声明它有头、身、手、脚等属性即可,至于需要什么样的手,什么样的头,则交给concreteBuilder
- 建造者(builder):提供统一的规范,赋值方法与产品属性一一对应,并提供一个方法,这个方法返回赋值完成后的产品对象
比如,生产一个机器人,则必须给它一个规范,这个规范就是建造头、身、手、脚等,每一个具体建造者,都必须按照自己的需求去生产这些部件
- 具体建造者(concreteBuilder):提供具体的赋值操作,不同产品需要不同的具体建造者
有生活机器人,工作机器人,学习机器人等,不同的机器人对不同部件的要求不同
- 指导者(director):控制部件的组装过程,调用不同的具体建造者生产不同的对象
其实就是调用各个属性的赋值方法,如果放在具体建造者类中,则每个具体建造者类都有一份重复的,所以就把这个调用过程抽取出来,成为指导者类,也是为了统一建造过程(我这里是抽取到抽象建造者中,这也是我觉得director没卵用的原因)
与工厂方法的区别
- “建造者”相当于工厂方法中的“工厂”,“具体建造者”相当于工厂方法中的“具体工厂”,但是,工厂方法中的“产品类”与“具体工厂”是一一对应的,多一种产品就得多个一个产品类和一具体工厂,而建造者模式是一个“产品类”对应多个“具体建造者”,多一种产品,只需要多一个具体建造者即可
- 工厂方法针对的是产品,建造者模式针对的是产品的部件
- 对象内容,工厂方法是由对象本身决定的,而建造者模式是由具体建造者(concreteBuilder)决定的,与对象分离
- 工厂方法得到的对象属于不同的类(虽然这些类有共同的接口),而建造者模式得到的对象属于同一个类(不一定实现接口)
- 工厂方法注重得到同一接口下不同的对象,建造者模式注重得到同一个类下不同的对象表示
优点
- 解耦:创建和使用分离
- 扩展性好:加一类产品,只需要加一个具体建造者类,符合开闭原则
- 客户端只需要指定产品的类型,就可以得到对应的对象,而不需要关注具体的创建过程
缺点
- 产品内部属性发生改变时,所有建造者都要修改,成本较大
应用
- 所需要的对象比较复杂,且有不同种类时,如车是复杂对象,车又分油车、电车、混动车等,这些车的部件都是不同的,但是都是同样的构建过程