设计模式 - Builder模式
Posted demonyan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式 - Builder模式相关的知识,希望对你有一定的参考价值。
建造者模式(Builder Pattern)又叫生成器模式,属于对象创建类模式。建造者模式是一个运用比较广泛的设计模式,经常可以在开源库中看到它的身影,自己重构代码时也可以灵活运用它。
基本概念
建造者模式将一个复杂对象的构建与其表示分离,使得同样的构建过程可以产生不同的表示,其通用类图如下所示:
由上图可知,建造者模式包含以下 4 种角色:
1. Product 产品类
表示被建造者创建的复杂产品对象。
2. Builder 抽象建造类
为创建 Product 对象的各个部件指定抽象接口。
3. ConcreteBuilder 具体建造类
实现抽象构建类的抽象方法,返回一个构建好的对象。
4. Director 导演类
构造一个使用 Builder 接口的对象。
建造者模式的通用源码如下所示:
/**
* 产品类
*/
public class Product
public void doSomething()
// 产品业务逻辑
/**
* 抽象建造类
*/
public abstract class Builder
// 设置产品的不同部分
public abstract void setPart();
// 建造产品
public abstract Product buildProduct();
/**
* 具体建造类
*/
public class ConcreteProduct extends Builder
private Product product = new Product();
@Override
public void setPart()
// 产品类内的逻辑处理
@Override
public Product buildProduct()
return product;
/**
* 导演类
*/
public class Director
private Builder builder = new ConcreteProduct();
// 建造一个产品
public Product getProductA()
// 设置不同的零件
builder.setPart();
return builder.buildProduct();
建造者模式的优点
1. 封装性:使用建造者模式可以使客户端不需要知道产品内部的组成细节。
2. 开闭原则:建造者独立,容易扩展。
3. 便于对构造过程进行更精细的控制:由于具体的建造者是独立的,因此可以对建造过程逐步细化,而不会对其他模块产生影响。
建造者模式使用场景
1. 相同的方法,不同的执行顺序,产生不同的事件结果。
2. 对象创建过程复杂,初始化参数很多或者调用顺序不同会产生不同的效能。
建造者模式应用示例
在创建对象时,静态工厂方法和构造器有一个共同的局限性:他们都不能很好的扩展到大量的可选参数。以下借用 Effective Java 中的示例。用一个类表示包装食品外面显示的营养成分标签,这些标签有一些域是必需的,比如含量以及卡路里;还有一些域是可选的,比如总脂肪量,饱和脂肪量,转化脂肪,胆固醇等。
对于这样的类,一般习惯采用重叠构造器(telescoping constructor)模式,提供一个只有必要参数的构造器,提供第二个构造器包含一个可选参数,第三个构造器包含两个可选参数,依此类推。以下是示例代码,它只显示 3 个可选参数:
public class NutritionFacts
private final int servingSize; // (mL) required
private final int servings; // (per container) required
private final int calories; // optional
private final int fat; // (g) optional
private final int sodium; // (mg) optional
public NutritionFacts(int servingSize, int servings)
this(servingSize, servings, 0);
// 一个可选参数
public NutritionFacts(int servingSize, int servings, int calories)
this(servingSize, servings, calories, 0);
// 二个可选参数
public NutritionFacts(int servingSize, int servings,
int calories, int fat)
this(servingSize, servings, calories, fat, 0);
// 三个可选参数
public NutritionFacts(int servingSize, int servings,
int calories, int fat, int sodium)
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = fat;
this.sodium = sodium;
如果仅仅只有 5 个参数还不算太糟,但随着参数数目的增加,重叠构造器也会增加,导致客户端代码会很难编写,并且代码也难以阅读。如果客户端不小心颠倒了其中两个参数的顺序,在编译器不会报错的情况下,程序会在运行时出现错误行为。
这种情况下,可以使用建造者模式代替重叠构造器。客户端不直接生成产品对象,而是先得到一个 builder 对象,然后在 builder 对象上调用类似于 setter 的方法设置每个相关的可选参数。最后客户端调用无参的 build 方法来生成产品对象。以下是示例代码:
public class NutritionFacts
private final int servingSize; // (mL) required
private final int servings; // (per container) required
private final int calories; // optional
private final int fat; // (g) optional
private final int sodium; // (mg) optional
public static class Builder
// Required parameters
private final int servingSize;
private final int servings;
// Optional parameters
private int calories = 0;
private int fat = 0;
private int sodium = 0;
public Builder(int servingSize, int servings)
this.servingSize = servingSize;
this.servings = servings;
public Builder calories(int val)
this.calories = val;
return this;
public Builder fat(int val)
this.fat = val;
return this;
public Builder sodium(int val)
this.sodium = val;
return this;
public NutritionFacts build()
return new NutritionFacts(this);
public NutritionFacts(Builder builder)
this.servingSize = builder.servingSize;
this.servings = builder.servings;
this.calories = builder.calories;
this.fat = builder.fat;
this.sodium = builder.sodium;
在建造者模式通用类图的基础上去掉了抽象建造类,且将具体建造类至于产品类内部。
很明显,客户端代码更容易编写,也更容易阅读。下面是客户端代码:
NutritionFacts cocaCola = new NutritionFacts.Builder(100, 200).calories(300)
.fat(400).sodium(500).build();
由此可知,Builder 模式十分灵活,builder 对象利用单独的方法来设置每个参数,同时还可以对参数增加约束条件。builder 的参数可以在创建对象期间灵活调整,也可以随着不同的对象而变化。
android 应用程序中也经常用到建造者模式,其中 AlertDialog 对话框的创建最为典型。由于 AlertDialog 有很多组成部分,比如 icon, message, title, button, view 等等,所以用重叠构造器也会遇到同样的问题。通过分析源码可知,AlertDialog 类也存在一个 Builder 内部类,结构和上面示例一样一样的。以下是 AlertDialog 的一个使用场景:
new AlertDialog.Builder(MainActivity.this)
.setTitle("Warning")
.setMessage("Wifi Disable!")
.setIcon(R.drawable.wifi_disable)
.setPositiveButton("OK", new DialogInterface.OnClickListener()
@Override
public void onClick(DialogInterface dialog, int which)
dialog.dismiss();
)
.setNegativeButton("Cancel", new DialogInterface.OnClickListener()
@Override
public void onClick(DialogInterface dialog, int which)
dialog.dismiss();
)
.show();
上述链式调用如果太长其实也影响代码阅读,最好做适当的封装,并且分步初始化。
此外,StrictMode 类中 ThreadPolicy 对象以及 VmPolicy 对象的创建,也采用同样的方式。
与工厂方法模式区别
建造者模式和工厂方法模式都属于对象创建类模式,但它们之间有比较明显的区别:在工厂方法模式里,关注的是一个产品整体,无须关心产品的各部分是如何创建出来的;而在建造者模式中,需要关注具体产品各个部件的创建以及装配顺序。因此,可以说工厂方法模式是一种粗线条的对象创建模式,而建造者模式关注产品各部分的创建过程,是一种细线条的对象创建模式。
参考资料
1. 设计模式之禅
2. Effective Java(第二版)
以上是关于设计模式 - Builder模式的主要内容,如果未能解决你的问题,请参考以下文章