设计模式——建造者模式

设计模式——建造者模式

先来看一下什么是设计模式。

1. 定义

某类特定问题的代码设计解决方案

其实设计模式呀,也只是前人针对某类问题的代码设计经验而已,并没有很高大上,也不要怕。咱们只需要理解它,并学会使用,如果能在此基础上修改,那也是极好的。毕竟咱们要青出于蓝而胜于蓝嘛。

2. 作用
  • 复用成功的代码设计模式,降低开发成本和周期
  • 适应业务变化
  • 提高代码复用率
  • 提高代码可维护性、可拓展性
  • 使代码更加优雅
  • 让代码更容易被他人理解
3. 设计模式的设计原则

在设计模式进行设计时需要遵循以下的原则:

  • 单一职责原则

    • 一个类=只有一个引起它变化的原因。

      如果一个类承担的职责过多,即耦合性太高=一个职责的变化可能会影响到其他的职责

  • 开放封闭原则

    • 一个实体(类、函数、模块等)应该对外扩展开放,对内修改关闭

      1. 即每次发生变化时,要通过添加新的代码来增强现有类型的行为,而不是修改原有的代码。
      2. 符合开放封闭原则的最好方式是提供一个固有的接口,然后让所有可能发生变化的类实现该接口,让固定的接口与相关对象进行交互。(工厂模式)
  • 里氏替代原则

    • 子类必须替换掉它们的父类型。

      1. 在软件开发过程中,子类替换父类后,程序的行为是一样的。
      2. 只有当子类替换掉父类后软件的功能不受影响时,父类才能真正地被复用,而子类也可以在父类的基础上添加新的行为。(策略模式)
  • 依赖倒置原则

    • 细节应该依赖于抽象,而抽象不应该依赖于细节。

      所谓的的 “面向接口编程,而不是面向实现编程”。这样可以降低客户与具体实现的耦合。

  • 接口隔离原则

    • 使用多个专门功能的接口,而不是使用单一的总接口。

      不要让一个单一的接口承担过多的职责,而应把每个职责分离到多个专门的接口中,进行接口分离。(比如网络回调接口的使用(SUCCESS ERROR)设计)

  • 合成复用原则

    • 在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分。

      新对象通过向这些对象的委派达到复用已用功能的目的。简单地说,就是要尽量使用合成/聚合,尽量不要使用继承。

  • 最小知识原则(迪米特法则)

    • 一个模块或对象应尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立,这样当一个模块修改时,影响的模块就会越少,扩展起来更加容易。

      1. 关于迪米特法则的其他描述:只与你直接的朋友们通信;不要跟“陌生人”说话。
      2. 外观模式(Facade Pattern)和中介者模式(Mediator Pattern)就使用了迪米特法则。

咱们今天的主体内容:

建造者设计模式

1.定义

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

2.主要作用

在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象。

  1. 用户只需要给出指定复杂对象的类型和内容;
  2. 建造者模式负责按顺序创建复杂对象(把内部的建造过程和细节隐藏起来)
3.解决的问题
  • 方便用户创建复杂的对象(不需要知道实现过程)
  • 代码复用性 & 封装性(将对象构建过程和细节进行封装 & 复用)

例子:造电脑 & 买电脑。

  1. 工厂(建造者模式):负责制造电脑(组装过程和细节在工厂内)
  2. 电脑购买者(用户):你只需要说出你需要的型号(对象的类型和内容),然后直接购买就可以使用了
    (不需要知道电脑是怎么组装的(CPU,主板,硬盘,内存等等))
4.模式的原理:
  1. 指挥者(Director)直接和客户(Client)进行需求沟通;
  2. 沟通后指挥者将客户创建产品的需求划分为各个部件的建造请求(Builder);
  3. 将各个部件的建造请求委派到具体的建造者(ConcreteBuilder);
  4. 各个具体建造者负责进行产品部件的构建;
  5. 最终构建成具体产品(Product)。
5.实例分析

接下来走个例子,来直观感受一下建造者模式

a.实例背景

  • 背景:我记得我之前开直播的时候,对电脑还是一个小白,但是为了电脑,还是去了电脑城一趟。(虽然被宰了,但是还好。毕竟哪个电脑使用到了现在还满OK的)。去电脑城组装一趟台式游戏机。
  • 分析过程:去了之后,好多家店,感觉每一家都特别高大上。最后找了一个我自己认为满OK的店,进去与老板交流。
    • 电脑城老板(Diretor)和我(Client)进行需求沟通用电脑(买来打游戏?学习?看片?)
    • 当他了解需求后,电脑城老板将我需要的主机划分为各个部件(Builder)的建造请求(CPU、主板、内存等等)
    • 指挥装机人员(ConcreteBuilder)去构建组件;
    • 将组件组装起来成我需要的电脑(Product)

b.开始装机(步骤)

步骤1:定义组装的过程(Builder):组装电脑的过程

public abstract class Builder {  

	// 第一步:装CPU
    public abstract void  BuildCPU()// 第二步:装主板
    public abstract void BuildMainboard();

	// 第三步:装硬盘
    public abstract void BuildHD();

    // 第四步:组装电脑
    public abstract Computer GetComputer();
        
}

步骤2: 电脑城老板委派任务给装机人员(Director)

public class Director{
    // 装机人员需要进行的步骤,装硬盘设备等等。
    public void Construct(Builder builder){
        builder.BuildCPU();
        builder.BuildMainboard();
        builder.BuildHD();
    }
}

步骤3: 定义具体产品类(Product):电脑

public class Computer{
    //电脑组件的集合
    private List<String> parts = new ArrayList<String>()//用于将组件组装到电脑里
    public void Add(String part){
    	parts.add(part);
    }
    
    public void Show(){
        for (int i = 0;i<parts.size();i++){    
        	System.out.println(“组件”+parts.get(i)+“装好了”);
        }
        
        System.out.println(“电脑组装完成,请验收”);
    }
        
    
}

步骤4: 创建具体的建造者(ConcreteBuilder):装机人员

	// 具体的某装机人员
  public class ConcreteBuilder extend Builder{
      
    // 创建产品实例
    Computer computer = new Computer();

    // 组装产品
    @Override
    public void  BuildCPU(){  
       computer.Add("组装CPU")
    }  

    @Override
    public void  BuildMainboard(){  
       computer.Add("组装主板")
    }  

    @Override
    public void  BuildHD(){  
       computer.Add("组装主板")
    }  

    // 返回组装成功的电脑
     @Override
      public  Computer GetComputer(){  
      return computer
    }  
}

步骤5: 客户端调用-我到带走电脑回家打游戏

public class BuilderPattern{
    
    public static void main(String[] args){

    Director director = new Director();
    Builder builder = new ConcreteBuilder();

    // 沟通需求后,老板叫某装机人员去装电脑
    director.Construct(builder);

    // 装完后,某组装人员搬来组装好的电脑
    Computer computer = builder.GetComputer();
    // 某组装人员展示电脑给我看,没有问题,带走回家打游戏
    computer.Show()}

}

看下展示的电脑OK不

组件CUP装好了
组件主板装好了
组件硬盘装好了
电脑组装完成,请验收

现在回想起来,好气哦。当时真的什么都不明白。都是装好了,并没有显示型号呀。。。

好了,键锋一转,

看一下

建造者模式的优点与缺点。
  • 优点

    • 易于解耦

      • 将产品本身与产品创建过程进行解耦,可以使用相同的创建过程来得到不同的产品。也就说细节依赖抽象。
    • 易于精确控制对象的创建

      • 将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰
    • 易于拓展

      • 增加新的具体建造者无需修改原有类库的代码,易于拓展,符合“开闭原则“。

        每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。

  • 缺点

    • 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
    • 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
应用场景
  • 需要生成的产品对象有复杂的内部结构,这些产品对象具备共性;
  • 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。

今天的重头戏——Android中的简易的建造者模式——参考自AlertDialog源码——版本名称:Oreo API Level: 26。

我们都知道AlertDialog有两个包的,一个是android.app包下的。另一个是android.support.v7.app包下的,那么这两个包中的AlertDialog区别大吗?以及使用上有什么区别吗?当然还有就是我们今天所要讲解的建造者模式啦。

1.使用

看一下两个包中create()方法中的代码。

  • android.app包

    • /**
               * Creates a {@link AlertDialog} with the arguments supplied to this builder. It does not
               * {@link Dialog#show()} the dialog. This allows the user to do any extra processing
               * before displaying the dialog. Use {@link #show()} if you don't have any other processing
               * to do and want this to be created and displayed.
               */
              / * *
              *创建一个{@link AlertDialog},并将参数提供给这个生成器。它不
              * {@link对话框#show()}对话框。这允许用户执行任何额外的处理
              在显示对话框之前。如果没有任何其他处理,请使用{@link #show()}
              *执行并希望创建和显示此操作。
              * /
              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;
              }
      
  • android.support.v7.app包

    • /**
               * Creates an {@link AlertDialog} with the arguments supplied to this
               * builder.
               * <p>
               * Calling this method does not display the dialog. If no additional
               * processing is needed, {@link #show()} may be called instead to both
               * create and display the dialog.
               */
              / * *
              *使用提供的参数创建一个{@link AlertDialog}
              *建设者。
              * < p >
              *调用此方法不会显示对话框。如果没有额外的
              *需要处理,{@link #show()}可以同时调用这两个
              *创建并显示对话框。
              * /
              public AlertDialog create() {
                  // We can't use Dialog's 3-arg constructor with the createThemeContextWrapper param,
                  // so we always have to re-set the theme
          //我们不能在createThemeContextWrapper参数中使用Dialog的3-arg构造函数,
      //所以我们总是要重新设置主题
                  final AlertDialog dialog = new AlertDialog(P.mContext, mTheme);
                  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区别就在Theme主题上。android.support.v7.app包下的AlertDialog总是要重新设置主题。

总结:两个差不多,都是弹框,只是支持的包不一样,所以做出来的样式也不一样,但功能和用法一样。在使用上两个包的区别不是很大,主要就是Theme主题的使用。

既然上面说了,在使用上两个包下的AlertDialog没有区别,那么我们就来看看android.support.v7.app包下的AlertDialog吧,毕竟是兼容包嘛。

2.开始分析今天的重头戏建造者模式

直接上源码,来看(只贴出重要代码)

// 首先是类,继承自AppCompatDialog,并实现了DialogInterface接口。使得拥有兼容属性
public class AlertDialog extends AppCompatDialog implements DialogInterface {
    
    // Alert核心控制类
    final AlertController mAlert;
    
    ......
        
        
    /**
     * Construct an AlertDialog that uses an explicit theme.  The actual style
     * that an AlertDialog uses is a private implementation, however you can
     * here supply either the name of an attribute in the theme from which
     * to get the dialog's style (such as {@link R.attr#alertDialogTheme}.
     */
    protected AlertDialog(@NonNull Context context, @StyleRes int themeResId) {
        super(context, resolveDialogTheme(context, themeResId));
        mAlert = new AlertController(getContext(), this, getWindow());
    }
	// 然后是构造方法,可以看到修饰符是protected,所以我们是不能直接new对象进行使用的。
    protected AlertDialog(@NonNull Context context, boolean cancelable,
            @Nullable OnCancelListener cancelListener) {
        this(context, 0);
        setCancelable(cancelable);
        setOnCancelListener(cancelListener);
    }
    
    // 一般AlertDialog展示的标题和内容,以及等等内容。传递进来的参数都到了Alert控制者类
    
     @Override
    public void setTitle(CharSequence title) {
        super.setTitle(title);
        mAlert.setTitle(title);
    }
    
    /**
     * Sets the message to display.
     *
     * @param message The message to display in the dialog.
     */
    public void setMessage(CharSequence message) {
        mAlert.setMessage(message);
    }
    
    
    
    ......
        
    
    /*
    	根据我们平常的使用习惯,是使用Builder.create()方法进行创建AlertDialog对象的。
    	那我们找一个我们常用的Builder。
    */
    public static class Builder {
        
        /*
        	Alert核心控制类中的静态内部类
        	主要作用就是保存AlertDialog的一些设置参数。可以看到Buidler中设置的参数都是赋值给了它对应的成员变量
        */
        private final AlertController.AlertParams P;
            
            public Builder(@NonNull Context context) {
            this(context, resolveDialogTheme(context, 0));
        }

        /**
         * Creates a builder for an alert dialog that uses an explicit theme
         * resource.
         * <p>
         * The specified theme resource ({@code themeResId}) is applied on top
         * of the parent {@code context}'s theme. It may be specified as a
         * style resource containing a fully-populated theme, such as
         * {@link R.style#Theme_AppCompat_Dialog}, to replace all
         * attributes in the parent {@code context}'s theme including primary
         * and accent colors.
         * <p>
         * To preserve attributes such as primary and accent colors, the
         * {@code themeResId} may instead be specified as an overlay theme such
         * as {@link R.style#ThemeOverlay_AppCompat_Dialog}. This will
         * override only the window attributes necessary to style the alert
         * window as a dialog.
         * <p>
         * Alternatively, the {@code themeResId} may be specified as {@code 0}
         * to use the parent {@code context}'s resolved value for
         * {@link android.R.attr#alertDialogTheme}.
         *
         * @param context the parent context
         * @param themeResId the resource ID of the theme against which to inflate
         *                   this dialog, or {@code 0} to use the parent
         *                   {@code context}'s default alert dialog theme
         */
        public Builder(@NonNull Context context, @StyleRes int themeResId) {
            P = new AlertController.AlertParams(new ContextThemeWrapper(
                    context, resolveDialogTheme(context, themeResId)));
            mTheme = themeResId;
        }
            
    }
    
    ......
    
    /*
    	我们一般使用Builder,进行调用setTitle(),setMessage()等等方法。
    	下面我们来看一下这些方法。返回值都是返回Builder自己,链式调用,行云流水。
    	每次set参数都是传给了AlertController.AlertParams进行保存。
    */
    ......
        
    /**
         * Set the title using the given resource id.
         *
         * @return This Builder object to allow for chaining of calls to set methods
         */
        public Builder setTitle(@StringRes int titleId) {
            P.mTitle = P.mContext.getText(titleId);
            return this;
        }

    	// setTitle的重载方法
    
        /**
         * Set the title displayed in the {@link Dialog}.
         *
         * @return This Builder object to allow for chaining of calls to set methods
         */
        public Builder setTitle(@Nullable CharSequence title) {
            P.mTitle = title;
            return this;
        }
    
    	/**
         * Set the message to display using the given resource id.
         *
         * @return This Builder object to allow for chaining of calls to set methods
         */
        public Builder setMessage(@StringRes int messageId) {
            P.mMessage = P.mContext.getText(messageId);
            return this;
        }
	
    	// setMessage的重载方法
    
        /**
         * Set the message to display.
         *
         * @return This Builder object to allow for chaining of calls to set methods
         */
        public Builder setMessage(@Nullable CharSequence message) {
            P.mMessage = message;
            return this;
        }
    
    /*
    	从上面代码看出,我们平常调用的方法传进来的参数,给了P,那么这个P是什么东西,其实就是一个AlertController.AlertParams静态内部类,然后在create()方法中的apply()方法进行给AlertDialog的属性赋值。赋值的值就是从我们调用的setTitle()和setMessage()方法传进来的,给了AlertController.AlertParams的参数,它再赋值给当前AlertDialog对象的AlertController成员变量对应的成员变量。
    	看一下apply()。(请移步到下一个代码块中)
    */
    
    
    // 下来就是我们的Builder的create()创建方法以及show()展示方法。
    
    /**
         * Creates an {@link AlertDialog} with the arguments supplied to this
         * builder.
         * <p>
         * Calling this method does not display the dialog. If no additional
         * processing is needed, {@link #show()} may be called instead to both
         * create and display the dialog.
         */
        public AlertDialog create() {
            // We can't use Dialog's 3-arg constructor with the createThemeContextWrapper param,
            // so we always have to re-set the theme
            final AlertDialog dialog = new AlertDialog(P.mContext, mTheme);
            /*
            	apply()是将之前保存在AlertController.AlertParams的参数值赋给当前AlertDialog对象的AlertController成员变量对应的成员变量。
            */
            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;
        }

        /**
         * Creates an {@link AlertDialog} with the arguments supplied to this
         * builder and immediately displays the dialog.
         * <p>
         * Calling this method is functionally identical to:
         * <pre>
         *     AlertDialog dialog = builder.create();
         *     dialog.show();
         * </pre>
         */
        public AlertDialog show() {
            final AlertDialog dialog = create();
            dialog.show();
            return dialog;
        }
    }

}

这里是AlertController#AlertParams.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);
            }
            // 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);
            }
            */
        }

最后在看一下AlertController 控制类中的成员变量,其实AlertController.AlertParams接收Builder赋值的数据,并把值展示给AlertDialog。

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

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

    private int mViewLayoutResId;

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

    Button mButtonPositive;
    private CharSequence mButtonPositiveText;
    Message mButtonPositiveMessage;

    Button mButtonNegative;
    private CharSequence mButtonNegativeText;
    Message mButtonNegativeMessage;

    Button mButtonNeutral;
    private CharSequence mButtonNeutralText;
    Message mButtonNeutralMessage;

    NestedScrollView mScrollView;

    private int mIconId = 0;
    private Drawable mIcon;

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

    ListAdapter mAdapter;

    int mCheckedItem = -1;

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

    private boolean mShowTitle;

    private int mButtonPanelLayoutHint = AlertDialog.LAYOUT_HINT_NONE;

    Handler mHandler;
}

以及控制类中的静态内部类AlertParams的成员变量,也就是Builder类传进来的值。

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

以上就是对它使用的建造者设计模式的分析过程。

从上可知AlertDialog并不能直接new出来,因为构造方法是protected的,所以如果我们要创建AlertDialog的话,我们 需要使用到该类中的一个静态内部类:public static class Builder,然后来调用AlertDialog 里的相关方法,来对AlertDialog进行定制,最后调用show()方法来显示我们的AlertDialog对话框!

而使用Builder来对AlertDialog进行定制。这中间,Builder就是所使用的建造者模式进行构建的。

AlertDialog dialog = new AlertDialog
	.Builder()
    .setTitle("嘟嘟嘟")
    .setMessage("这是一个建造者模式")
    .create()
    .show();
		
/*
	使用静态内部类Builder中的定制方法(如setTitle(),setMessage()等等),最后构建成功创建create(),然后展示到用户面前show()。
	写起代码来行云流水,链式调用,非常舒服。
*/

下面我们来写一个小Demo,学习一下AlertDialog中的建造者模式。

那咱们就使用建造者模式组装一个简易小汽车吧。

需求:组装一个简易小汽车(car)。

分析:简易小汽车有那些零件进行组装(轮胎(tyre),车身(body),方向盘(wheel),座椅(seat))。

开始组装(编码)

CarBuilder.java文件

public class CarBuilder {

	// 轮胎
	private String tyreType;
	// 车身
	private String bodyType;
	// 方向盘
	private String wheelType;
	// 座椅
	private String seatType;

	/*
	 * 这里我们不允许外部的类直接去new它,当然了,只允许我们的Car。所以,我们在构造方法中 做一个限定。
	 * 只需要Car去new它
	 */
	CarBuilder() {

	}

	public final CarBuilder tyreType(String tyreType) {
		this.tyreType = tyreType;
		return this;
	}

	public final CarBuilder bodyType(String bodyType) {
		this.bodyType = bodyType;
		return this;
	}

	public final CarBuilder wheelType(String wheelType) {
		this.wheelType = wheelType;
		return this;
	}

	public final CarBuilder seatType(String seatType) {
		this.seatType = seatType;
		return this;
	}

	public final Car build(){
		return new Car(tyreType, bodyType, wheelType, seatType);
	}

}

Car.java文件

public class Car {

	// 轮胎
	private final String TYRETYPE;
	// 车身
	private final String BODYTYPE;
	// 方向盘
	private final String WHEELTYPE;
	// 座椅
	private final String SEATTYPE;
	
	public Car(String tyreType, String bodyType, String wheelType, String seatType) {
		super();
		TYRETYPE = tyreType;
		BODYTYPE = bodyType;
		WHEELTYPE = wheelType;
		SEATTYPE = seatType;
	}
	
	//现在我们就可以创建我们的构造者了。
    public static CarBuilder builder() {
        return new CarBuilder();
    }

	public void printString() {
		System.out.println("Car [TYRETYPE=" + TYRETYPE + ", BODYTYPE=" + BODYTYPE + ", WHEELTYPE=" + WHEELTYPE + ", SEATTYPE="
				+ SEATTYPE + "]");
	}
    
}

Test.java文件

public class Test {

	public static void main(String[] args) {
		Car car = Car
				.builder()
				.tyreType("轮胎")
				.bodyType("车身")
				.wheelType("方向盘")
				.seatType("座椅")
				.build();
		car.printString();;
	}
	
}

之后就可以从简易小汽车,做一个重量级小轿车呀。车内装饰什么的呢。需要什么加什么。比如我要加上一个车内装饰招财猫。代码修改如下:

CarBuilder.java文件加入一个成员变量车内装饰。

public class CarBuilder {
    ...

    // 车内装饰
    private String upholsteryType;

    public final CarBuilder upholsteryType(String upholsteryType){
            this.upholsteryType = upholsteryType;
            return this;
        }
    
    public final Car build(){
		return new Car(tyreType, bodyType, wheelType, seatType, upholsteryType);
	}

    ...
}

Car.java文件

public class Car {
    ...
    
    // 车内装饰
	private String UPHOLSTERYTYPE;;
	
	public Car(
			String tyreType, 
			String bodyType, 
			String wheelType, 
			String seatType, 
			String upholsterytype) {
		super();
		TYRETYPE = tyreType;
		BODYTYPE = bodyType;
		WHEELTYPE = wheelType;
		SEATTYPE = seatType;
		UPHOLSTERYTYPE = upholsterytype;
	}
    
    public void printString() {
		System.out.println("Car [TYRETYPE=" + TYRETYPE + ", BODYTYPE=" + BODYTYPE + ", WHEELTYPE=" + WHEELTYPE + ", SEATTYPE="
				+ SEATTYPE + ", UPHOLSTERYTYPE=" + UPHOLSTERYTYPE + "]");
	}
    
    ...
        
}

Test.java文件

public class Test {

	public static void main(String[] args) {
		Car car = Car
				.builder()
				.tyreType("轮胎")
				.bodyType("车身")
				.wheelType("方向盘")
				.seatType("座椅")
				.upholsteryType("招财猫")
				.build();
		car.printString();
	}
	
}

代码扩展性强,调用逻辑思维清楚。链式调用,行云流水。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值