Android设计模式--建造者模式

假如一个对象由许多不同的属性构造,我们想要构造一个我们自己指定特定属性的对象,最简单的方法就是为每种情况提供一个构造函数,我们根据不同的构造函数来得到我们需要的包含了指定属性的对象。我们还是举个例子吧。

一个人有姓名、性别、年龄、身高,体重这五个属性组成,当我们创建一个人这个对象的时候,我们可能有下面这些情况:

1、只希望指定姓名 
2、只希望指定性别 
3、只希望指定年龄 
4、只希望指定身高 
5、只希望指定体重 
6、只希望指定姓名和性别 
7、只希望指定姓名和年龄 
8、只希望指定姓名和身高 
9、只希望指定姓名和体重 
10、只希望指定性别和年龄 
11、只希望指定性别和身高 
12、……

上面就不一样列举了,就是一个排列组合问题,是不是有些凌乱了,如果一个对象的属性更多,那情况就更多了,显然把所以执行情况的构造函数都写出来不是一个明智的选择,因为想想你要写多少个构造函数,好恐怖,不敢想象。

那我们有没有一种方法来解决这个问题呢?这里我们就要使用建造者模式,它就是单独的来对一个对象进行构造,将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。也就是说它来完成对象的构造过程,并且这个过程可以构造出上面我们所说的所有我们希望得到的对象。

建造模式是将复杂的内部创建封装在内部,对于外部调用的人来说,只需要传入建造者和建造工具,对于内部是如何建造成成品的,调用者无需关心。

针对上面所说的那个包含了5个属性的对象,我们使用构建者模式如何完成,下面我们来看看。

1、定义一个Person类,他包含了所有属性的get,set方法。

public class Person {
    private String name;
    private boolean sex;
    private int age;
    private float height;
    private float weight;


    public Person(String name, boolean sex, int age, float height, float weight) {
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.height = height;
        this.weight = weight;
    }

}

2、创建一个Builder类

public class Builder {
    private String name;
    private boolean sex;
    private int age;
    private float height;
    private float weight;

    public Builder setName(String name) {
        this.name = name;
        return this;
    }

    public Builder setSex(boolean sex) {
        this.sex = sex;
        return this;
    }

    public Builder setAge(int age) {
        this.age = age;
        return this;
    }

    public Builder setHeight(float height) {
        this.height = height;
        return this;
    }

    public Builder setWeight(float weight) {
        this.weight = weight;
        return this;
    }

    public Person create() {
        return new Person(name, sex, age, height, weight);
    }
}

上面我们就写好了这个构造过程了。现在就可以根据我们的需要来得到任何我们想要的对象。

Builder builder = new Builder();
builder.setName("Mirhunana");
builder.setAge(23);
Perons person = builder.create();

上面我们就得到了一个我们想要的对象,很方便,很简单。

上面就是构建者的基本思想,现实我们的使用的时候,可能会做出不同的变形,但是基本思想是不变的。下面我们来讲讲完整的构建者模式。

如下图所示。

这里写图片描述

它分为抽象建造者(Builder)角色、具体建造者(ConcreteBuilder)角色、导演者(Director)角色、产品(Product)角色四个角色。

抽象建造者(Builder)角色:给 出一个抽象接口,以规范产品对象的各个组成成分的建造。一般而言,此接口独立于应用程序的商业逻辑。模式中直接创建产品对象的是具体建造者 (ConcreteBuilder)角色。具体建造者类必须实现这个接口所要求的两种方法:一种是建造方法(buildPart1和 buildPart2),另一种是返还结构方法(retrieveResult)。一般来说,产品所包含的零件数目与建造方法的数目相符。换言之,有多少 零件,就有多少相应的建造方法。

  具体建造者(ConcreteBuilder)角色:担任这个角色的是与应用程序紧密相关的一些类,它们在应用程序调用下创建产品的实例。这个角色要完成的任务包括:1.实现抽象建造者Builder所声明的接口,给出一步一步地完成创建产品实例的操作。2.在建造过程完成后,提供产品的实例。

  导演者(Director)角色:担任这个角色的类调用具体建造者角色以创建产品对象。应当指出的是,导演者角色并没有产品类的具体知识,真正拥有产品类的具体知识的是具体建造者角色。

  产品(Product)角色:产品便是建造中的复杂对象。一般来说,一个系统中会有多于一个的产品类,而且这些产品类并不一定有共同的接口,而完全可以是不相关联的。

下面为了说明这个完整的过程,我们对上面我们写的那个构建者模式进行改进。

1、产品类Product 
就是上面的Person

2、抽象建造者类Builder,就是上面的Builder的接口,目的就是为了为构造者提供统一的接口

public interface Builder {

    public Builder setName(String name);

    public Builder setSex(boolean sex);

    public Builder setAge(int age);

    public Builder setHeight(float height);

    public Builder setWeight(float weight);

    public Person create();
}

3、 具体建造者类ConcreteBuilder,就是前面的Builder,只是它实现了一个共同的Builder接口

public class ConcreteBuilder implements Builder {
    private String name;
    private boolean sex;
    private int age;
    private float height;
    private float weight;

    public Builder setName(String name) {
        this.name = name;
        return this;
    }

    public Builder setSex(boolean sex) {
        this.sex = sex;
        return this;
    }

    public Builder setAge(int age) {
        this.age = age;
        return this;
    }

    public Builder setHeight(float height) {
        this.height = height;
        return this;
    }

    public Builder setWeight(float weight) {
        this.weight = weight;
        return this;
    }

    public Person create() {
        return new Person(name, sex, age, height, weight);
    }
}

4、导演者类Director,它就是操作builder对象的

public class Director {
    private Builder builder;

    public Director(Builder builder){
        this.builder = builder;
    }

    public void construct(String name, boolean sex, int age, float height, float weight) {
        builder.setName(name);
        builder.setSex(sex);
        builder.setAge(age);
        builder.setHeight(height);
        builder.setWeight(weight);
    }
}

5、客户端代码

public class Test {
    public static void main(String[] args) {
        Builder builder = new ConcreteBuilder();
        Director pcDirector = new Director(builder);
        pcDirector.construct("Mirhunana", true, 23, 180, 100);
        Person person = builder.create();
    }
}

下面我们来看看Android中的构建者模式吧,最经典的就是AlertDialog了,下面来看看它的用法。

private void showDialog(Context context) {  
    AlertDialog.Builder builder = new AlertDialog.Builder(context);  
    builder.setIcon(R.drawable.icon);  
    builder.setTitle("Title");  
    builder.setMessage("Message");  
    builder.setPositiveButton("Button1",  
            new DialogInterface.OnClickListener() {  
                public void onClick(DialogInterface dialog, int whichButton) {  
                    setTitle("点击了对话框上的Button1");  
                }  
            });  
    builder.setNeutralButton("Button2",  
            new DialogInterface.OnClickListener() {  
                public void onClick(DialogInterface dialog, int whichButton) {  
                    setTitle("点击了对话框上的Button2");  
                }  
            });  
    builder.setNegativeButton("Button3",  
            new DialogInterface.OnClickListener() {  
                public void onClick(DialogInterface dialog, int whichButton) {  
                    setTitle("点击了对话框上的Button3");  
                }  
            });  
    builder.create().show();  // 构建AlertDialog, 并且显示
} 

很显然AlertDialog内部有一个Builder类,它就是用来根据我们的需要构造AlertDialog的。

下面我们来看看这个Builder类。

public static class Builder {
    private final AlertController.AlertParams P;
    private int mTheme;

    public Builder(Context context) {
        this(context, resolveDialogTheme(context, 0));
    }

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

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

    public Builder setTitle(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(int messageId) {
        P.mMessage = P.mContext.getText(messageId);
        return this;
    }

    public Builder setMessage(CharSequence message) {
        P.mMessage = message;
        return this;
    }

    public Builder setIcon(int iconId) {
        P.mIconId = iconId;
        return this;
    }

    public Builder setIcon(Drawable icon) {
        P.mIcon = icon;
        return this;
    }

    public Builder setIconAttribute(int attrId) {
        TypedValue out = new TypedValue();
        P.mContext.getTheme().resolveAttribute(attrId, out, true);
        P.mIconId = out.resourceId;
        return this;
    }

    public Builder setPositiveButton(int textId, final OnClickListener listener) {
        P.mPositiveButtonText = P.mContext.getText(textId);
        P.mPositiveButtonListener = listener;
        return this;
    }

    public Builder setPositiveButton(CharSequence text, final OnClickListener listener) {
        P.mPositiveButtonText = text;
        P.mPositiveButtonListener = listener;
        return this;
    }

    public Builder setNegativeButton(int textId, final OnClickListener listener) {
        P.mNegativeButtonText = P.mContext.getText(textId);
        P.mNegativeButtonListener = listener;
        return this;
    }

    public Builder setNegativeButton(CharSequence text, final OnClickListener listener) {
        P.mNegativeButtonText = text;
        P.mNegativeButtonListener = listener;
        return this;
    }

    public Builder setNeutralButton(int textId, final OnClickListener listener) {
        P.mNeutralButtonText = P.mContext.getText(textId);
        P.mNeutralButtonListener = listener;
        return this;
    }

    public Builder setNeutralButton(CharSequence text, final OnClickListener listener) {
        P.mNeutralButtonText = text;
        P.mNeutralButtonListener = listener;
        return this;
    }

    public Builder setCancelable(boolean cancelable) {
        P.mCancelable = cancelable;
        return this;
    }

    public Builder setOnCancelListener(OnCancelListener onCancelListener) {
        P.mOnCancelListener = onCancelListener;
        return this;
    }

    public Builder setOnDismissListener(OnDismissListener onDismissListener) {
        P.mOnDismissListener = onDismissListener;
        return this;
    }

    public Builder setOnKeyListener(OnKeyListener onKeyListener) {
        P.mOnKeyListener = onKeyListener;
        return this;
    }

    public Builder setItems(int itemsId, final OnClickListener listener) {
        P.mItems = P.mContext.getResources().getTextArray(itemsId);
        P.mOnClickListener = listener;
        return this;
    }

    public Builder setItems(CharSequence[] items, final OnClickListener listener) {
        P.mItems = items;
        P.mOnClickListener = listener;
        return this;
    }

    public Builder setAdapter(final ListAdapter adapter, final OnClickListener listener) {
        P.mAdapter = adapter;
        P.mOnClickListener = listener;
        return this;
    }

    public Builder setCursor(final Cursor cursor, final OnClickListener listener,
            String labelColumn) {
        P.mCursor = cursor;
        P.mLabelColumn = labelColumn;
        P.mOnClickListener = listener;
        return this;
    }

    public Builder setMultiChoiceItems(int itemsId, boolean[] checkedItems, 
            final OnMultiChoiceClickListener listener) {
        P.mItems = P.mContext.getResources().getTextArray(itemsId);
        P.mOnCheckboxClickListener = listener;
        P.mCheckedItems = checkedItems;
        P.mIsMultiChoice = true;
        return this;
    }

    public Builder setMultiChoiceItems(CharSequence[] items, boolean[] checkedItems, 
            final OnMultiChoiceClickListener listener) {
        P.mItems = items;
        P.mOnCheckboxClickListener = listener;
        P.mCheckedItems = checkedItems;
        P.mIsMultiChoice = true;
        return this;
    }

    public Builder setMultiChoiceItems(Cursor cursor, String isCheckedColumn, String labelColumn, 
            final OnMultiChoiceClickListener listener) {
        P.mCursor = cursor;
        P.mOnCheckboxClickListener = listener;
        P.mIsCheckedColumn = isCheckedColumn;
        P.mLabelColumn = labelColumn;
        P.mIsMultiChoice = true;
        return this;
    }

    public Builder setSingleChoiceItems(int itemsId, int checkedItem, 
            final OnClickListener listener) {
        P.mItems = P.mContext.getResources().getTextArray(itemsId);
        P.mOnClickListener = listener;
        P.mCheckedItem = checkedItem;
        P.mIsSingleChoice = true;
        return this;
    }

    public Builder setSingleChoiceItems(Cursor cursor, int checkedItem, String labelColumn, 
            final OnClickListener listener) {
        P.mCursor = cursor;
        P.mOnClickListener = listener;
        P.mCheckedItem = checkedItem;
        P.mLabelColumn = labelColumn;
        P.mIsSingleChoice = true;
        return this;
    }

    public Builder setSingleChoiceItems(CharSequence[] items, int checkedItem, final OnClickListener listener) {
        P.mItems = items;
        P.mOnClickListener = listener;
        P.mCheckedItem = checkedItem;
        P.mIsSingleChoice = true;
        return this;
    } 

    public Builder setSingleChoiceItems(ListAdapter adapter, int checkedItem, final OnClickListener listener) {
        P.mAdapter = adapter;
        P.mOnClickListener = listener;
        P.mCheckedItem = checkedItem;
        P.mIsSingleChoice = true;
        return this;
    }

    public Builder setOnItemSelectedListener(final AdapterView.OnItemSelectedListener listener) {
        P.mOnItemSelectedListener = listener;
        return this;
    }

    public Builder setView(View view) {
        P.mView = view;
        P.mViewSpacingSpecified = false;
        return this;
    }

    public Builder setInverseBackgroundForced(boolean useInverseBackground) {
        P.mForceInverseBackground = useInverseBackground;
        return this;
    }

    public AlertDialog create() {
        final AlertDialog dialog = new AlertDialog(P.mContext, mTheme, 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;
    }

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

它的做法就是: 
1、在构造函数里面创建一个AlertParams对象用来缓存AlertDialog的所有属性,另外单独定义一个theme来缓存AlertDialog的主题。

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

2、我们可以通过一系类的set函数,主要就是把我们设置的属性缓存到AlertParams这个对象里面。

3、调用create函数,它的操作就是创建一个AlertDialog对象,然后把AlertParams里面缓存的属性全部应用到AlertDialog上面去。我们来看看它的具体操作。

(1) 创建AlertDialog对象

final AlertDialog dialog = new AlertDialog(P.mContext, mTheme, false);

(2) 把缓存的属性全部设置到AlertDialog上面去

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);
}

首先我们来看看apply函数。

public void apply(AlertController dialog) {
    if (mCustomTitleView != null) {
        dialog.setCustomTitle(mCustomTitleView);
    } else {
        if (mTitle != null) {
            dialog.setTitle(mTitle);
        }
        if (mIcon != null) {
            dialog.setIcon(mIcon);
        }
        if (mIconId >= 0) {
            dialog.setIcon(mIconId);
        }
        if (mIconAttrId > 0) {
            dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId));
        }
    }
    if (mMessage != null) {
        dialog.setMessage(mMessage);
    }
    if (mPositiveButtonText != null) {
        dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText,
                mPositiveButtonListener, null);
    }
    if (mNegativeButtonText != null) {
        dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText,
                mNegativeButtonListener, null);
    }
    if (mNeutralButtonText != null) {
        dialog.setButton(DialogInterface.BUTTON_NEUTRAL, mNeutralButtonText,
                mNeutralButtonListener, null);
    }
    if (mForceInverseBackground) {
        dialog.setInverseBackgroundForced(true);
    }
    // For a list, the client can either supply an array of items or an
    // adapter or a cursor
    if ((mItems != null) || (mCursor != null) || (mAdapter != null)) {
        createListView(dialog);
    }
    if (mView != null) {
        if (mViewSpacingSpecified) {
            dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight,
                    mViewSpacingBottom);
        } else {
            dialog.setView(mView);
        }
    }

    /*
    dialog.setCancelable(mCancelable);
    dialog.setOnCancelListener(mOnCancelListener);
    if (mOnKeyListener != null) {
        dialog.setOnKeyListener(mOnKeyListener);
    }
    */
}

可以看到AlertParams的这个函数就是把自己内部缓存的属性值全部设置到了AlertDialog里面。

上面的思想跟我们前面将的基本相同,唯一的不同就是,它里面创建了一个属性容器AlertParams,用来存放AlertDialog所有的属性,最后会将这些属性应用到AlertDialog身上,前面我们将的是直接在Builder内部来操作Product对象,也相当于这里的AlertDialog。

另外一个地方也用到了这种构建者模式,那就是我们在创建自定义的Notification的时候。

具体我们来看看Notification里面的setLatestEventInfo函数。

public void setLatestEventInfo(Context context,
        CharSequence contentTitle, CharSequence contentText, PendingIntent contentIntent) {
    Notification.Builder builder = new Notification.Builder(context);

    // First, ensure that key pieces of information that may have been set directly
    // are preserved
    builder.setWhen(this.when);
    builder.setSmallIcon(this.icon);
    builder.setPriority(this.priority);
    builder.setTicker(this.tickerText);
    builder.setNumber(this.number);
    builder.mFlags = this.flags;
    builder.setSound(this.sound, this.audioStreamType);
    builder.setDefaults(this.defaults);
    builder.setVibrate(this.vibrate);

    // now apply the latestEventInfo fields
    if (contentTitle != null) {
        builder.setContentTitle(contentTitle);
    }
    if (contentText != null) {
        builder.setContentText(contentText);
    }
    builder.setContentIntent(contentIntent);
    builder.buildInto(this);
}

可以看到我们设置Notification属性,它也是通过构建者模式,它的做法又有些不同,就是它直接把Notification属性都缓存在Builder对象里面,然后通过builder.buildInto(this)把builder里面的属性应用到this上,这里的this就是Notification对象本身,因为setLatestEventInfo是它的一个成员函数。

我们可以来大概看看Buidler对象里面的属性。

public static class Builder {
    private static final int MAX_ACTION_BUTTONS = 3;

    private Context mContext;

    private long mWhen;
    private int mSmallIcon;
    private int mSmallIconLevel;
    private int mNumber;
    private CharSequence mContentTitle;
    private CharSequence mContentText;
    private CharSequence mContentInfo;
    private CharSequence mSubText;
    private PendingIntent mContentIntent;
    private RemoteViews mContentView;
    private PendingIntent mDeleteIntent;
    private PendingIntent mFullScreenIntent;
    private CharSequence mTickerText;
    private RemoteViews mTickerView;
    private Bitmap mLargeIcon;
    private Uri mSound;
    private int mAudioStreamType;
    private long[] mVibrate;
    private int mLedArgb;
    private int mLedOnMs;
    private int mLedOffMs;
    private int mDefaults;
    private int mFlags;
    private int mProgressMax;
    private int mProgress;
    private boolean mProgressIndeterminate;
    private ArrayList<String> mKindList = new ArrayList<String>(1);
    private Bundle mExtras;
    private int mPriority;
    private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
    private boolean mUseChronometer;
    private Style mStyle;
    private boolean mShowWhen = true;

    ......
    ......
    }

从这里我们就可以看到,确实他内部定义了很多成员变量来存放Notification的属性,最后它会通过builder.buildInto(this)函数把这些属性应用到Notificaition身上,这样达到了为Notificaiton设置属性的目的。我们来看看buildInto函数。

public Notification buildInto(Notification n) {
    build().cloneInto(n, true);
    return n;
}

它内部通过build函数来来创建一个Notification对象,然后把它里面缓存的属性应用到这个Notification上面,最后通过cloneInto把创建的这个Notification对象复制给传进来的这个Notification,也就是我们真正的Notification,这样最终就把所有的属性应用到我们的Notification上面。

1、Builder里面的build()创建一个Notification,并且把Builder里面缓存的属性都应用到这个Notification上面。

2、Builder里面的cloneInto函数,把我们创建的Notificaiton复制给传进来的Notification,这样我们的Notification就拥有这些属性。

具体的源码这里就不展示了,可以看看自定义Notification和Toast这篇文章, 
我们可以看到Builder构建者的使用非常灵活,但是思路和思想确实一致的,我们关键需要把握思想。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值