将一个复杂对象的构建与它的表示分离,使得一样的构造过程能够建立不一样的表示。java
即逐步创建由多个部件组成的对象,每次创建中各部件对外接口一致,但内部实现功能能够不同,相同的构建过程能够建立不一样的对象。android
适用于流程固定(顺序不必定固定),但建造的目标不一样的场景。例如购买电脑,不一样人对电脑有不一样的需求,办公向的,游戏向的。但都是由机箱、主板、显卡、电源等组成。又如建造房子,不一样房子结构不一样,但都有地基、墙、地板、门窗等等。又如软件开发,产品经理提出功能需求,技术领导拆分任务,指定具体程序员完成。程序员
适用于构建许多字段和嵌套对象组合成的复杂对象。安全
例如,假设要建造上图中的 House
。建简单的房子,只须要建造墙和地板,安装门窗,并建造一个屋顶。 可是,若是你想要建造一个更大,更明亮的房子,后院和其余设施(如加热系统,管道和电线)怎么办?app
最简单的解决方案是扩展基类 House
并建立一组子类来覆盖参数的全部组合。但它会产生不少的子类。 任何新参数(例如门廊样式)都须要增长新的子类。ide
还有另外一种方法不涉及子类。在基类 House
中建立一个巨型构造函数,其中包含控制 house
对象的全部可能参数。这种方法虽然确实消除了对子类的需求,但它产生了另外一个问题。函数
那就是产生了不少未使用的参数,令构造函数变得很难看。如不是全部房子都有雕像和泳池。ui
建造者模式将对象的构造过程拆分,并由专门的 Builder
对象来构造对象。this
该模式将对象构造过程拆分红一组步骤(buildWalls,buildDoor等)。要建立对象,须要在 Builder
对象中执行相应的步骤,并只调用须要的步骤便可,不须要调用全部步骤。spa
当须要构建产品的各类表示时,某些建造步骤可能须要不一样的实现。 例如,小屋的墙壁能够用木头建造,但城堡的墙壁必须用石头建造。
在这种状况下,能够建立多个不一样的 Builder
实例类,这些 Builder
以不一样的方式实现同一组构建步骤。 而后,能够在构造过程当中使用这些 Builder
来生成不一样类型的对象。
例如,假设一个 Builder
,用木头和玻璃建造一切,第二个用石头和铁建造一切,第三个用金和钻石制造一切。 经过调用相同的步骤,能够得到第一个建筑师的常规房屋,第二个建筑物的小城堡和第三个建筑物的宫殿。
为了更好的构建复杂对象,也能够建立 Director
类,将一系列建造步骤封装到其中,经过调用它来按顺序执行一系列建造步骤。它并非必须的,可是将经常使用的建造步骤封装能够更好在其余地方复用。
角色 | 类别 | 说明 |
---|---|---|
Builder | 抽象的建造者 | 接口或抽象类,将建造的具体过程交由其子类实现,便于扩展。但全部建造步骤和返回产品函数均在此声明。 |
ConcreteBuilder | 具体的建造者 | 能够有多个,实现 Builder 中的全部建造步骤。不一样建造者的实现方式能够不一样。 |
Product | 具体的产品类 | 要被建造的较为复杂的对象。 |
Director | 导演者 | 定义调用建造步骤的顺序。通常不与产品类发生依赖关系,与建造者类直接交互,一般封装可能被常用的一系列建造步骤。 |
在整个过程当中:
如上图所示,设置选项由不少个部分组成,但每次实际使用时,不须要同时启用全部部分,而是根据使用状况不一样,组合不一样部分,同时一些部分还有前后顺序,如点击事件需先添加了右箭头才能添加。此类复杂对象可使用建造者模式建立(此处仅用于演示建造者模式。 android 中实际使用时,为其添加自定义 attr 属性,在 xml 中直接设置更方便,而且实际应用建造模式时并不须要严格建立四个部分,除 Product
外其他三个部分常常混合使用)。
Product — SettingItemView
class SettingItemView(context: Context) : LinearLayout(context) {
// ... 省略构建函数
fun addTitle(title: String) {
settingTitleView.visibility = View.VISIBLE
settingTitleView.text = title
}
fun addRightArrow(isShow: Boolean) {
settingRightArrow.visibility = View.VISIBLE
settingRightArrow.visibility = if (isShow) View.VISIBLE else View.GONE
}
fun addOnClickListener(listener: View.OnClickListener) {
if (if (settingRightArrow.visibility != View.VISIBLE) {
throw IllegalStateException("add click listener should after adding right arrow")
}
setOnClickListener(listener)
}
// ... 省略设置函数
}
复制代码
Builder
interface Builder {
fun reset()
fun addTitle(title: String)
fun addRightArrow(isShow: Boolean)
fun addOnClickListener(listener: View.OnClickListener)
fun create(): SettingItemView
}
复制代码
ConcreteBuilder
class ConcreteBuilder(private val context: Context) : Builder {
private var settingItemView = SettingItemView(context)
override fun reset() {
settingItemView = SettingItemView(context)
}
override fun addTitle(title: String) {
settingItemView.addTitle(title)
}
override fun addRightArrow(isShow: Boolean) {
settingItemView.addRightArrow(isShow)
}
override fun addOnClickListener(listener: View.OnClickListener) {
settingItemView.addOnClickListener(listener)
}
override fun create(): SettingItemView {
return settingItemView
}
}
复制代码
Builder 构建时另外一种经常使用方式是将全部参数记录下来,在建立时再统一构建并检查错误。
Director
class Director(private val builder: Builder) {
fun construct() {
builder.addTitle("帐号与安全")
builder.addRightArrow(true)
builder.addOnClickListener(View.OnClickListener {
Toast.makeText(it.context, "点击", Toast.LENGTH_SHORT).show()
})
}
}
复制代码
Director 一般只负责调用 Buidler 执行操做,不直接返回产品。
调用
val builder = ConcreteBuilder(context)
val director = Director(builder)
val settingItemView = builder.create()
复制代码
将生成的 SettingItemView 对象加载到窗口中去后以下图所示:
在实际应用中,采用建造模式构建复杂对象时,一般会对建造模式进行必定简化,大体简化以下:
public class AlertDialog extends Dialog implements DialogInterface {
// ... 省略若干构造方法
protected AlertDialog(Context context, @StyleRes int themeResId) {
this(context, themeResId, true);
}
// ... 省略若干设置属性方法
public void setView(View view) {
mAlert.setView(view);
}
// 建造者
public static class Builder {
// ... 省略
public Builder setTitle(CharSequence title) {
P.mTitle = title;
return this;
}
// 建造者中提供建立目标对象的方法
public AlertDialog create() {
// Context has already been wrapped with the appropriate theme.
final AlertDialog dialog = new AlertDialog(P.mContext, 0, false);
P.apply(dialog.mAlert);
dialog.setCancelable(P.mCancelable);
if (P.mCancelable) {
dialog.setCanceledOnTouchOutside(true);
}
dialog.setOnCancelListener(P.mOnCancelListener);
dialog.setOnDismissListener(P.mOnDismissListener);
if (P.mOnKeyListener != null) {
dialog.setOnKeyListener(P.mOnKeyListener);
}
return dialog;
}
}
}
复制代码
使用建造模式:
val builder = AlertDialog.Builder(context)
builder.setTitle("问题:")
.setMessage("请问你满十八岁了吗?")
.setIcon(R.mipmap.ic_launcher_round)
.setCancelable(true)
val dialog = builder.create()
复制代码
AlertDialog 的建造者模式 AlertDialog.Builder
同时扮演了 Builder
、ConcreateBuilder
、Director
三个角色。
都属于建立模式
意图不一样
工厂模式关注的是对象总体,不关注对象的组成部分,建造者模式关注对象组成部分的建立过程。
粒度不一样
工厂模式建立的产品性质相对单一,建造者模式建立的是复合产品,由复杂部分组成,部分不一样构成的产品也不一样。
Article by Wuhb