建造者模式,也喜欢被叫做构建器,其实我们去看很多框架的源码,你会发现,都会有个build方法,比如mybatis读取配置文件返回defaultSqlSessionFactory,就是用的build()方法。
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
想了解详细过程可以移步MyBatis系列之Mybatis源码解读
一般如果一个对象非常的复杂,有非常多的成员变量的时候,就会把这些成员变量分部分来初始化并且组合最终构建出这个非常复杂的对象,比如mybatis种的Confugration对象,这个模式和模板模式很类似,但是这里强调的是构造对象,而模板方法强调的是流程。
好,我们先来看看这样一段代码,这里我假设我要构造一个游戏角色,一般我们创建角色会有一些参数要我们来填,比如名字,身高,体重,性别,有些属性是初始化好的,比如最初的属性值,最初的技能,最初的穿着等。
/**
* @Author Dark traveler
* @Note 我心净处,何处不是西天。
* @Descrption
* @E-Mail : [email protected]
* @Date : Created in 22:51 2020-4-11
*/
@Data
@Accessors(chain = true)
public class Person {
private String name;
private Double height;
private Double weight;
private String sex;
//游戏主角的基本属性
private BasePeoperty basePeoperty;
//游戏主角的基本服饰
private PersonDress personDress;
//游戏角色的基本技能
private PersonSkills personSkills;
}
/**
* @Author Dark traveler
* @Note 我心净处,何处不是西天。
* @Descrption 属性系统
* @E-Mail : [email protected]
* @Date : Created in 22:51 2020-4-11
*/
@Data
@Accessors(chain = true)
@AllArgsConstructor
public class BasePeoperty {
// 力量
private Integer power;
// 敏捷
private Integer agile;
// 智力
private Integer intelligence;
}
/**
* @Author Dark traveler
* @Note 我心净处,何处不是西天。
* @Descrption 衣服系统
* @E-Mail : [email protected]
* @Date : Created in 22:53 2020-4-11
*/
@Data
@Accessors(chain = true)
@AllArgsConstructor
public class PersonDress {
private String clothes;
private String pans;
private String shoes;
}
/**
* @Author Dark traveler
* @Note 我心净处,何处不是西天。
* @Descrption 技能系统
* @E-Mail : [email protected]
* @Date : Created in 22:54 2020-4-11
*/
@Data
@Accessors(chain = true)
@AllArgsConstructor
public class PersonSkills {
private HashMap skills;
}
/**
* @Author Dark traveler
* @Note 我心净处,何处不是西天。
* @Descrption 游戏角色构造器
* @E-Mail : [email protected]
* @Date : Created in 23:03 2020-4-11
*/
public interface PersonBuilder {
//构造基本属性
PersonBuilder buildPerperty();
//构造衣服
PersonBuilder buildDress();
//构造技能
PersonBuilder buildSkills();
//构造人物
Person build();
}
/**
* @Author Dark traveler
* @Note 我心净处,何处不是西天。
* @Descrption 原始人物构造器
* @E-Mail : [email protected]
* @Date : Created in 23:07 2020-4-11
*/
public class ComplexPersonBuilder implements PersonBuilder{
private Person person = new Person();
@Override
public PersonBuilder buildPerperty() {
BasePeoperty basePeoperty = new BasePeoperty(10, 10, 8);
person.setBasePeoperty(basePeoperty);
return this;
}
@Override
public PersonBuilder buildDress() {
PersonDress personDress = new PersonDress("普通上衣", "普通裤子", "普通鞋子");
person.setPersonDress(personDress);
return this;
}
@Override
public PersonBuilder buildSkills() {
HashMap map = new HashMap<>(5);
map.put(1,"火球术");
map.put(2,"跳斩");
map.put(3,"天雷咒");
PersonSkills personSkills = new PersonSkills(map);
person.setPersonSkills(personSkills);
return this;
}
@Override
public Person build() {
return person;
}
}
/**
* @Author Dark traveler
* @Note 我心净处,何处不是西天。
* @Descrption
* @E-Mail : [email protected]
* @Date : Created in 22:49 2020-4-11
*/
public class Client {
public static void main(String[] args) {
//人物构造器
ComplexPersonBuilder builder = new ComplexPersonBuilder();
//初始化人物构造页面
Person person = builder.buildPerperty().buildDress().buildSkills().build();
//请给你的人物输入信息
person.setName("道士").setHeight(1.75).setWeight(65.0).setSex("男");
System.out.println(person);
}
}
Person(name=道士, height=1.75, weight=65.0, sex=男, basePeoperty=BasePeoperty(power=10, agile=10, intelligence=8), personDress=PersonDress(clothes=普通上衣, pans=普通裤子, shoes=普通鞋子), personSkills=PersonSkills(skills={1=火球术, 2=跳斩, 3=天雷咒}))
像上面那样,我们的初始化人物构造器就写完了,我们玩家创建角色的时候只需要传入角色的基本信息,就能够得到一个原始角色了。
但是,有人又会说了,我干脆直接写个全参数构造器不香吗?当然,这也是可以的,但是一般的复杂对象,比如参数5,60个,它又可能需要有的值为空,有的值不为空,难道写5,60个构造函数吗?而且,你上面也没有发现可以实现有的参数为空,有的不为空啊,你看之前你说的mybatis,传的配置文件有的设置了有的没有设置也不打紧啊?
当然,上面那个例子确实没有实现这些功能,因为代码是基于业务的,所以这个原始场景是已经写好了的,而且还有一种可以自定义初始衣服和初始技能的模型SimplePersonBuiler。不过,基于上面的这种情况,我们再来对代码进行一些改变,把它变得更加像mybatis里面的构建器。
代码使用了上面属性类和衣服类,再加上下面的类。
/**
* @Author Dark traveler
* @Note 我心净处,何处不是西天。
* @Descrption
* @E-Mail : [email protected]
* @Date : Created in 23:41 2020-4-11
*/
public class GameRole {
private String name;
private Double height;
private Double weight;
private String sex;
//游戏主角的基本属性
public BasePeoperty basePeoperty;
//游戏主角的基本服饰
public PersonDress personDress;
//游戏角色的技能
public Skills personSkills;
//私有化构造函数
private GameRole(){};
//人物角色构造器
public static class GameRoleBuilder{
GameRole gameRole = new GameRole();
public GameRoleBuilder setBaseInfo(String name,Double height,Double weight,String sex){
gameRole.name = name;
gameRole.height = height;
gameRole.weight = weight;
gameRole.sex = sex;
return this;
}
public GameRoleBuilder setBasePeoperty(Integer power,Integer agile,Integer intelligence){
gameRole.basePeoperty = new BasePeoperty(power,agile,intelligence);
return this;
}
public GameRoleBuilder setPersonDress(String clothes,String pans,String shoes){
gameRole.personDress = new PersonDress(clothes,pans,shoes);
return this;
}
public GameRoleBuilder setPersonSkills(String... values){
Skills skills = new Skills();
if(StringUtils.isNoneBlank(values)){
for(String skillNum:values){
skills.add(skillNum);
}
}
gameRole.personSkills = skills;
return this;
}
public GameRole build(){return gameRole;}
}
//人物技能
private static class Skills{
public ArrayList skills = null;
public Map map = new HashMap<>();
public Skills(){
//初始化所有技能表
map.put("火球术",1);
map.put("跳斩",2);
map.put("天雷咒",3);
map.put("高级火球术",4);
map.put("高级跳斩",5);
map.put("高级天雷咒",6);
//初始化角色技能列表
skills = new ArrayList();
//初始化角色拥有技能
skills.add(1);
skills.add(2);
skills.add(3);
}
public void add(String skillName){
Integer skillNumOld = map.get(skillName);
if(null == skillNumOld){
System.out.println("添加的技能不存在,本次添加无效");
return;
}
skills.add(skillNumOld);
}
}
}
/**
* @Author Dark traveler
* @Note 我心净处,何处不是西天。
* @Descrption
* @E-Mail : [email protected]
* @Date : Created in 0:16 2020-4-12
*/
public class Client {
public static void main(String[] args) {
//等于用角色类调用它的构造器的构造方法获得构造器对象,最后调用build方法返回角色对象
GameRole role = new GameRole.GameRoleBuilder()
.setBaseInfo("法师", 165.0, 48.0, "女")
.setBasePeoperty(10, 8, 12)
.setPersonDress("华丽的衣服", "华丽的裤子", "华丽的鞋子")
.setPersonSkills("高级火球术","高级天雷咒")
.build();
System.out.println(role.hashCode());
}
}
然后我们把我们不想要的属性注释掉,再来看看能运行吗?
public class Client {
public static void main(String[] args) {
GameRole role = new GameRole.GameRoleBuilder()
.setBaseInfo("法师", 165.0, 48.0, "女")
.setBasePeoperty(10, 8, 12)
//.setPersonDress("华丽的衣服", "华丽的裤子", "华丽的鞋子")
.setPersonSkills()
.build();
System.out.println(role.hashCode());
}
}
效果也是完全ok的,是不是很像一些开源框架里面的构造器了呢?不过还是可以改进的,把这些东西都放在配置文件中读取,有兴趣的小伙伴可以自己改造一下哦。
优点
缺点