创建型设计模式之Builder模式

1 Builder模式概念

1.1 介绍

  Builder模式是一步一步创建一个复杂对象的创建型模式,它允许用户在不知道内部构建细节的情况下,可以更精细地控制对象的构造流程。该模式是为了将构建复杂对象的过程和它的部件解耦,使得构建过程和部件的表示隔离开来。
  Builder模式,在于分工明确,一个抽象建造者,一个具体的建造者,一个指挥者,当然还需要具体的产品。那么我们以一个软件产品为例:技术主管就是抽象建造者,他和产品经理沟通,知道要做一个什么样的产品;而程序员就是具体的劳动者,技术主管说咋做你就咋做;而指挥者就是公司的产品经理,负责和用户沟通,了解客户的需求。

1.2 定义

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

1.3 使用场景

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

2 Builder模式UML类图通用

角色介绍:

  • Product——产品类 : 产品的抽象类。
  • Builder——抽象类, 规范产品的组建,一般是由子类实现具体的组件过程。
  • ConcreteBuilder——具体的构建者。
  • Director——指挥者,统一组装过程(可省略)。

这里写图片描述

3 通用模式代码

(1)产品类

/**
 * 产品类
 */
public class Product {
    public void doSomething() {
        // 独立业务处理
    }
}

(2)抽象建造者类

/**
 * 抽象建造者
 * 如果有多个产品类就有几个具体的建造者,而且这多个产品类具有相同接口或抽象类
 */
public abstract class Builder { 
    // setPart方法是零件的配置,设置产品的不同零件,或者不同的装配顺序,以产生不同的产品
    public abstract void setPart(); 

    // 建造产品
    public abstract Product buildProduct();
}

(3)具体建造者类

/**
 * 具体建造者
 */
public class ConcreteProduct extends Builder {
    private Product product = new Product(); 

    // 设置产品零件
    public void setPart() {
        // 产品类内的逻辑处理    
    }

    // 组建一个产品
    public Product buildProduct() {
        return product;
    }
}

(4)指挥者(导演)类

/**
 * 指挥者类
 * 指挥者类起到封装的作用,避免高层模块深入到建造者内部的实现类
 */
public class Director {
    // 构建不同的产品
    private Builder builder = new ConcreteProduct(); 

    public Product getAProduct() {
        builder.setPart();

        // 设置不同的零件,产生不同的产品
        return builder.buildProduct();
    }

}

4 Builder模式变种(常用)

4.1 普通

public class Person {
    private String name;
    private int age;
    private double height;
    private double weight;

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

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public double getHeight() {
        return height;
    }
    public void setHeight(double height) {
        this.height = height;
    }
    public double getWeight() {
        return weight;
    }
    public void setWeight(double weight) {
        this.weight = weight;
    }
}

  创建各个需要的对象:

Person p1=new Person();
Person p2=new Person("张三");
Person p3=new Person("李四",18);
Person p4=new Person("王五",21,180);
Person p5=new Person("赵六",17,170,65.4);

  可以想象一下这样创建的坏处,最直观的就是四个参数的构造函数的最后面的两个参数到底是什么意思,可读性不怎么好,如果不点击看源码,不知道哪个是weight哪个是height。还有一个问题就是当有很多参数时,编写这个构造函数就会显得异常麻烦,这时候如果换一个角度,试试Builder模式,你会发现代码的可读性一下子就上去了。

4.2 使用Builder模式

public class Person {
    private String name;
    private int age;
    private double height;
    private double weight;

    private Person(Builder builder){
        this.name=builder.name;
        this.age=builder.age;
        this.height=builder.height;
        this.weight=builder.weight;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public double getHeight() {
        return height;
    }
    public void setHeight(double height) {
        this.height = height;
    }
    public double getWeight() {
        return weight;
    }
    public void setWeight(double weight) {
        this.weight = weight;
    }

    static class Builder{
        private String name;
        private int age;
        private double height;
        private double weight;

        public Builder setName(String name) {
            this.name=name;
            return this;
        }
        public Builder setAge(int age) {
            this.age=age;
            return this;
        }
        public Builder setHeight(double height) {
            this.height=height;
            return this;
        }
        public Builder setWeight(double weight) {
            this.weight=weight;
            return this;
        }
        public Person build(){
            return new Person(this);// build()返回Person对象
        }
    }
}

  在Builder类里定义了一份与Person类一模一样的变量,通过一系列的成员函数进行设置属性值,但是返回值都是this,也就是都是Builder对象,最后提供了一个build函数用于创建Person对象,返回的是Person对象,对应的构造函数在Person类中进行定义,也就是构造函数的入参是Builder对象,然后依次对自己的成员变量进行赋值,对应的值都是Builder对象中的值。此外Builder类中的成员函数返回Builder对象自身的另一个作用就是让它支持链式调用,使代码可读性大大增强。于是可以这样创建Person类。

Person person = new Person().Builder()
      .setName("张三")
      .setAge(18)
      .setHeight(178.5)
      .setWeight(67.4)
      .build();

4.3 总结一下

(1)定义一个静态内部类Builder,内部的成员变量和外部类一样
Builder类通过一系列的方法用于成员变量的赋值,并返回当前对象本身(this)。
(2)Builder类提供一个build方法或者create方法用于创建对应的外部类,该方法内部调用了外部类的一个私有构造函数,该构造函数的参数就是内部类Builder。
(3)外部类提供一个私有构造函数供内部类调用,在该构造函数中完成成员变量的赋值,取值为Builder对象中对应的值。

5 Builder模式实战

(1)产品类——探探App

/**
 * 产品类——探探App
 */
public class Product {
    public static final String ANDROID = "android";
    public static final String IOS = "ios";

    private String appName;
    private String appFuction;
    private String appSystem;

    public String getAppName() {
        return appName;
    }

    public void setAppName(String appName) {
        this.appName = appName;
    }

    public String getAppFuction() {
        return appFuction;
    }

    public void setAppFuction(String appFuction) {
        this.appFuction = appFuction;
    }

    public String getAppSystem() {
        return appSystem;
    }

    public void setAppSystem(String appSystem) {
        this.appSystem = appSystem;
    }

    @Override
    public String toString() {
        return "Product [appName=" + appName + ", appFuction=" + appFuction
                + ", appSystem=" + appSystem + "]";
    }
}

(2)抽象建造者类——技术主管(可省略)

/**
 * 抽象建造者类——技术主管 
 */
public abstract class Build {
    public abstract Build setAppName(String appName);

    public abstract Build setAppFuction(String appFuction);

    public abstract Build setAppSystem(String appSystem);

    public abstract Product build();
}

(3)具体建造者类——全栈程序员

/**
 * 具体建造者类——全栈程序员
 */
public class WorkBuilder extends Build {

    private Product product;// 产品类
    private InnerProduct innerProduct = new InnerProduct();// 产品缓冲类

    @Override
    public Build setAppName(String appName) {
        innerProduct.setAppName(appName);
        return this;
    }

    @Override
    public Build setAppFuction(String appFuction) {
        innerProduct.setAppFuction(appFuction);
        return this;
    }

    @Override
    public Build setAppSystem(String appSystem) {
        innerProduct.setAppSystem(appSystem);
        return this;
    }

    @Override
    public Product build() {
        product = new Product();
        product.setAppName(innerProduct.getAppName());
        product.setAppFuction(innerProduct.getAppFuction());
        product.setAppSystem(innerProduct.getAppSystem());
        return product;
    }

    /**
     * 产品缓冲类
     */
    private class InnerProduct {
        private String appName;
        private String appFuction;
        private String appSystem;

        public String getAppName() {
            return appName;
        }

        public void setAppName(String appName) {
            this.appName = appName;
        }

        public String getAppFuction() {
            return appFuction;
        }

        public void setAppFuction(String appFuction) {
            this.appFuction = appFuction;
        }

        public String getAppSystem() {
            return appSystem;
        }

        public void setAppSystem(String appSystem) {
            this.appSystem = appSystem;
        }
    }
}

(4)指挥者类——产品经理(可省略)

/**
 * 指挥者类——产品经理 
 */
public class Director {
    public static Product create(String system) {
        return new WorkBuilder().setAppSystem(system).setAppName("探探").setAppFuction("划一划找妹子").build();
    }
}

(5)客户端——客户

/**
 * 客户端——客户
 */
public class Client {  
    public static void main(String[] args){  
        // 客户:我需要一个可以摇一摇找妹子的软件  
        // 产品经理:分析得出就做一个探探  
        // 技术主管:appName:探探,系统:ios,android功能:摇一摇找妹子  
        Product android = Director.create(Product.ANDROID);  
        Product ios = Director.create(Product.IOS);  
        System.out.println(android);
        System.out.println(ios);

        /**
         * 变异来的建造者可以只需要具体建造者,抽象的不要了,指挥者也可以不要了
         */
        // 程序员觉得太累了,工资又少,干的最多,最后决定自己出去单干。  
        WorkBuilder niubiProgremer = new WorkBuilder();  
        Product androidBest = niubiProgremer.setAppName("探探").setAppSystem(Product.ANDROID).setAppFuction("摇一摇,找妹子").build();  
        Product iosBest = niubiProgremer.setAppName("探探").setAppSystem(Product.IOS).setAppFuction("摇一摇,找妹子").build();
        System.out.println("-------------------------------------------------------------------------------------");
        System.out.println(androidBest);
        System.out.println(iosBest);
    }  
}  

(6)结果

Product [appName=探探, appFuction=划一划找妹子, appSystem=android]
Product [appName=探探, appFuction=划一划找妹子, appSystem=ios]
-------------------------------------------------------------------------------------
Product [appName=探探, appFuction=摇一摇,找妹子, appSystem=android]
Product [appName=探探, appFuction=摇一摇,找妹子, appSystem=ios]

6 Android源码中的Builder模式

  在Android源码中,我们最常用到的Builder模式就是AlertDialog.Builder, 使用该Builder来构建复杂的AlertDialog对象。

6.1 AlertDialog时序图分析

这里写图片描述
这里写图片描述

6.2 AlertDialog源码分析

  在Android源码中,我们最常用到的Builder模式就是AlertDialog.Builder, 使用该Builder来构建复杂的AlertDialog对象。简单示例如下 :

(1)showDialog——显示基本的AlertDialog

// 显示基本的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, 并且显示
    } 

  从类名可以看出这是一个Builder模式,通过Builder对象来组装Dialog的各个部分,如:Title\Button\Message等,将Dialog的构造和表示进行分离。

  结果如图所示 :

这里写图片描述

(2)AlertDialog

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

    // 构造函数  
    protected AlertDialog(Context context, int theme) {  
        this(context, theme, true);  
    }  

    // 构造AlertDialog  
    AlertDialog(Context context, int theme, boolean createContextWrapper) {  
        super(context, resolveDialogTheme(context, theme), createContextWrapper);  
        mWindow.alwaysReadCloseOnTouchAttr();  
        mAlert = new AlertController(getContext(), this, getWindow());  
    }  

    // 实际上调用的是mAlert的setTitle方法  
    @Override  
    public void setTitle(CharSequence title) {  
        super.setTitle(title);  
        mAlert.setTitle(title);  
    }  

    // 实际上调用的是mAlert的setCustomTitle方法  
    public void setCustomTitle(View customTitleView) {  
        mAlert.setCustomTitle(customTitleView);  
    }  

    public void setMessage(CharSequence message) {  
        mAlert.setMessage(message);  
    }  

    // AlertDialog其他的代码省略  

    // ************  Builder为AlertDialog的内部类   *******************  
    public static class Builder {  
        // 1:存储AlertDialog的各个参数, 例如title, message, icon等.  
        private final AlertController.AlertParams P;  
        // 属性省略  

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

        // Builder的其他代码省略 ......  

        // 2:设置各种参数  
        public Builder setTitle(CharSequence title) {  
            P.mTitle = title;  
            return this;  
        }  

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

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

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

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

        // 3:构建AlertDialog, 传递参数  
        public AlertDialog create() {  
            // 4:调用new AlertDialog构造对象, 并且将参数传递个体AlertDialog   
            final AlertDialog dialog = new AlertDialog(P.mContext, mTheme, false);  
            // 5:将P中的参数应用的dialog中的mAlert对象中  
            P.apply(dialog.mAlert); // 重要 
            dialog.setCancelable(P.mCancelable);  
            if (P.mCancelable) {  
                dialog.setCanceledOnTouchOutside(true);  
            }  
            dialog.setOnCancelListener(P.mOnCancelListener);  
            if (P.mOnKeyListener != null) {  
                dialog.setOnKeyListener(P.mOnKeyListener);  
            }  
            return dialog;  
        }  
    }  
}

  通过Builder来设置AlertDialog中的title, message, button等参数, 这些参数都存储在类型为AlertController.AlertParams的成员变量P中,AlertController.AlertParams中包含了与之对应的成员变量。在调用Builder类的create函数时才创建AlertDialog, 并且将Builder成员变量P中保存的参数应用到AlertDialog的mAlert对象中,即P.apply(dialog.mAlert)代码段。我们看看AlertController的实现 :

(3)AlertController


class AlertController {
    private final Context mContext;
    private final AppCompatDialog mDialog;
    private final Window mWindow;

    private CharSequence mTitle;
    private CharSequence mMessage;
    private ListView mListView;
    private View mView;

    private int mViewLayoutResId;

    private int mViewSpacingLeft;
    private int mViewSpacingTop;
    private int mViewSpacingRight;
    private int mViewSpacingBottom;
    private boolean mViewSpacingSpecified = false;

    private Button mButtonPositive;
    private CharSequence mButtonPositiveText;
    private Message mButtonPositiveMessage;

    private Button mButtonNegative;
    private CharSequence mButtonNegativeText;
    private Message mButtonNegativeMessage;

    private Button mButtonNeutral;
    private CharSequence mButtonNeutralText;
    private Message mButtonNeutralMessage;

    private NestedScrollView mScrollView;

    private int mIconId = 0;
    private Drawable mIcon;

    private ImageView mIconView;
    private TextView mTitleView;
    private TextView mMessageView;
    private View mCustomTitleView;

    private ListAdapter mAdapter;

    private int mCheckedItem = -1;

    private int mAlertDialogLayout;
    private int mButtonPanelSideLayout;
    private int mListLayout;
    private int mMultiChoiceItemLayout;
    private int mSingleChoiceItemLayout;
    private int mListItemLayout;

    private int mButtonPanelLayoutHint = AlertDialog.LAYOUT_HINT_NONE;

    private Handler mHandler;

    /**
     * installContent(),极为重要,它调用了Window对象的setContentView,这个setContentView与Activity中一样,实际上Activity最终也是调用Window对象的setContentView函数。
     */
    public void installContent() {
        // 设置窗口
        final int contentView = selectContentView();
        // 设置窗口的内容视图布局
        mDialog.setContentView(contentView);
        // 初始化AlertDialog其他子视图的内容
        setupView();// setupView
    }

  private int selectContentView() {
        if (mButtonPanelSideLayout == 0) {
            return mAlertDialogLayout;
        }
        if (mButtonPanelLayoutHint == AlertDialog.LAYOUT_HINT_SIDE) {
            return mButtonPanelSideLayout;
        }
        return mAlertDialogLayout;// AlertDialog的布局id
    }

    /**
     * setupView()
     */ 
    private void setupView() {
        // 获取并初始化内容区域
        final View parentPanel = mWindow.findViewById(R.id.parentPanel);
        final View defaultTopPanel = parentPanel.findViewById(R.id.topPanel);
        final View defaultContentPanel = parentPanel.findViewById(R.id.contentPanel);
        final View defaultButtonPanel = parentPanel.findViewById(R.id.buttonPanel);
        final ViewGroup customPanel = (ViewGroup) parentPanel.findViewById(R.id.customPanel);
        setupCustomContent(customPanel);

        // 自定义内容视图区域
        final View customTopPanel = customPanel.findViewById(R.id.topPanel);
        final View customContentPanel = customPanel.findViewById(R.id.contentPanel);
        final View customButtonPanel = customPanel.findViewById(R.id.buttonPanel);
        final ViewGroup topPanel = resolvePanel(customTopPanel, defaultTopPanel);
        final ViewGroup contentPanel = resolvePanel(customContentPanel, defaultContentPanel);
        final ViewGroup buttonPanel = resolvePanel(customButtonPanel, defaultButtonPanel);

        setupContent(contentPanel);
        setupButtons(buttonPanel);
        setupTitle(topPanel);

        final boolean hasCustomPanel = customPanel != null
                && customPanel.getVisibility() != View.GONE;
        final boolean hasTopPanel = topPanel != null
                && topPanel.getVisibility() != View.GONE;
        final boolean hasButtonPanel = buttonPanel != null
                && buttonPanel.getVisibility() != View.GONE;

        // Only display the text spacer if we don't have buttons.
        if (!hasButtonPanel) {
            if (contentPanel != null) {
                final View spacer = contentPanel.findViewById(R.id.textSpacerNoButtons);
                if (spacer != null) {
                    spacer.setVisibility(View.VISIBLE);
                }
            }
        }

        if (hasTopPanel) {
            if (mScrollView != null) {
                mScrollView.setClipToPadding(true);
            }
        }

        // Update scroll indicators as needed.
        if (!hasCustomPanel) {
            final View content = mListView != null ? mListView : mScrollView;
            if (content != null) {
                final int indicators = (hasTopPanel ? ViewCompat.SCROLL_INDICATOR_TOP : 0)
                        | (hasButtonPanel ? ViewCompat.SCROLL_INDICATOR_BOTTOM : 0);
                setScrollIndicators(contentPanel, content, indicators,
                        ViewCompat.SCROLL_INDICATOR_TOP | ViewCompat.SCROLL_INDICATOR_BOTTOM);
            }
        }

        final ListView listView = mListView;
        if (listView != null && mAdapter != null) {
            listView.setAdapter(mAdapter);
            final int checkedItem = mCheckedItem;
            if (checkedItem > -1) {
                listView.setItemChecked(checkedItem, true);
                listView.setSelection(checkedItem);
            }
        }
    }

    /**
     * AlertController.AlertParams 持有AlertController的所有属性,在调用builder里的设置属性方法时,就是给AlertController.AlertParams做一个缓存。
     */
    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;

        public AlertParams(Context context) {
            mContext = context;
            mCancelable = true;
            mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        }

        /**
         * 最主要的方法是apply(),实际上就是把P中的参数挨个的设置到AlertController中, 也就是AlertDialog中的mAlert对象。从AlertDialog的各个setter方法中我们也可以看到,实际上也都是调用了mAlert对应的setter方法。
         */
        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);
            }
            // 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);
                }
            } else if (mViewLayoutResId != 0) {
                dialog.setView(mViewLayoutResId);
            }

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

(5)调用create()和show()方法(该函数在Dialog中)

new AlertDialog.Builder(this).setTitle("标题").setIcon(R.mipmap.ic_launcher).setMessage("测试").create().show(); 

public class Dialog implements DialogInterface.... {

    public void create() {
        if (!mCreated) {
            dispatchOnCreate(null);
        }
    }

    /**
     * show()主要作用:
     * (1)通过dispatchOnCreate函数来调用AlertDialog的onCreate函数;
     * (2)然后调用AlertDialog的onStart函数
     * (3)最后将Dialog的DecorView添加到WindowManaget中。
     */
    public void show() {
        if (mShowing) {
            if (mDecor != null) {
                if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
                    mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);
                }
                mDecor.setVisibility(View.VISIBLE);
            }
            return;
        }

        mCanceled = false;

        // 1.onCreate调用
        if (!mCreated) {
            dispatchOnCreate(null);// dispatchOnCreate
        }

        // 2.onStart
        onStart();
        // 3.获取DecorView
        mDecor = mWindow.getDecorView();
           // 代码省略
        // 4.获取布局参数
        WindowManager.LayoutParams l = mWindow.getAttributes();
        if ((l.softInputMode
                & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
            WindowManager.LayoutParams nl = new WindowManager.LayoutParams();
            nl.copyFrom(l);
            nl.softInputMode |=
                    WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
            l = nl;
        }

        try {
            // 5.将mDecor添加到WindowManager中
            mWindowManager.addView(mDecor, l);
            mShowing = true;
            // 发送一个显示Dialog的消息
            sendShowMessage();
        } finally {
        }
    }

    // dispatchOnCreate()
    void dispatchOnCreate(Bundle savedInstanceState) {
            if (!mCreated) {
                onCreate(savedInstanceState);// 接着看AlertDialog的onCreate函数
                mCreated = true;
            }
        }
    }

(6)AlertDialog的onCreate方法

public class AlertDialog extends AppCompatDialog implements DialogInterface {

    private final AlertController mAlert;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);// Dialog的onCreate空实现
        mAlert.installContent();// 调用了AlertController的installContent()方法
    }
}

(7)AlertController的installContent()和setupView()方法

class AlertController {

    private int mAlertDialogLayout;

    // 构造函数
    public AlertController(Context context, AppCompatDialog di, Window window) {
        mContext = context;
        mDialog = di;
        mWindow = window;
        mHandler = new ButtonHandler(di);

        final TypedArray a = context.obtainStyledAttributes(null, R.styleable.AlertDialog,R.attr.alertDialogStyle, 0);

        // AlertDialog的布局id,也就是AlertDialog_android_layout.xml的布局
        mAlertDialogLayout = a.getResourceId(R.styleable.AlertDialog_android_layout, 0);
        mButtonPanelSideLayout = a.getResourceId(R.styleable.AlertDialog_buttonPanelSideLayout, 0);

        mListLayout = a.getResourceId(R.styleable.AlertDialog_listLayout, 0);
        mMultiChoiceItemLayout = a.getResourceId(R.styleable.AlertDialog_multiChoiceItemLayout, 0);
        mSingleChoiceItemLayout = a
               .getResourceId(R.styleable.AlertDialog_singleChoiceItemLayout, 0);
        mListItemLayout = a.getResourceId(R.styleable.AlertDialog_listItemLayout, 0);

        a.recycle();

        /* We use a custom title so never request a window title */
        di.supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
    }

    /**
     * installContent(),极为重要,它调用了Window对象的setContentView,这个setContentView与Activity中一样,实际上Activity最终也是调用Window对象的setContentView函数。
     */
    public void installContent() {
        // 设置窗口
        final int contentView = selectContentView();
        // 设置窗口的内容视图布局
        mDialog.setContentView(contentView);
        // 初始化AlertDialog其他子视图的内容
        setupView();// setupView
    }

  private int selectContentView() {
        if (mButtonPanelSideLayout == 0) {
            return mAlertDialogLayout;
        }
        if (mButtonPanelLayoutHint == AlertDialog.LAYOUT_HINT_SIDE) {
            return mButtonPanelSideLayout;
        }
        return mAlertDialogLayout;// AlertDialog的布局id
    }

    /**
     * setupView()
     */ 
    private void setupView() {
        // 获取并初始化内容区域
        final View parentPanel = mWindow.findViewById(R.id.parentPanel);
        final View defaultTopPanel = parentPanel.findViewById(R.id.topPanel);
        final View defaultContentPanel = parentPanel.findViewById(R.id.contentPanel);
        final View defaultButtonPanel = parentPanel.findViewById(R.id.buttonPanel);
        final ViewGroup customPanel = (ViewGroup) parentPanel.findViewById(R.id.customPanel);
        setupCustomContent(customPanel);

        // 自定义内容视图区域
        final View customTopPanel = customPanel.findViewById(R.id.topPanel);
        final View customContentPanel = customPanel.findViewById(R.id.contentPanel);
        final View customButtonPanel = customPanel.findViewById(R.id.buttonPanel);
        final ViewGroup topPanel = resolvePanel(customTopPanel, defaultTopPanel);
        final ViewGroup contentPanel = resolvePanel(customContentPanel, defaultContentPanel);
        final ViewGroup buttonPanel = resolvePanel(customButtonPanel, defaultButtonPanel);

        setupContent(contentPanel);
        setupButtons(buttonPanel);
        setupTitle(topPanel);

        final boolean hasCustomPanel = customPanel != null
                && customPanel.getVisibility() != View.GONE;
        final boolean hasTopPanel = topPanel != null
                && topPanel.getVisibility() != View.GONE;
        final boolean hasButtonPanel = buttonPanel != null
                && buttonPanel.getVisibility() != View.GONE;

        // Only display the text spacer if we don't have buttons.
        if (!hasButtonPanel) {
            if (contentPanel != null) {
                final View spacer = contentPanel.findViewById(R.id.textSpacerNoButtons);
                if (spacer != null) {
                    spacer.setVisibility(View.VISIBLE);
                }
            }
        }

        if (hasTopPanel) {
            if (mScrollView != null) {
                mScrollView.setClipToPadding(true);
            }
        }

        // Update scroll indicators as needed.
        if (!hasCustomPanel) {
            final View content = mListView != null ? mListView : mScrollView;
            if (content != null) {
                final int indicators = (hasTopPanel ? ViewCompat.SCROLL_INDICATOR_TOP : 0)
                        | (hasButtonPanel ? ViewCompat.SCROLL_INDICATOR_BOTTOM : 0);
                setScrollIndicators(contentPanel, content, indicators,
                        ViewCompat.SCROLL_INDICATOR_TOP | ViewCompat.SCROLL_INDICATOR_BOTTOM);
            }
        }

        final ListView listView = mListView;
        if (listView != null && mAdapter != null) {
            listView.setAdapter(mAdapter);
            final int checkedItem = mCheckedItem;
            if (checkedItem > -1) {
                listView.setItemChecked(checkedItem, true);
                listView.setSelection(checkedItem);
            }
        }
    }
}

  在该函数调用之后整个Dialog的视图内容全部设置完毕。

(8)小总结
  在AlertDialog的Builder模式中并没有看到Director角色出现,并没按照标准的模式使用,而是做了一些修改。AlertDialog.Builder同时扮演了上下文提到的builder、ConcreteBuilder、Director的角色,简化了Builder模式的设计。

7 总结

  Builder模式在Android开发中也较为常用,通常作为配置类的构建器将配置的构建和表示分离开来,同时也是将配置从目标类中隔离出来,避免过多的setter方法。Builder模式比较常见的实现形式是通过调用链实现,这样使得代码更简洁、易懂,例如图片请求框架ImageLoader就是通过ImageLoaderConfig进行配置,这样避免了目标类中被过多的接口“污染”。

7.1 优点

(1)良好的封装性, 使用建造者模式可以使客户端不必知道产品内部组成的细节;
(2)建造者独立,容易扩展;
(3)在对象创建过程中会使用到系统中的一些其它对象,这些对象在产品对象的创建过程中不易得到。

7.2 缺点

(1)会产生多余的Builder对象以及Director对象,消耗内存;
(2)对象的构建过程暴露。

8 参考文章与链接

《Android源码设计模式解析与实战》

《设计模式之禅》

Android源码分析之Builder模式

Android进阶系列之源码分析AlertDialog建造者模式

Android设计模式——Builder模式

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值