美文网首页
构建者(Builder)模式

构建者(Builder)模式

作者: 笔墨Android | 来源:发表于2017-12-17 14:12 被阅读0次

    不怕跌倒,所以飞翔

    本文中知识点概述:

    1. 构建者模式的定义以及为什么要使用构建者模式;
    2. 构建者模式在Android中常用的使用方式;
    3. 构建者模式在源码中的体现;

    本文将从以下几个方面讲解关于构建者模式的使用和自己关于构建者使用时候的理解.

    1.构造者模式的定义以及为什么要使用构建者模式

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

    其实举生活中的例子的话,我怕你们不是很好理解,所以这里我决定举一个日常开发中常见的例子,便于大家理解:

    设想这么一个场景,项目中需求一个学生对象,学生呢?有年级和班级,很简单的例子,你创建了一个

    public class Student {
        private String grade;
        private String Id;
    
        public Student(String grade, String id) {
            this.grade = grade;
            Id = id;
        }
    }
    

    你创建了一个这样一个学生对象,在很长一段时间,项目是这么维护的,但是突然有一天,项目经理跑过来和你说我们有一个新功能要维护学生的年龄和性别(这个要求不过分),然后你觉得也没什么,加就加吧,但是你又想了,不能修改之前的逻辑是吧,然后你聪明的复写了一个构造方法,解决了这个问题,你的代码编程了这个样子.

    public class Student {
        private String grade;
        private String Id;
    
        private String age;
        private String sex;
        
        public Student(String grade, String id) {
            this.grade = grade;
            Id = id;
        }
    
        public Student(String grade, String id, String age, String sex) {
            this.grade = grade;
            Id = id;
            this.age = age;
            this.sex = sex;
        }
    }
    

    好,至此问题解决了,你也觉得没有什么大不了的,其实代码也没有做太大的修改,又过了一段时间,你们项目经理又跑来了,说之前的需求很好,我们想针对学生增加一些更多的参数,你就问,你想加什么啊???产品经理告诉你,这次我们想多加一些属性来确保学生信息的完整,所以我们要加地区,各科的学习成绩,身高体重,高矮胖瘦,学习能力...... 一下子要加10个属性,你心中一定是一万头草泥马在奔腾,这个时候你还是像之前一样,添加的话,我估计你连参数都区分不开,创建一个对象传入10个左右的参数,可能你觉得没什么,不就是传10个参数吗?但是产品又说了,可能又的学生不需要这么多参数,身高和体重可以不传,你是在相应的创建一个9个参数的构造方法吗?你说我可以,那好有一天维护到20个参数呢?你怎么办,你还可以?那我只能说这篇文章你不适合你,你赶紧去传参数吧!

    针对以上情况,编程界的大神们就创建了构建者模式,这个也就是构建者模式的由来,其实构建者模式应用的场景不只于此,那么就看大家的想像控件了,其实所有的涉及模式都是为了你少写代码,或者看上去你写代码比较牛逼使用的.好了唠叨了这么多,就是想让大家对构建者有一个更好的认识!

    2.构建者模式在Android中常用的使用方式;

    针对上面这个问题,一般的项目中都会遇到相应的问题,就是有些对象有无止境的参数,需要传,所以我先针对上面这个例子进行讲解,这样呢?解决了上面的问题,我好在继续往下讲解,先上一段代码.

    public class Student {
    
        public static void main(String[] args) {
            Student student = new Builder()
                    .setAge("4")
                    .setGrade("3年级")
                    .setSex("男")
                    .builder();
    
            System.out.println(student.toString());
        }
    
        private String grade;
        private String Id;
        private String age;
        private String sex;
        private String hobby;
    
        public Student(Builder builder) {
            this.grade = builder.grade;
            this.Id = builder.Id;
            this.age = builder.age;
            this.sex = builder.sex;
            this.hobby = builder.hobby;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "grade='" + grade + '\'' +
                    ", Id='" + Id + '\'' +
                    ", age='" + age + '\'' +
                    ", sex='" + sex + '\'' +
                    ", hobby='" + hobby + '\'' +
                    '}';
        }
    
        public static class Builder {
            private String grade;
            private String Id;
            private String age;
            private String sex;
            private String hobby;
    
            public Builder setGrade(String grade) {
                this.grade = grade;
                return this;
            }
    
            public Builder setId(String id) {
                Id = id;
                return this;
            }
    
            public Builder setAge(String age) {
                this.age = age;
                return this;
            }
    
            public Builder setSex(String sex) {
                this.sex = sex;
                return this;
            }
    
            public Builder setHobby(String hobby) {
                this.hobby = hobby;
                return this;
            }
    
            public Student builder() {
                return new Student(this);
            }
        }
    }
    

    打印结果如下:

    Student{grade='3年级', Id='null', age='4', sex='男', hobby='null'}
    

    这里有一个问题,是关于Android Studio3.0以上的版本,其实你运行这段代码的时候是运行不起来的,为什么呢?因为运行到main方法的时候会报错.

    Error:Gradle: failed to create directory 'D:\Temp\Builder\app\build\generated\source\r\debug\com\hejin\builder'.
    Error:Gradle: java.util.concurrent.ExecutionException: java.util.concurrent.ExecutionException: com.android.tools.aapt2.Aapt2Exception: AAPT2 error: check logs for details
    Error:Gradle: java.util.concurrent.ExecutionException: com.android.tools.aapt2.Aapt2Exception: AAPT2 error: check logs for details
    Error:Gradle: com.android.tools.aapt2.Aapt2Exception: AAPT2 error: check logs for details
    Error:Gradle: Execution failed for task ':app:processDebugResources'.
    > Failed to execute aapt
    

    解决办法是在项目的gradle.properties的文件的最后添加android.enableAapt2=false就完美的解决问题了!

    其实这里面就使用到了相应的构建者模式,其实就是在相应的类中添加一个构建的对象,最后在把对象相应的值,赋值给你创建的对象,这样不管几个参数,你只要在构建那个类中添加相应的方法就可以了,也就解决了上面说的那个问题!

    3.构建者模式在源码中的体现;

    相信大家在开发的时候,都使用过对话框这么一个类吧!

            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setIcon(R.mipmap.ic_launcher)
                    .setTitle("这是一个标题")
                    .setMessage("这个是中间显示的内容")
                    .create().show();
    

    相信这段代码很常见(请忽略我写的比较简单),其实这个AlertDialog.Builder就是一个典型的构建者模式,接下来我们去看看它是怎么实现的吧!

    1. AlertDialog.Builder是AlertDialog的一个内部类,这个和上面那个学生类的构建者是一样的;

    2. 往下看的时候你会发现,如下代码;

          public Builder setTitle(@StringRes int titleId) {
              P.mTitle = P.mContext.getText(titleId);
              return this;
          }
      

      也是和上面的学生类中的Builder一样,这里也是给相应的内容赋值的一波操作(这里我只截取了一个设置标题的方法,这里你可以看看其他的内容)!

    3. 然后我们看看create()这个方法是怎么实现的;

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

      这里其实你不用看那么多,否则你会乱的,主要看第4行和第5行,首先第4行也是创建了一个你想要的对象,和Student的builder方法是一样的,那么你可能会问了,第5行做了什么?

    4. 然后我们看看第5行的具体实现:

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

      其实你仔细看下这个方法其实就是相当于给对话框进行赋值的(相当于Student中的构造函数中传入的那个Builder对象其实含义是一样的),因为对话框有好多层的封装,关于赋值就不在这里进行讲解了,你只要体会一下其中的思想,知道到底是怎么回事就好了!


    其实构建者模式的写法基本上都这样,只要你自己看你会发现其实在项目中有很多都是这么去实现的,如果有写的不对的,还请大家指处,小弟马上进行改正!!!

    相关文章

      网友评论

          本文标题:构建者(Builder)模式

          本文链接:https://www.haomeiwen.com/subject/iaiywxtx.html