前言
设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。它描述了在软件设计过程中的一些不断重复发生的问题,以及该问题的解决方案。也就是说,它是解决特定问题的一系列套路,是前辈们的代码设计经验的总结,具有一定的普遍性,可以反复使用。其目的是为了提高代码的可重用性、代码的可读性和代码的可靠性。
经过汇总的23种设计模式它是总结了面向对象设计当中最有价值的经验。对之前来讲可能是对其中部分设计模式还是相对来说熟悉的但仔细琢磨还是会有些疑问,正好在目前相对来说有更多的业余时间,可以来一次重新学习设计模式!
本篇内容关于建造者模式。包含建造者模式的设计、实现以及疑问点。
定义
建造者模式是设计模式的一种,将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。——百度百科
一般在去创建一个类的对象,都是直接使用其构造器得到。但对于复杂的产品,里面可能是包含多且复杂的属性。在代码使用这个产品对象进行直接创建就需要了解这个类所有的属性及它的运作流程,才能正常创建出自己想要的。
对于建造者模式就是把产品对象的创建委托给一个叫做建造者的家伙,客户代码就通过建造者获取,也就是定义里的构建与表示分离。
对于一个复杂产品很多内容是客户代码方并不需要了解的,而有些是需要让客户代码指定的。建造者构建这个对象的内容时,有的是默认,有的是通过客户代码传参。
传统建造者
下面用鸡公煲作为例子:
这里的客户代码需要的产品对象也就是鸡公煲,客户代码最终通过指挥者获取这个产品对象。客户代码是知道自己需要这个产品也知道其中的一些内容,这些内容由客户代码指定,但对于这个产品还有很多属性或者成分客户代码不想要去学习了解这些都是由建造者完善就好。
图上的例子相当于是四个属性内容都由客户代码调用construct()指定,也可以说是还有更多的其他隐藏属性由具体建造者默认给定
最终我们的客户代码通过指挥者的construct()方法指定自己要指定的产品的部分内容,就能得到需要的产品对象。
代码示意:
代码语言:javascript复制// 产品类
class ChickenPot{
private Qz qz;
private Dyc dyc;
private Td td;
private Jzg jzg;
//...
// getter setter
}
代码语言:javascript复制// 抽象建造者 (接口或抽象类)
interface AbstractBuilder{
// 指定各个部分
void qz(Qz qz);
void dyc(Dyc dyc);
void td(Td td);
void jzg(Jzg jzg);
//...
// 组装成品
ChickenPot build();
}
代码语言:javascript复制// 具体建造者
class Builder implements AbstractBuilder{
private Qz qz;
private Dyc dyc;
private Td td;
private Jzg jzg;
//...
// 也可以将void换成this,方便链式调用
void qz(Qz qz){
this.qz = qz;
}
void dyc(Dyc dyc){
this.dyc = dyc;
}
void td(Td td){
this.td = td;
}
void jzg(Jzg jzg){
this.jzg = jzg;
}
ChickenPot build(){
ChickenPot chicken = new ChickenPot();
chicken.setQz(this.qz);
chicken.setDyc(this.dyc);
chicken.setTd(this.td);
chicken.setJzg(this.jzg);
return chicken;
}
}
代码语言:javascript复制class Director{
private AbstractBuilder builder;
Director(AbstrctBuilder builder){
this.builder = builder;
}
public ChikenPot construct(Qz qz,Dyc dyc,Td td,Jzg jzg){
builder.qz(qz);
builder.dyc(dyc);
builder.td(td);
builder.jzg(jzg);
return builder.build();
}
}
以上就是一个简单的建造者模式的实现,主要是屏蔽客户代码直接面对构建顺序,或是指定对象全部内容复杂性。
代码语言:javascript复制// 客户代码通过指挥者获取鸡公煲对象
ChikenPot chiken = new Director().construct(xxx,xxx,xxx,xxx);
上述代码其实表达了客户对于四个配菜是加的自己带的,自己指定千张、豆芽菜等等属性值,更多的情况这些其实是建造者默认给产品指定的。真正让客户代码传入内容的字段是少的。
简化版
简化版本相当于是去掉指挥者这个环节,让客户代码直接使用建造者的方法完成获取产品对象。
在实际情况下可能并没有太复杂的对象属性的相互依赖,对于产品只用根据自己的需求构建需要指定内容的部分获得对象,不涉及依赖顺序,就可以不使用指挥者环节。
代码都是一样的,可在Idea上安装【Builder Generator】即可一键生成。
具体建造者:
客户代码直接去使用建造者提供的方法获取产品对象
代码语言:javascript复制ChickenPot chicken = new ChickenPotBuilder().withQz("xxx").withTd("xxx").build();
问题
建造者模式和工厂模式有啥区别呢?
在我看来最大的区别在于,一个是通过描述创建一个抽象类下的哪个具体类?另一个也是传些内容创建一个具体类的怎样的对象?
对于工厂模式客户代码需要了解产品的知识是要少于建造者模式的。客户代码只需要自己用什么。工厂返回一个具体类对象。
建造者模式中客户代码相对来说对自己要用的产品了解知识多一点,但不需要完全了解原理。一个具体类可产生各式各样的对象,客户代码去指导了部分内容,具体工作在建造者,最终完成对象构建。
工厂模式:
建造者模式:
客户代码为啥不直接用产品类的构造器或者Setter呢?
简单的对象当然是无所谓的,针对复杂具体类使用构造器要么排列组合写出各种构造器,要么面对全部参数的构造器,这样就要去对产品类的了解知识增加了。第二就是属性依赖,对于一个属性内容它在使用上时需要依赖当前产品对象的其他属性,我们没办法了解这个产品其中运作细节,无法创建出正确的对象。在建造者就可以完成初始化工作,以及针对你的内容的进一步修饰才能组装到产品对象当中。
总结
建造者模式是提供给客户代码获取复杂类型对象的一种新方式,比起原生的创建进行了封装,但也提供各个步骤有客户代码进行选择。通过提取抽象,建造者也能便于扩展,不同的建造者可对隐藏的细节操作以及客户传入内容的具体指定不同,为产品对象里面的组合属性提供更多的选择。