23种软件设计模式完整教程
介绍
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式是一种对象创建型模式。对比现实世界,类似于:
要造一台计算机主机,需要CPU、内存卡、显卡、电源等。组装过程也相对复杂,比如一些接线的处理。
要造一辆汽车,需要底盘、方向盘、发动机、座椅、轮子等等。
要造航空母舰,需要甲板、起飞装置、机库、动力系统、武器库等。
意图:
把复杂的对象创建过程进行剥离,尤其是内部部件的配置,进行独立变化和维护。
何时使用:
复杂对象的构建和实现分离,比如客户端的对话框的构建。
多参数方法的改良,如果一个对象的创建需要多个可选参数,而且参数还会变化和扩展,应用建造者模式可以让代码简洁,易与扩展与编写。
优点:
屏蔽细节:使用者不需要完全了解建造细节。
易于扩展:不同的建造者相互独立,新的产品可以创建新的建造者。
缺点:
局限性:产品组成部分必须大部分相同。
类膨胀:如果产品数量过多,也会导致建造者的数量膨胀。
使用场景:
JDK:Locale
JDK:StringBuilder
Gson:GsonBuilder
OkHttp:OkHttpClient.Builder
注意事项:
多个产品差异很大的情况下,不推荐使用建造者。
只有一种产品时,可以使用无抽象建造者,指挥者和建造者在一个类里面实现。
与抽象工厂的区别:
建造者模式返回一个完整产品,抽象工厂返回一套产品(一个产品族)。
建造者模式侧重构造和组建复杂产品,工厂模式侧重生产不同类型的产品。
代码实现
(实现一)有抽象建造者
主要角色有:
抽象建造者(Abstract Builder),声明需要的部件。
具体建造者(Concrete Builder),定义明确的部件。
指挥者(Director),指挥组装过程。
产品(Product),产品,由多个部件组成。
类图:
package com.knowledge.system.software_design_pattern.builder_pattern.myself_instance2;
public class Product {
private String partA;
private String partB;
private String partC;
public String getPartA() {
return partA;
}
public void setPartA(String partA) {
this.partA = partA;
}
public String getPartB() {
return partB;
}
public void setPartB(String partB) {
this.partB = partB;
}
public String getPartC() {
return partC;
}
public void setPartC(String partC) {
this.partC = partC;
}
@Override
public String toString() {
return "Product{" +
"partA='" + partA + '\'' +
", partB='" + partB + '\'' +
", partC='" + partC + '\'' +
'}';
}
}
抽象建造者:
package com.knowledge.system.software_design_pattern.builder_pattern.myself_instance2;
public interface IBuilder {
void buildA();
void buildB();
void buildC();
Product getResult();
}
具体建造者:
package com.knowledge.system.software_design_pattern.builder_pattern.myself_instance2;
public class BuilderA implements IBuilder {
private Product product;
public BuilderA() {
this.product = new Product();
}
@Override
public void buildA() {
product.setPartA("A1");
}
@Override
public void buildB() {
product.setPartA("A2");
}
@Override
public void buildC() {
product.setPartA("A3");
}
@Override
public Product getResult() {
return product;
}
}
指挥者:
package com.knowledge.system.software_design_pattern.builder_pattern.myself_instance2;
public abstract class Director implements IBuilder {
private IBuilder builder;
public Director(IBuilder builder) {
this.builder = builder;
}
public Product construct(){
builder.buildA();
builder.buildB();
builder.buildC();
return builder.getResult();
}
}
使用示例:
package com.knowledge.system.software_design_pattern.builder_pattern.myself_instance2;
import com.alibaba.fastjson.JSON;
public class Test {
public static void main(String[] args) {
IBuilder builder=new BuilderA();
Director director=new Director(builder);
Product product=director.construct();
System.out.println(JSON.toJSON(product));
}
}
(实现二)无抽象建造者
实现上去掉抽象接口,具体实现省略。
有无抽象创建者的选择方法:
有抽象创建者:
产品由多个部件组成,不同部件可以更换,不同部件的配置和更换为新的产品。部件之间存在复杂的依赖关系。创建过程中还依赖到外部环境的对象。
无抽象创建者:
产品由多个部件组成。部件之间的依赖关系简单。创建过程中不需要依赖外部环境。
其他使用案例:(实际研发中使用到的场景)
1. 复杂对象构建举例
一个对话框由多个部件组成,比如标题栏、内容、确定按钮、取消按钮等等。有的页面需要标题栏,有的页面不需要,有的页面需要确定按钮,有的不需要,这些都通过 Builder 来构建这个复杂的对话框。
CommonAlertDialogFragment builder = new CommonAlertDialogFragment.Builder(getActivity())
.setMessage(xxxx)
.setCancelable(true)
.setNegativeButtonText(xxx, null)
.setPositiveButtonText(xxx, null)
.setOnDismissListener(new OnDismissListener() {
@Override
public void onDismiss() {
...
}
});
CommonAlertDialogFragment dialog = builder.create()
2.多参数方法改良举例
在方法参数类型较多的时候,我们会使用方法多态,但这样会造成构造方法数量膨胀。
假设我们现在要设计一个下载器,有这么一些配置:
必选项
下载的 url 地址
下载后的文件的保存地址
可选项
是否支持多线程,默认不支持
是否支持文件覆盖,默认可以
下载失败重试次数,默认10次
如果直接用方法多态的方式,如果想要尽可能地覆盖这些条件的组合所有的情况,需要很多的重载方法,于是有这样的代码:
void downloadFile(String url, String savePath);
void downloadFile(String url, String savePath, boolean multiThread);
void downlaodFile(String url, String savePath, boolean multiThread, boolean overwrite);
void downloadFile(String url, String savePath, boolean multiThread, boolean overwrite, int retryNum);
void downloadFile(String url, String savePath, int retryNum);
...
后续增减参数类型,都会修改到这些构造方法,不符合开闭原则。
所以,我们用建造者模式来改善它:
public static class Builder {
private String url;
private String savePath;
private int retryNum = 0;
private boolean multiThread = false;
private boolean overwrite = true;
public Builder(String url, String savePath) {
this.url = url;
this.savePath = savePath;
}
public Builder addRetryNum(int retryNum) {
this.retryNum = retryNum;
return this;
}
public Builder addMultiThread(boolean multiThread) {
this.multiThread = multiThread;
return this;
}
public Builder addOverwrite(boolean overwrite) {
this.overwrite = overwrite;
return this;
}
public DownloadParams build() {
return new DownloadParams(url, savePath, retryNum, callBack, multiThread, overwrite, sync);
}
}
3. JDK:Locale
Locale aLocale = new Builder()
.setLanguage("sr")
.setScript("Latn")
.setRegion("RS")
.build();
4. JDK:StringBuilder
String str = (new StringBuilder())
.append(1)
.append(1.2f)
.append("Hello World!")
.toString();
5. Gson:GsonBuilder
GsonBuilder buider = new GsonBuilder().
.registerTypeAdapter(MediaBean.class, new MediaBeanDeserializer())
.registerTypeAdapter(UserBean.class, new UserBeanDeserializer())
.registerTypeAdapter(LiveBean.class, new LiveBeanDeserializer());
Gson gson = builder.create();
6. OkHttp:OkHttpClient.Builder
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.connectTimeout(20, TimeUnit.MILLISECONDS)
.readTimeout(60, TimeUnit.MILLISECONDS)
.writeTimeout(60, TimeUnit.MILLISECONDS)
.followRedirects(true);
.followSslRedirects(true);
.retryOnConnectionFailure(true);
OkHttpClient okHttpClient = builder.build();