建造者模式(Builder),将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
使用建造者模式的情景:
主要是用于创建一些复杂的对象,这些对象内部构建间的建造顺序通常是稳定的,但对象内部的构建通常面临着复杂的变化。
建造者模式的好处就是使得建造代码与表示代码分离,由于建造者隐藏了该产品是如何组装的,所以若需要改变一个产品的内部表示,只需要再定义一个具体的建造者就可以了。
举例:
当玩一个角色扮演类的游戏时,通常都需要创建一个角色,可以选择男性或女性,生成人物的打扮和武器也会根据选择的性别发生变化,但是男性和女性单独创建的流程都是固定不变的,即所有男性的打扮、武器都是固定的,这个生成角色的过程就可以用建造者模式实现。代码如下:
// Builder接口,用来定义构造对象需要的全部流程,并且有一个返回组装好的对象的方法
public interface IBuilder {
public void buildSex();
public void buildCloth();
public void buildWeapon();
public Person createPerson();
}
// 构建男性角色的类
public class BuildMan implements IBuilder {
Person person;
public BuildMan() {
this.person = new Person();
}
@Override
public void buildSex() {
person.setSex("男");
System.out.println("已选择男性角色进行创建");
}
@Override
public void buildCloth() {
person.setCloth("男性服饰");
System.out.println("创建男性角色服装");
}
@Override
public void buildWeapon() {
person.setWeapon("男性武器");
System.out.println("创建男性角色武器");
}
@Override
public Person createPerson() {
return this.person;
}
}
// 构建女性角色的类
public class BuildWomen implements IBuilder {
Person person;
public BuildWomen() {
this.person = new Person();
}
@Override
public void buildSex() {
person.setSex("女");
System.out.println("已选择女性角色进行创建");
}
@Override
public void buildCloth() {
person.setCloth("女性服装");
System.out.println("创建女性角色服装");
}
@Override
public void buildWeapon() {
person.setWeapon("女性武器");
System.out.println("创建女性角色武器");
}
@Override
public Person createPerson() {
return this.person;
}
}
// 角色类
public class Person {
private String sex;
private String cloth;
private String weapon;
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getCloth() {
return cloth;
}
public void setCloth(String cloth) {
this.cloth = cloth;
}
public String getWeapon() {
return weapon;
}
public void setWeapon(String weapon) {
this.weapon = weapon;
}
public String show(){
return "角色性别:" + sex + " 角色服饰:" + cloth + " 角色武器:" + weapon;
}
}
是不是有的朋友会问,现在不是可以直接创建对象了吗?代码如下:
public class Test {
public static void main(String[] args) {
// 创建一个男性角色
BuildMan man = new BuildMan();
man.buildSex();
man.buildCloth();
man.buildWeapon();
Person person1 = man.createPerson();
System.out.println(person1.show());
System.out.println("============");
BuildWomen woman = new BuildWomen();
woman.buildSex();
woman.buildCloth();
woman.buildWeapon();
Person person2 = woman.createPerson();
System.out.println(person2.show());
}
}
仔细观察,所有生成角色的业务逻辑是一样的,必须要设置性别、服装和武器,少设置一个就会不符合业务逻辑。建造者模式就是把构造的流程放到一个专门指挥角色生成的对象中,由他负责去生成相应的属性,这样可以避免忘记生成某个属性,导致错误出现。下面添加这个指挥者类:
public class BuildDerector {
public void BuildPerson(IBuilder builder) {
builder.buildSex();
builder.buildCloth();
builder.buildWeapon();
}
}
指挥者类只负责加工每个对象,不负责其他的业务逻辑。
下面看主程序类:
public class Test {
public static void main(String[] args) {
// 创建一个男性角色
BuildDerector buildDerector = new BuildDerector();
BuildMan man = new BuildMan();
buildDerector.BuildPerson(man);
Person person1 = man.createPerson();
System.out.println(person1.show());
System.out.println("============");
BuildWomen woman = new BuildWomen();
buildDerector.BuildPerson(woman);
Person person2 = woman.createPerson();
System.out.println(person2.show());
}
}
结果如下:
已选择男性角色进行创建
创建男性角色服装
创建男性角色武器
角色性别:男 角色服饰:男性服饰 角色武器:男性武器
============
已选择女性角色进行创建
创建女性角色服装
创建女性角色武器
角色性别:女 角色服饰:女性服装 角色武器:女性武器
刚开始看的时候,我觉得建造者模式和模板模式十分相似,都是按照一个模板去实现业务逻辑,下面拿出来模板模式的主要代码:
// 体育运动模板类
public abstract class Sports {
public void start(){
System.out.println("比赛开始,开始计时。");
}
public void end(){
System.out.println("时间到,比赛结束。");
}
public abstract void process();
// 使用final修饰,防止修改模板
public final void play(){
start();
process();
end();
}
}
public class Basketball extends Sports {
@Override
public void process() {
System.out.println("开始进行篮球比赛");
}
}
public class Test {
public static void main(String[] args) {
Sports sports = new Basketball();
sports.play();
System.out.println("=== another sport game ===");
sports = new Football();
sports.play();
}
}
可以看出来,模板模式执行的业务逻辑都定义在了父类当中,所以每个子类都需要按照这个流程去执行。而建造者模式,虽然本次例子中,都是由同一个指挥者去执行,但指挥者类可以扩展出来子类,根据不同子类的建造者,采用不同的策略去执行,并且能保证同一个建造者实例化的对象执行的业务逻辑都相同,但是不同类的建造者实例化的对象执行的业务逻辑可以不相同。
模板模式的使用情景:
每个子类的业务逻辑基本一样;很多代码可以进行复用;进行系统维护升级的情况下,可以采用模板模式进行向下兼容,通过增加钩子判断相应的版本以及执行的流程。
建造者模式的使用场景:
很多子类都需要执行相同的方法,但是存在顺序、逻辑不完全相同的情况