builder模式

定义

复杂的构建与表示分离,使得同样的构建过程可以有不同的表示。

使用场景

  • 相同方法不同的执行顺序,产生不同的结果。
  • 多个部件或者零件装配到一个对象中,但是产生的运行结果又不相同时候。
  • 产品类型非常复杂,或者产品类中的调用顺序不同产生不同的作用,这个时候使用建造者模式非常合适。
  • 初始化一个对象特别复杂,如参数多,且很多参数都有默认值。

UML类图

image

  • builder
    • 抽象类,规范产品的组建,一般由子类实现具体的过程。
  • ConcreteBuilder:
    • 具体的builder类。
  • Director
    • 构造一个使用Builder接口的对象.
  • Product
    • 产品类的抽象。 ConcreteBuilder创建该产品的内部表示并定义它的装配过程。
    • 包含定义组成部件的类,包括将这些部件装配成最终产品的接口。

Builder模式的简单实现

场景:假设客户需要建造一个房子,于是他就会找到一个建筑公司(Director),而建筑公司找到包工头(builder)来建造房子。包工头也不会自己去建房子,他让一些工人(ConcreteBuilder)去建造房子。

角色

  • builder(包工头)

    给出一个抽象接口,定义了各个工人所需要进行的工作。这些工作是为了完成对房子的创建。

  • ConcreteBuilder(工人)

    具体建造者,具体的建造者(工人)去造房子。房子造完后需要将房子还给房屋主人 所以需要有返回房子的方法。

  • Director(建筑公司)

    • 他知道房子怎么设计
    • 他会指挥工人去建造:代码中 ,他也肯定会持有工人 的这个对象的引用
    • 所以他肯定对工人所具备的能力和行为有很深的了解。
    • 从整体角度出发,什么样的房子都能跟你建成。他所有具备的功能可以覆盖你完整的需求。哪怕业主只提出建个非常简单的房子,但是他所具备的能力必须全部覆盖:代码中,所有提出的需求在设计者这个类里面都能找得到。

具体代码

product类,房子:

package com;
//房子类 即product角色
public class Room {
    //窗户
    private String window;
    //地板
    private String floor;

    public String getWindow() {
        return window;
    }

    public void setWindow(String window) {
        this.window = window;
    }

    public String getFloor() {
        return floor;
    }

    public void setFloor(String floor) {
        this.floor = floor;
    }

    @Override
    public String toString() {
        return "Room [window=" + window + ", floor=" + floor + "]";
    }


}

抽象builder类,包工头:

/**
 * 抽象builder类
 *
 */
public interface Builder {
    //设置窗户
    public void makeWindow();
    //设置地板
    public void makeFloor();

    //创建房子
    public Room build();

}

ConcreteBuilder,具体builder类,工人,持有对房子的引用。

package com;

/**
 * 具体builder类,持有对房子的引用。
 * @author xijun
 *
 */
public class WorkBuilder implements Builder{
    Room room  = new Room();
    @Override
    public void makeWindow() {
        room.setWindow("磨砂窗户");
    }

    @Override
    public void makeFloor() {
        room.setFloor("美式地板");
    }

    @Override
    public Room build() {

        return room;
    }

}

Director,建筑公司,持有对包工头(builder)的引用。

package com;


public class Director {

    public Room build(Builder build) {
        build.makeFloor();
        build.makeWindow();
        return build.build();
    }
}

测试代码,client类:

package com;

public class Client {

    public static void main(String[] args) {

        Builder workBuild = new WorkBuilder();
        Director designer = new Director();
        Room room = designer.build(workBuild);
        System.out.println(room.toString());
    }

}

输出结果:

Room [window=法式窗户, floor=欧式地板]

builder模式变型

实际开发中,Director通常会被省略,client端通常扮演指挥者的角色,concreteBuilder直接来进行对象的组装,省去了builder,通过链式调用来达到目的。

image

大致代码如下:

new TestBuilder("a").set().set("b").build();

这种方法适合于:初始化一个对象特别复杂,如参数多,且很多参数都有默认值。

将上面的代码进行builder变型

我们先不探讨这几个类的具体意义,
先将上面的代码进行变型为这种链式调用方式:

WorkBuilder类:

package com;

/**
 * 具体builder类,
 * @author xijun
 *
 */
public class WorkBuilder {

    RoomParams roomParams;

    public WorkBuilder() {
        this.roomParams = new RoomParams();
    }
    //每个方法都会返回自身WorkBuilder,以便可以链式调用。
    public WorkBuilder makeWindow(String window) {
        roomParams.window = window;
        return this;
    }

    //每个方法都会返回自身WorkBuilder,以便可以链式调用。
    public WorkBuilder makeFloor(String floor) {
        roomParams.floor = floor;
        return this;
    }

    public Room build() {
        Room room = new Room();
        //通过apply方法将RoomParams类中的成员变量传递到Room中。
        room.apply(roomParams);
        return room;
    }

    class RoomParams {
        public String window;
        public String floor;
    }

}

其中所有的调用方法(makeWindow(),makeFloor())返回了WorkBuilder本身,这样可以链式调用,直到调用build()方法。

其中RoomParams是WorkBuilder的内部类,和Room的成员变量是一样的,这样以便于将RoomParams的成员变量传到Room中去。通过

    Room room = new Room();
    room.apply(roomParams);

这段代码可以将这些应用的参数传递到Room类的成员变量中。
我们看一下Room类。

package com;

import com.WorkBuilder.RoomParams;

/**
 * 房子类,产品角色
 */
public class Room {
    private String window;
    private String floor;

    //通过apply方法将RoomParams类中的成员变量传递到Room中。
    public void apply(RoomParams roomParams) {
        this.window = roomParams.window;
        this.floor = roomParams.floor;
    }

    @Override
    public String toString() {
        return "Room [window=" + window + ", floor=" + floor + "]";
    }

}

这样我们的client端就可以链式调用了。

package com;

public class Client {

    public static void main(String[] args) {
        Room room = (new WorkBuilder())
        .makeFloor("diban")
        .makeWindow("window").build();
        System.out.println(room.toString());
    }
}

输出结果:

Room [window=window, floor=diban]

Android AlertDialog就是builder模式的变型

AlertDialog的用法会是这样的链式调用:

 new AlertDialog.Builder(MainActivity.this).setTitle("标题").setIcon(R.mipmap.ic_launcher).show();

AlertDialog通过buiilder模式将 AlertDialog的构造和表示分离。

我们看看源码:

//\frameworks\base\core\java\android\app\AlertDialog


public class AlertDialog extends Dialog implements DialogInterface {
 // Controller, 接受Builder成员变量P中的各个参数
    private AlertController mAlert;

 // 构造函数AlertDialog
    AlertDialog(Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
    ...
    //构造AlertController.
        mAlert = AlertController.create(getContext(), this, getWindow());
    }
    ...

    @Override
    public void setTitle(CharSequence title) {
        super.setTitle(title);
        //调用AlertController的setTitle。
        mAlert.setTitle(title);
    }

    /**
     * @see Builder#setCustomTitle(View)
     */
    public void setCustomTitle(View customTitleView) {
        mAlert.setCustomTitle(customTitleView);
    }

    public void setMessage(CharSequence message) {
        mAlert.setMessage(message);
    }
    //AlertDialog中的内部类。
    public static class Builder {
    //AlertController.AlertParams就是存储各种成员变量参数,如title,message.
        private final AlertController.AlertParams P;


        public Builder(Context context, int themeResId) {
            P = new AlertController.AlertParams(new ContextThemeWrapper(
                    context, resolveDialogTheme(context, themeResId)));
        }


        public Context getContext() {
            return P.mContext;
        }



    //设置title,也是设置到了AlertController.AlertParams中。      
        public Builder setTitle(CharSequence title) {
            P.mTitle = title;
            return this;
        }


        public Builder setCustomTitle(View customTitleView) {
            P.mCustomTitleView = customTitleView;
            return this;
        }

        //构建AlertDialog, 传递参数
        public AlertDialog create() {
        //调用new AlertDialog构造对象, 并且将参数传递个体AlertDialog 
            final AlertDialog dialog = new AlertDialog(P.mContext, 0, false);
            //通过调用Apply函数来给P设置参数。
            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;
        }

        public AlertDialog show() {
            final AlertDialog dialog = create();
            dialog.show();
            return dialog;
        }
    }

}

AlertController类中的内部类AlertParams:

    public static class AlertParams {
    //各种各样的参数。
        public int mIconId = 0;
        public Drawable mIcon;
        public int mIconAttrId = 0;
        //标题
        public CharSequence mTitle;
        public View mCustomTitleView;
        //内容
        public CharSequence mMessage;
        public CharSequence mPositiveButtonText;
        public DialogInterface.OnClickListener mPositiveButtonListener;
        public CharSequence mNegativeButtonText;
    ...
    //通过apply这个函数来设置参数。
        public void apply(AlertController dialog) {

            if (mCustomTitleView != null) {
                dialog.setCustomTitle(mCustomTitleView);
            } else {
                if (mTitle != null) {
                //设置title。
                    dialog.setTitle(mTitle);
                }
                if (mIcon != null) {
                    dialog.setIcon(mIcon);
                }
                if (mIconId != 0) {
                    dialog.setIcon(mIconId);
                }
                if (mIconAttrId != 0) {
                    dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId));
                }
            }


        }

通过java控制台来体会 AlertDialog

我将AlertDialog中的builder模式代码进行删减,以便更加直观和深入的体会其中的模式。控制台中输出:

title是:123

client端:

package com;

import com.AlertDialog.Builder;

public class Client {

    public static void main(String[] args) {
        new AlertDialog.Builder().setTitle("123").show();
    }
}

AlertDialog类:

package com;

public class AlertDialog {
    private AlertController mAlert;
    public AlertDialog() {
        mAlert = new AlertController().create();
    }

    static class Builder{

        private AlertController.AlertParams p;
        Builder(){
            p=new AlertController.AlertParams();
        }

        public Builder setTitle(String title) {
            p.title = title;
            return this;
        }

        public AlertDialog show() {
            AlertDialog alertDialog = new AlertDialog();
            p.apply(alertDialog.mAlert);
            return alertDialog;
        }

    }

}

AlertController:

package com;

public class AlertController {

    private String mTitle;
    public AlertController create() {
        return new AlertController();
    }

    public void setTitle(String title) {
        mTitle = title;
        System.out.println("title是:"+ title);
    }

    public static class AlertParams {
        String title;

        public void apply(AlertController mAlert) {
            if(title != null) {
                mAlert.setTitle(title);
            }
        }

    }

}

StringBuilder就是builder模式的变型

这种方式调用具体也有很多的使用场景,例如java中的StringBuilder:
image

在这个Builder模式的实现中,Client同时充当了Director的角色;StringBuilder同时充当了Builder接口和ConcreteBuilder。这是一个最简化的Builder模式的实现。

   1:
 //Client同时充当了Director的角色
   2:
 StringBuilder builder = new
 StringBuilder();
   3:
 builder.Append("happyhippy"
);
   4:
 builder.Append(".cnblogs"
);
   5:
 builder.Append(".com"
);
   6:
 //返回string对象:happyhippy.cnblogs.com
   7:
 builder.ToString(); 

参考:
《Android源码设计模式解析与实战》
Builder模式的误区:将复杂对象的构建进行封装,就是Builder模式了吗?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值