Builder 模式

内容简介:介绍了经典实现以及我们日常的实现方法。最后选取了一个 android 源码中的 Build 设计模式—— AlertDialog 进行分析。

前言

在我们的日常开发中,会经常用到 builder 设计模式,最常见的应用场景就是构造对象参数较多的时候,下面本文将 builder 模式梳理总结一下。

定义

如果说非要给 builder 模式一个定义,那通过查看了《Android源码设计模式解析与实战》,以下是其给出的定义:

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

这样的总结比较经典,但是感觉离彻底明白其中的含义还差点距离。前半句可以理解为将一个对象的创建过程分多步,后半句可以这样理解,使用同样的构建过程,传递不同的参数会产生不同的结果。

经典实现

640?wx_fmt=png上图为 builder 经典写法的 uml ,其实在实际开发过程中, Director 部分经常就被去掉了。 如下所示,定义了 AbstractBuilder :

public abstract class AbstractBuilder {	
    public abstract void buildPart1(int numOfWheel);	
    public abstract void buildPart2(int numOfSeat);	
    public abstract void buildPart3(int capacity);	
    public abstract Vehicle build();	
}

demo 的目标是将车辆 Vehicle 的构建分离, demo 中的 Product 为 Vehicle 类型,可以看下 Vehicle 的定义:

public abstract class Vehicle {	
    // 车轮的数量	
    protected int numOfWheel;	
    // 座椅的数量	
    protected int numOfSeat;	
    // 载重,car按照人数,truck按照吨数	
    protected int capacity;	
    public void setNumOfWheel(int numOfWheel) {	
        this.numOfWheel = numOfWheel;	
    }	
    public void setNumOfSeat(int numOfSeat) {	
        this.numOfSeat = numOfSeat;	
    }	
    public abstract void setCapacity(int capacity);	
}

demo 中 Vehicle 的子类有 Car (小轿车)和 Truck (卡车), Car 类的定义如下:

public class Car extends Vehicle {	
    @Override	
    public void setCapacity(int capacity) {	
        // TODO Auto-generated method stub	
        super.capacity = capacity;	
    }	
    @Override	
    public String toString() {	
        // TODO Auto-generated method stub	
        return "car parameter:" + numOfWheel + "-" + numOfSeat + "-" + capacity;	
    }	
}

Truck 的定义如下:

public class Truck extends Vehicle {	
    @Override	
    public void setCapacity(int capacity) {	
        // TODO Auto-generated method stub	
        super.capacity = capacity;	
    }	
    @Override	
    public String toString() {	
        // TODO Auto-generated method stub	
        return "car parameter:" + numOfWheel + "-" + numOfSeat + "-" + capacity + " ton";	
    }	
}

接下来就看下 Car 的 builder CarBuilder :

public class CarBuilder extends AbstractBuilder {	
    private Car car = new Car();	
    @Override	
    public void buildPart1(int numOfWheel) {	
        // TODO Auto-generated method stub	
        car.setNumOfWheel(numOfWheel);	
    }	
    @Override	
    public void buildPart2(int numOfSeat) {	
        // TODO Auto-generated method stub	
        car.setNumOfSeat(numOfSeat);	
    }	
    public void buildPart3(int capacity) {	
        // TODO Auto-generated method stub	
        car.setCapacity(capacity);	
    }	
    public Vehicle build() {	
        return car;	
    }	
}

TruckBuilder 定义如下:

public class TruckBuilder extends AbstractBuilder {	
    private Truck truck = new Truck();	
    @Override	
    public void buildPart1(int numOfWheel) {	
        // TODO Auto-generated method stub	
        truck.setNumOfWheel(numOfWheel);	
    }	
    @Override	
    public void buildPart2(int numOfSeat) {	
        // TODO Auto-generated method stub	
        truck.setNumOfSeat(numOfSeat);	
    }	
    @Override	
    public void buildPart3(int capacity) {	
        // TODO Auto-generated method stub	
        truck.setCapacity(capacity);	
    }	
    @Override	
    public Vehicle build() {	
        // TODO Auto-generated method stub	
        return truck;	
    }	
}

以上定义了 CarBuiler , TruckBuiler 。虽然在实际开发中经常会省略掉 Director 部分,为了演示, demo 也定义了 Director

public class Director {	
    private AbstractBuilder builder;	
    public Director(AbstractBuilder builder) {	
        this.builder = builder;	
    }	
    public void construct(int numOfWheel, int numOfSeat, int capacity) {	
        if (builder != null) {	
            builder.buildPart1(numOfWheel);	
            builder.buildPart2(numOfSeat);	
            builder.buildPart3(capacity);	
        }	
    }	
}

OK,所有需要定义的部分已经完成,接下来就去调用一下:

public class Client {	
    public static void main(String [] args) {	
        AbstractBuilder builder = new CarBuilder();	
        Director director = new Director(builder);	
        director.construct(4, 5, 5);	
        Car car = (Car) builder.build();	
        System.out.println(car);	
        AbstractBuilder builder2 = new TruckBuilder();	
        Director director2 = new Director(builder2);	
        director2.construct(8, 2, 5);	
        Truck truck = (Truck) builder2.build();	
        System.out.println(truck);	
    }	
}

程序输入如下:

car parameter:4-5-5 

truck parameter:8-2-5 ton

demo演示部分将Vehicle的构造过程分3步,执行完3步构建后返回实例对象。

日常写法

上面的 demo 是经典的写法,但在实际开发中,很少写的那么标准或者那么复杂,大多数情况下 builder 模式主要是为了防止在构建对象时传递太多的参数。查看下以下 demo :

public class Student {	
    private String name;	
    private String nickName;	
    private String sex;	
    private int age;	
    private int weight;	
    private int height;	
    public Student(String name, String nickName, String sex, int age, int weight, int height) {	
        // TODO Auto-generated constructor stub	
        this.name = name;	
        this.nickName = nickName;	
        this.sex = sex;	
        this.age = age;	
        this.weight = weight;	
        this.height = height;	
    }	
    @Override	
    public String toString() {	
        // TODO Auto-generated method stub	
        return "student info:name=" + name + "\n" +	
                            "nickname=" + nickName + "\n" +	
                            "sex=" + sex + "\n" + 	
                            "age=" + age + "\n" + 	
                            "weight=" + weight + "\n" +	
                            "height=" + height;	
    }	
    public static class Builder {	
        private String name;	
        private String nickName;	
        private String sex;	
        private int age;	
        private int weight;	
        private int height;	
        public Builder name(String name) {	
            this.name = name;	
            return this;	
        }	
        public Builder nickName(String nickName) {	
            this.nickName = nickName;	
            return this;	
        }	
        public Builder sex(String sex) {	
            this.sex = sex;	
            return this;	
        }	
        public Builder age(int age) {	
            this.age = age;	
            return this;	
        }	
        public Builder weight(int weight) {	
            this.weight = weight;	
            return this;	
        }	
        public Builder height(int height) {	
            this.height = height;	
            return this;	
        }	
        public Student build() {	
            return new Student(name, nickName, sex, age, weight, height);	
        }	
    } 	
}

以下是测试程序:

public class Client {	
    public static void main(String [] args) {	
        Student student = new Student.Builder().name("rock")	
                                               .nickName("store")	
                                               .sex("boy")	
                                               .age(12)	
                                               .weight(60)	
                                                .height(176).build();	
        System.out.println(student);	
    }	
}

程序运行结果如下:

student info:name=rock

nickname=store

sex=boy

age=12

weight=60

height=176

Android中的使用

在开发过程中,经常使用的 builder 模式其实就是上文所说的日常写法,Android 中最常见的 builder 模式就是 AlertDialog 的创建过程了,以下是 AlertDialog 创建过程的常见写法。

AlertDialog.Builder builder = new AlertDialog.Builder(context)	
                                             .setTitle(title)	
                                             .setView(view)	
                                             .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {	
                                                @Override	
                                                public void onClick(DialogInterface dialog, int which) {	
                                                }	
                                            })	
                                            .setNegativeButton(android.R.string.cancel, null);	
builder.create().show();

感觉很熟悉,这就是我们最常用的 AlertDialog 的构建过程。扒一扒源码,由于 AlertDialog.Builder 的源码较多,就不全部贴出来,感兴趣的同学可以自行看一下。

public static class Builder {	
    private final AlertController.AlertParams P;	
    public Builder(Context context) {	
        this(context, resolveDialogTheme(context, ResourceId.ID_NULL));	
    }	
   ......	
    public Builder setTitle(@StringRes int titleId) {	
        P.mTitle = P.mContext.getText(titleId);	
        return this;	
    }	
    public Builder setTitle(CharSequence title) {	
        P.mTitle = title;	
        return this;	
    }	
    public Builder setCustomTitle(View customTitleView) {	
        P.mCustomTitleView = customTitleView;	
        return this;	
    }	
    ......	
    public Builder setMessage(CharSequence message) {	
        P.mMessage = message;	
        return this;	
    }	
   public Builder setPositiveButton(@StringRes int textId, final OnClickListener listener) {	
        P.mPositiveButtonText = P.mContext.getText(textId);	
        P.mPositiveButtonListener = listener;	
        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;	
    }	
}

AlertDialog.Buidler 类中定义各种 set 方法,执行完 set 方法之后再执行 create 方法便创建了一个 AlertDialog 。

这应该是一个标准的 builder 模式了。可以发现 AlertDialog.Builder 执行 set 方法,其实就是将set参数复制给了对象 P 。对象 P 是什么结构呢?

public static class AlertParams {	
    public final Context mContext;	
    public final LayoutInflater mInflater;	
    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;	
    public DialogInterface.OnClickListener mNegativeButtonListener;	
    public CharSequence mNeutralButtonText;	
    public DialogInterface.OnClickListener mNeutralButtonListener;	
    public boolean mCancelable;	
    public DialogInterface.OnCancelListener mOnCancelListener;	
    public DialogInterface.OnDismissListener mOnDismissListener;	
    public DialogInterface.OnKeyListener mOnKeyListener;	
    public CharSequence[] mItems;	
    public ListAdapter mAdapter;	
    public DialogInterface.OnClickListener mOnClickListener;	
    public int mViewLayoutResId;	
    public View mView;	
    public int mViewSpacingLeft;	
    public int mViewSpacingTop;	
    public int mViewSpacingRight;	
    public int mViewSpacingBottom;	
    public boolean mViewSpacingSpecified = false;	
    public boolean[] mCheckedItems;	
    public boolean mIsMultiChoice;	
    public boolean mIsSingleChoice;	
    public int mCheckedItem = -1;	
    public DialogInterface.OnMultiChoiceClickListener mOnCheckboxClickListener;	
    public Cursor mCursor;	
    public String mLabelColumn;	
    public String mIsCheckedColumn;	
    public boolean mForceInverseBackground;	
    public AdapterView.OnItemSelectedListener mOnItemSelectedListener;	
    public OnPrepareListViewListener mOnPrepareListViewListener;	
    public boolean mRecycleOnMeasure = true;	
    ......	
}

可以发现,AlertController.AlertParams 类型的对象 P 其实就是存放了构建 AlertDialog 需要的各种参数。对象 P 中还有其他函数操作,感兴趣的同学可以去看一下。

将参数保存到P对象,然后执行 create 函数,创建新的 AlertDialog 对象,然后 P 中存放的属性设置给新建的 AlertDilaog 对象,这样,就完成了 AlertDialog 的构建。

总结

Builder 模式的目标是将复杂对象的创建过程进行分解,使对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。在实际开发过程中,通常是在复杂对象内部申明静态内部类 Builder,在 Builder 中保存复杂对象的属性,然后使用 create 或者 build 函数将保存的属性设置给对象。

其实日常开发过程中使用builder模式还没有让我们领略到builer模式的强大,建议参考下这篇文章体会一下:

https://www.cnblogs.com/happyhippy/archive/2010/09/01/1814287.html

--END--

识别二维码,关注我们

640?wx_fmt=png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值