摘要:
说到Builder设计模式,相信很多人已经相当熟悉了,很自然地联想到AlertDialog
,一个使用了Builder设计模式并经常使用到的类。这篇文章将从怎么从Builder设计模式的定义,Builder设计模式的运用和Builder设计模式的优点三方面,再一次深入理解该设计模式的使用,然后封装一个WatchView
的例子,如下图,希望大家同样有所收获。
一、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();
运行效果图:
将上面的代码稍微修改一下:
WatchView.Builder builder=new WatchView.Builder(this);
builder.setRadius(300f)
.setMinuteColor(Color.BLUE)
.setSecondColor(Color.RED)
.setHourColor(0xff999999);
WatchView watchView=builder.create();
运行效果图:
你会发现,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设计模式的例子,可以支持链式调用,这样子的例子还包括:AlertDialog
,ImageLoaderConfig
,ActionSheetDialog
等。
再来看一下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类图如下:
将原来的WatchView
和Builder
提取为抽象类,WatchViewImpl
和BuilderImpl
分别为对应的实现类,依赖抽象,方便扩展,改写后的代码如下:
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设计模式的优点
从AlertDialog
和WatchView
的使用过程,你会发现Builder设计模式的优点:1、结构清晰,2、用法简单,3、方便扩展。
- 结构清晰,Builder设计模式结构基本固定,一个目标对象,一个Builder对象
- 用法简单,链式调用Builder提供的方法,自由组合多种组件,构建出多种不一样的目标产品
- 方便扩展,面向抽象或接口的设计原则,方便功能的扩展、更新、升级
PS:
- 如果您对UML类图相关符号还是不太熟悉,建议阅读《Android开发之UML类图简介》
- 如果你不明白什么是面向抽象编程,建议阅读《面向抽象的编程思想》
- 如果您不熟悉接口的定义和使用意义,建议阅读《深入理解接口的定义和意义》
WatchView源码路径:view/WatchView.java
你可能感兴趣的文章
转载请注明出处: https://www.teachcourse.cn/2436.html ,谢谢支持!