Android开发之深入理解Builder设计模式

2017-04-23 14:38 阅读 767 次 评论 0 条
版权声明:本文著作权归TeachCourse所有,未经许可禁止转载,谢谢支持!
转载请注明出处:http://teachcourse.cn/2436.html

摘要:

说到Builder设计模式,相信很多人已经相当熟悉了,很自然地联想到AlertDialog,一个使用了Builder设计模式并经常使用到的类。这篇文章将从怎么从Builder设计模式的定义,Builder设计模式的运用和Builder设计模式的优点三方面,再一次深入理解该设计模式的使用,然后封装一个WatchView的例子,如下图,希望大家同样有所收获。

Builder设计模式例子

一、Builder设计模式的定义

在讲解定义之前,先来看一下AlertDialog的简单使用过程,代码如下:

AlertDialog mDialog = null;  
    Builder mBuilder = new AlertDialog.Builder(this);  

    mBuilder.setIcon(R.drawable.ic_launcher);  
    mBuilder.setTitle("系统提示");  
    mBuilder.setMessage("你确定要退出吗?"); 

    mDialog = mBuilder.create();//创建AlertDialog对象  
    mDialog.show();//显示创建的AlertDialog

Builder设计模式同时也叫建造者设计模式,目标对象通过一个Build类,一步一步实现构建的过程,每一步独立地执行,通过组件的装配完成目标对象的创建。

简单地说,Builder设计模式的定义,可以理解成:通过组件的装配一步一步完成目的对象的构建过程

二、Builder设计模式的运用

可以简单地理解成Builder设计模式的运用,就是封装一个用法类似于AlertDialog类的过程,该过程隐藏了类的内部实现细节,程序员只需要按照AlertDialog固定的方式去使用Builder设计模式封装的类即可。

为了能够更好理解Builder设计模式的运用,先看一个简单的例子:

    WatchView.Builder builder=new WatchView.Builder(this);
    WatchView watchView=builder.create();

运行效果图:

Build设计模式例子

将上面的代码稍微修改一下:

    WatchView.Builder builder=new WatchView.Builder(this);

    builder.setRadius(300f)
    .setMinuteColor(Color.BLUE)
    .setSecondColor(Color.RED)
    .setHourColor(0xff999999);

    WatchView watchView=builder.create();

运行效果图:

Builder设计模式例子

你会发现,WatchView是一个使用Builder设计模式封装的类,该类的用法和AlertDialog一致,在使用的过程设置想要的属性,最终会构建出不一样的目的对象。因为每一步但是独立执行,所以可以设置对应属性或不设置,将不会影响目的对象的构建,这样的设计模式降低程序员的学习成本,简单好用。

然后,再来看一下WatchView.Builder内部类的源码:

     public Builder(Context context) {
            this.context = context;
        }
     /**
      *设置电子表的半径大小,默认占满当前屏幕
      */
     public Builder setRadius(float radius) {
            this.radius = radius;
            return this;
        }
     /**
      *设置电子表的时针颜色,默认黑色
      */
     public Builder setHourColor(int hourColor) {
            this.hourColor = hourColor;
            return this;
        }
     /**
      *设置电子表的分针颜色,默认黑色
      */
     public Builder setMinuteColor(int minuteColor) {
            this.minuteColor = minuteColor;
            return this;
        }
     /**
      *设置电子表的秒钟颜色,默认红色
      */
     public Builder setSecondColor(int secondColor) {
            this.secondColor = secondColor;
            return this;
        }
    ...

分析WatchView.Builder的每一个setXXX()方法,发现统一返回当前对象,通过当前对象去调用另一个setXXX()方法,然后继续调用,这也是为什么使用Builder设计模式的例子,可以支持链式调用,这样子的例子还包括:AlertDialogImageLoaderConfigActionSheetDialog等。

再来看一下Builder.create()方法的源码:

    public WatchView create() {
        final WatchView watchView = new WatchView(context);
        /**
         * 设置大小
         */
        watchView.mRadius = radius;
        watchView.mPadding = padding;
        watchView.mTextSize = textSize;
        watchView.mHourWidth = hourWidth;
        watchView.mMinuteWidth = minuteWidth;
        watchView.mSecondWidth = secondWidth;
        /**
         * 设置颜色值
         */
        watchView.mHourColor = hourColor;
        watchView.mMinuteColor = minuteColor;
        watchView.mSecondColor = secondColor;
        watchView.mLongScaleColor = longScaleColor;
        watchView.mShortScaleColor = shortScaleColor;
        watchView.mBackgroundColor = backgroundColor;
        watchView.mBackgroundExternalColor = backgroundExternalColor;
        return watchView;
    }

可以看到Builder.create()创建了首先创建一个目的对象,然后给目的对象的每一个属性赋值,最后返回目的对象,完成整个构建过程。到此,我们对Builder设计模式的思路大体清晰了,在使用该模式之前你需要考虑三个条件:

  • 条件一:目的对象是否需要构建多种运行效果?
  • 条件二:组成目标对象的每一个元素或组件是否相互独立?
  • 条件三:初始化一个目的对象是否特别复杂,如参考多,且很多参数都有默认值

如果满足上述三个条件,你可以考虑使用Builder设计模式。

2.1经典的Builder设计模式

其实,在上面的示例中,我们省略了Builder的抽象过程,而直接使用具体类,经典Builder设计模式的案例中,又该是怎么写的呢?为此,TeachCourse将针对上述示例进行改写,使其更加灵活,符合面向对象程序设计的开闭原则(Open Close Principle),改写代码后的UML类图如下:

继承关系的UML图

将原来的WatchViewBuilder提取为抽象类,WatchViewImplBuilderImpl分别为对应的实现类,依赖抽象,方便扩展,改写后的代码如下:

public abstract class Builder {

    public abstract Builder setRadius(float radius) ;

    public abstract void setBackgroundExterColor(int backgroundExterColor) ;

    public abstract Builder setTextSize(float textSize) ;

    public abstract Builder setHourWidth(float hourWidth) ;

    public abstract Builder setMinuteWidth(float minuteWidth) ;

    public abstract Builder setSecondWidth(float secondWidth) ;

    public abstract Builder setLongScaleColor(int longScaleColor) ;

    public abstract Builder setShortScaleColor(int shortScaleColor) ;

    public abstract Builder setHourColor(int hourColor) ;

    public abstract Builder setMinuteColor(int minuteColor) ;

    public abstract Builder setSecondColor(int secondColor) ;

    public abstract Builder setBackgroundColor(int backgroundColor) ;

    public abstract Builder setPadding(float padding) ;


    public abstract WatchView create() ;
}

BuilderImpl具体实现保持不变,将调用代码的逻辑修改如下:

    Builder builder=new WatchView.BuilderImpl(this);
    builder.setRadius(300f).setMinuteColor(Color.BLUE).setSecondColor(Color.RED).setHourColor(0xff999999);

    WatchView watchView=builder.create();

即使项目升级或更新,也不用担心,我们只需要重写具体实现类即可,下面为BuilderImple具体实现:

public class BuilderImpl {
     public BuilderImpl(Context context) {
            this.context = context;
        }
     /**
      *设置电子表的半径大小,默认占满当前屏幕
      */
     public Builder setRadius(float radius) {
            this.radius = radius;
            return this;
        }
     /**
      *设置电子表的时针颜色,默认黑色
      */
     public Builder setHourColor(int hourColor) {
            this.hourColor = hourColor;
            return this;
        }
     /**
      *设置电子表的分针颜色,默认黑色
      */
     public Builder setMinuteColor(int minuteColor) {
            this.minuteColor = minuteColor;
            return this;
        }
     /**
      *设置电子表的秒钟颜色,默认红色
      */
     public Builder setSecondColor(int secondColor) {
            this.secondColor = secondColor;
            return this;
        }
    ...
}

抽象类WatchView代码如下:

public abstract class WatchView extends View {
    public WatchView(Context context) {
        super(context);
    }

    public WatchView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }
    public abstract void paint(Canvas canvas);
}

具体类WatchViewImpl只需要重写paint()方法即可绘制电子表样式,代码如下:

public class WatchViewImpl extends WatchView {
    ...
    /**
     * 开始绘制钟表
     * @param canvas
     */
    public void paint(Canvas canvas) {
        //移动画布中心点到当前View中心
        canvas.translate(getWidth() / 2, getHeight() / 2);
        //绘制外圆背景颜色
        paintExternalCircle(canvas);
        //绘制内圆背景颜色
        paintCircle(canvas);
        //绘制刻度
        paintScale(canvas);
        //绘制指针
        paintPointer(canvas);
    }

   ...

可能,你需要在电子表上添加新的功能,比如:添加图片背景,也只需要在重写paint()方法时,增加一个paintBg()的方法,而不用改动项目额外的代码。

到此,TeachCourse已经将经典的Builder设计模式讲解完,如果你还是觉得很难理解,那跟着TeachCourse再来捋一下思路吧。

  • 目标对象分为抽象目标对象和具体目标对象,所有使用目标对象的地方,统一依赖抽象目标对象
  • Builder同样分为抽象Builder和具体Builder,所有使用Builder的地方,统一依赖抽象Builder

三、Builder设计模式的优点

AlertDialogWatchView的使用过程,你会发现Builder设计模式的优点:1、结构清晰,2、用法简单,3、方便扩展。

  • 结构清晰,Builder设计模式结构基本固定,一个目标对象,一个Builder对象
  • 用法简单,链式调用Builder提供的方法,自由组合多种组件,构建出多种不一样的目标产品
  • 方便扩展,面向抽象或接口的设计原则,方便功能的扩展、更新、升级

PS:

WatchView源码路径:view/WatchView.java

关注公众号 扫一扫二维码,加我QQ

如果文章对你有帮助,欢迎点击上方按钮关注作者

来源:TeachCourse每周一次,深入学习Android教程,关注(QQ1589359239或公众号TeachCourse)
转载请注明出处:http://teachcourse.cn/2436.html

资源分享

源码下载
如何使用SVN提交项目备份? 如何使用SVN提交项目备份?
TreeMap方法解析 TreeMap方法解析
调试最快的Android模拟器-Genymotion常见问题 调试最快的Android模拟器-Geny
浅谈ViewHolder的优化getView方法 浅谈ViewHolder的优化getView

发表评论

呲牙 憨笑 坏笑 偷笑 色 微笑 抓狂 睡觉 酷 流汗 鼓掌 大哭 可怜 疑问 晕 惊讶 得意 尴尬 发怒 奋斗 衰 骷髅 啤酒 吃饭 礼物 强 弱 握手 OK NO 勾引 拳头 差劲 爱你

表情