说明:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
使用场景
- 相同的方法,不同的执行顺序,产生不同的事件结果时;
- 多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时;
- 产品类非常复杂,或者产品类中的调用顺序不同产生了不同的作用,这时使用建造者模式非常合适;
- 当初始化一个对象特别复杂,如参数多,且很多参数都具有默认值时。
简单实现
说明:举个计算机的简单组装例子。分为构建主板、设置操作系统、设置显示器 3 部分。
计算机抽象类,即 Product 对象
public abstract class Computer {
protected String mBoard;
protected String mDisplay;
protected String mOS;
public Computer() {
}
// 设置主板
public void setBoard(String board) {
mBoard = board;
}
// 设置显示器
public void setDisplay(String display) {
mDisplay = display;
}
//设置操作系统
public abstract void setOS();
@Override
public String toString() {
return "Computer{" +
"mBoard='" + mBoard + '\'' +
", mDisplay='" + mDisplay + '\'' +
", mOS='" + mOS + '\'' +
'}';
}
}
具体的Computer类,Macbook
public class Macbook extends Computer {
public Macbook() {
}
@Override
public void setOS() {
mOS = "Mac OS X 10.10";
}
}
抽象builder类
public abstract class Builder {
// 设置主板
public abstract void buildBoard(String board);
// 设置显示器
public abstract void buildDisplay(String display);
// 设置操作系统
public abstract void buildOS();
// 创建 Computer
public abstract Computer create();
}
具体的builder类,MacbookBuilder
public class MacbookBuilder extends Builder {
private Computer mComputer = new Macbook();
@Override
public void buildBoard(String board) {
mComputer.setBoard(board);
}
@Override
public void buildDisplay(String display) {
mComputer.setDisplay(display);
}
@Override
public void buildOS() {
mComputer.setOS();
}
@Override
public Computer create() {
return mComputer;
}
}
负责构造 Computer
public class Director {
Builder mBuilder;
public Director(Builder builder) {
mBuilder = builder;
}
/**
* 构建对象
*/
public void construct(String board, String display) {
mBuilder.buildBoard(board);
mBuilder.buildDisplay(display);
mBuilder.buildOS();
}
}
测试类
public class Test {
public static void main(String[] args) {
// 构建器
Builder builder = new MacbookBuilder();
// Director
Director pcDirector = new Director(builder);
// 封装构建过程,4核、内存2GB、Mac系统
pcDirector.construct("Intel 主板", "Retina 显示器");
// 构建计算机,输出相关信息
System.out.println("Computer Info : " + builder.create().toString());
}
}
输出结果:
Computer Info : Computer [mBoard=Intel 主板, mDisplay=Retina 显示器, mOS=Mac OS X 10.10]
上面示例中,通过具体的 MacbookBuilder 来构建 Macbook 对象,二 Director 封装了构建复杂产品对象的过程,对外隐藏构建细节。Builder 与 Director 一起将一个附在对象的构建与它的表示分离,使得同样的构建过程可以创建不同的对象。
在实际开发过程中,Director 角色经常会被省略。而直接使用一个 Builder 来进行对象的组装,这个 Builder 通常为链式调用,对应的每个 setter 方法返回自身,即 return this。其大致代码如下:
new TestBuilder().setA("A").setB("B").create();
Android 源码中的 Builder 模式
最常见的 AlertDialog.Builder。详情请自行搜索。
开发实际使用
此时参考别人写好的 CustomerPopupWindow 类,如下
public class CustomPopupWindow extends PopupWindow {
private View mContentView;
private View mParentView;
private CustomPopupWindowListener mListener;
private boolean isOutsideTouch;
private boolean isFocus;
private Drawable mBackgroundDrawable;
private int mAnimationStyle;
private boolean isWrap;
private CustomPopupWindow(Builder builder) {
this.mContentView = builder.contentView;
this.mParentView = builder.parentView;
this.mListener = builder.listener;
this.isOutsideTouch = builder.isOutsideTouch;
this.isFocus = builder.isFocus;
this.mBackgroundDrawable = builder.backgroundDrawable;
this.mAnimationStyle = builder.animationStyle;
this.isWrap = builder.isWrap;
initLayout();
}
public static Builder builder() {
return new Builder();
}
private void initLayout() {
mListener.initPopupView(mContentView);
setWidth(isWrap ? LayoutParams.WRAP_CONTENT : LayoutParams.MATCH_PARENT);
setHeight(isWrap ? LayoutParams.WRAP_CONTENT : LayoutParams.MATCH_PARENT);
setFocusable(isFocus);
setOutsideTouchable(isOutsideTouch);
setBackgroundDrawable(mBackgroundDrawable);
if (mAnimationStyle != -1)//如果设置了动画则使用动画
setAnimationStyle(mAnimationStyle);
setContentView(mContentView);
}
/**
* 获得用于展示popup内容的view
*/
public View getContentView() {
return mContentView;
}
/**
* 用于填充contentView,必须传ContextThemeWrapper(比如activity)不然popupwindow要报错
*/
public static View inflateView(ContextThemeWrapper context, int layoutId) {
return LayoutInflater.from(context)
.inflate(layoutId, null);
}
public void show() {//默认显示到中间
if (mParentView == null) {
showAtLocation(mContentView, Gravity.CENTER | Gravity.CENTER_HORIZONTAL, 0, 0);
} else {
showAtLocation(mParentView, Gravity.CENTER | Gravity.CENTER_HORIZONTAL, 0, 0);
}
}
public static final class Builder {
private View contentView;
private View parentView;
private CustomPopupWindowListener listener;
private boolean isOutsideTouch = true;//默认为true
private boolean isFocus = true;//默认为true
private Drawable backgroundDrawable = new ColorDrawable(0x00000000);//默认为透明
private int animationStyle = -1;
private boolean isWrap;
private Builder() {
}
public Builder contentView(View contentView) {
this.contentView = contentView;
return this;
}
public Builder parentView(View parentView) {
this.parentView = parentView;
return this;
}
public Builder isWrap(boolean isWrap) {
this.isWrap = isWrap;
return this;
}
public Builder isOutsideTouch(boolean isOutsideTouch) {
this.isOutsideTouch = isOutsideTouch;
return this;
}
public Builder isFocus(boolean isFocus) {
this.isFocus = isFocus;
return this;
}
public Builder backgroundDrawable(Drawable backgroundDrawable) {
this.backgroundDrawable = backgroundDrawable;
return this;
}
public Builder animationStyle(int animationStyle) {
this.animationStyle = animationStyle;
return this;
}
public Builder customListener(CustomPopupWindowListener listener) {
this.listener = listener;
return this;
}
public CustomPopupWindow build() {
if (contentView == null)
throw new IllegalStateException("contentView is required");
if (listener == null)
throw new IllegalStateException("CustomPopupWindowListener is required");
return new CustomPopupWindow(this);
}
}
public interface CustomPopupWindowListener {
public void initPopupView(View contentView);
}
}
总结
- 优点
- 良好的封装性,使用建造者模式可以使客户端不必知道产品内部组成的细节;
- 建造者独立,容易扩展。
- 缺点
- 会产生多余的 Builder 对象以及 Director 对象,消耗内存。