建造者模式是一种常用的设计模式,你可能每时每刻都在用只是你没有察觉到。比如我们常用的lambok内的@Builder
注解,就是使用了建造者模式,业务代码中只要调用.builder
方法,然后设置属性,最后调用.build
就得到了最终的目标对象。这篇文章首先讲建造者模式内的几种角色,以及他们之间是如何配合完成一个对象的创建,最后会将lambok实现建造者模式的代码写出来。这种代码其实很简单,在日常开发过程中都是可以使用的。
1. 建造者模式
在建造者模式中有几个重要的角色,一个是指挥者、一个是建造者,最后就是具体需要建造的对象。
指挥者:就是指挥建造者来实现一系列的流程建造一个对象。
建造者:通过指挥者的调度,依次执行逻辑代码,最终一步一步的将产品建造出来。
产品对象:也就是需要建造的对象。
概念大概知道了,下面通过代码体现出来。
1.1 产品对象
这里以炒菜为例,现在客户分别需要两个当地特色菜品,其中醋溜土豆丝只有厨师张三会做,油焖茄子只有李四会做。客户来了以后和点菜的服务员说了,他就想吃这两样特色菜,此时服务员将菜单交给大厨师傅,师傅开始指挥张三和李四开始做菜。
public class Product{
}
//醋溜土豆丝
public class Potato extends Product{
private Integer salt;
private Integer oil;
public void setSalt(Integer salt){
this.salt = salt;
}
public void setOil(Integer oil){
this.oil = oil;
}
}
//油焖茄子
public class Eggplant extends Product{
private Integer salt;
private Integer oil;
public void setSalt(Integer salt){
this.salt = salt;
}
public void setOil(Integer oil){
this.oil = oil;
}
}
需要创建的两个产品有了,但是还要具体的建造者。
1.2 建造者
public class Builder{
void addSalt();
void addOil();
Product get();
}
//醋溜土豆丝建造者
public class PotatoBuilder implements Builder{
private Potato potato;
public void addSalt(){
potato.addSalt(1);
}
public void addOil(){
potato.addOil(20);
}
}
//油焖茄子建造者
public class EggplantBuilder implements Builder{
private Eggplant eggplant;
public void addSalt(){
potato.addSalt(3);
}
public void addOil(){
potato.addOil(50);
}
}
1.3 指挥者
建造者有了,需要一个指挥者,这里的指挥者就相当于张三和李四的大厨师傅。
//指挥者
public class Director{
public void doit(Builder builder){
//指挥做菜
builder.addSalt();
buiilder.addOil();
}
}
传上菜单,开始做菜。
public class Main{
public static void main(String[] args){
Builder potatoBuilder = new PotatoBuilder();
Builder eggplantBuilder = new EggplantBuilder();
Director director = new Director();
//醋溜土豆丝出锅
director.doit(potatoBuilder);
Potato potato= (Potato)potatoBuilder.get();
//油焖茄子出锅
director.doit(eggplantBuilder);
Eggplant eggplant = (Potato)EggplantBuilder.get();
}
}
整个建造者实现的过程是如上代码,加上上面对建造者模式的说明,应该不难理解。三个角色在其中各司其职,最终完成产品的建造过程。体现到UML图就是如下:
一般在什么时候能用到这个呢,之前在说mybatis源码的时候,就用到建造者模式,这个说起来就点多了,为了更好的说明清楚,来说一个实际开发中能够经常用到的。
前端传入的参数一般都是用DTO来接收,但是最终入库的时候是需要转换成对应的实体对象,恰恰DTO中接收参数的字段和实际实体类的字段名有出入,这个转换过程就可以用到建造者模式。因为直接在业务代码中写冗长的转换代码实在是不太妥。
在实体类中创建一个建造者内部类,然后将DTO传入给建造者,有建造者完成构建并给出对应的实体对象。
2. 建造者的实际运用
接上面的DTO的介绍来写一个对象转换中使用到建造者模式。
public class User{
private String userName;
private String mobile;
private Integer age;
private String nickName;
//getter……
//setter……
//生成建造者对象
public Builder builder(){
return new Builder();
}
//建造者
public class Builder{
//建造者构建目标对象user
public User build(UserDTO userDto){
User user = new User();
user.setUserName(userDto.getName());
user.serMobile(userDto.getPhone());
user.setAge(userDto.getAge());
user.setNickName(userDto.getNickName());
return user;
}
}
}
此时业务代码中只要一行代码就可以。如下:
User user = User.builder().build(userDto);
很简单的一种使用方式,也是很有高效的,业务代码中用到这种封装的地方很多,就避免了重复代码的出现。也许你可以说其实可以直接在业务代码中抽离一个公共的方法完成,但是想比较而言,这种方式不好。如果抽离方法可能会比较麻烦,需要写在公共的包内,然后公共的包又要引入dao层对应的包,很容易出现循环引用,设计上也不是很合理。如果直接由内部建造者模式实现,减少的麻烦不止一点。
另外要说明的一点就是,实际在开发中转换用的比较多的是dozer,保证两个类的字段名相同,然后用dozer来实现转换。又或者通过配置文件来配置两个类字段的对应关系。但是dozer这种使用方式是很慢的,相对于这种建造者模式来说。可以通过debug断点的方式测试一下。