Android设计模式——Builder模式
2019-09-29 本文已影响0人
如愿以偿丶
1.Builder模式介绍
Builder模式又称建造者模式,将复杂对象的构建过程和表示过程进行分离,让其(参数)构建过程变得更加的简单和直观。
2.Builder模式使用场景
1.一个复杂的对象,对象中的方法调用顺序不同产生了不同的作用,可以使用Builder模式
2.当我们初始化一个特别复杂,参数很多的对象,且很多参数都具有默认值时,可以使用Builder模式
3.Builder模式和链式调用的区别
Builder设计模式和链式调用是没有任何关系的
3.1.Builder它是一种设计模式,但是它一般会采用链式调用的方式,并不是所有的链式调用都是Builder设计模式
Builder模式体现形式:它一般都会有一个Builder对象,例如:Dialog,OkHttp,Retrofit,Glide等等都采用了Builder设计模式
通过Dialog源码简单分析
/**
* AlertDialog :最终构建的Dialog对象
* Bulider:用于构建我们的Dialog
* AlertParams:用于存放我们设置的参数
*/
public class AlertDialog extends Dialog implements DialogInterface {
//AlertController用来接收Builder 成员变量 P中的各个参数
private AlertController mAlert;
//构造函数
protected AlertDialog(Context context) {
this(context, 0);
}
AlertDialog(Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
super(context, createContextThemeWrapper ? resolveDialogTheme(context, themeResId) : 0,
createContextThemeWrapper);
mWindow.alwaysReadCloseOnTouchAttr();
mAlert = AlertController.create(getContext(), this, getWindow());
}
/**
* 这就是最终设置的Title
*/
@Override
public void setTitle(CharSequence title) {
super.setTitle(title);
mAlert.setTitle(title);
}
/**
* 这就是最终设置的Message
*/
public void setMessage(CharSequence message) {
mAlert.setMessage(message);
}
/**
* 图标可以为
*/
public void setIcon(Drawable icon) {
mAlert.setIcon(icon);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mAlert.installContent();
}
//**********************Builder 为AlertDialog静态内部类 ************************
public static class Builder {
//存储 AlertDialog的各个参数 , icon title,message,button等等
private final AlertController.AlertParams P;
public Builder(Context context, int themeResId) {
P = new AlertController.AlertParams(new ContextThemeWrapper(
context, resolveDialogTheme(context, themeResId)));
}
// Builde采用了链式调用的方式,返回自身对象this
public Builder setTitle(@StringRes int titleId) {
P.mTitle = P.mContext.getText(titleId);
return this;
}
public Builder setMessage(CharSequence message) {
P.mMessage = message;
return this;
}
public Builder setIcon(Drawable icon) {
P.mIcon = icon;
return this;
}
/**
* 这里是create(),还有些是build()
* 1.该方法之前只是设置(存储)一系列的参数
* 2.create方法主要创建我们的Dialog对象,然后通过AlertParams将参数进行合并设置给我的Dialog,最终返回dialog对象。
*/
public AlertDialog create() {
// 1.创建我们的AlertDialog对象,
final AlertDialog dialog = new AlertDialog(P.mContext, 0, false);
// 2.将P中的参数进行合并应用到 dialog的mAlert对象中
P.apply(dialog.mAlert);
....
return dialog;
}
}
}
Dialog源码图解分析:
3.2.链式调用它只是我们采用的一种调用方式
链式调用体现形式:在调用方法的时候都会返回自身对象。
这个我在之前的Android6.0运行权限框架封装中有使用到
/**
* 链式调用使用
*/
public class PermissionHelper {
//1.需要传什么参数
//1.1 Object Fragment 或 Activity 1.2 int 请求码 1.3 String[] 需要请求权限数组
private Object mObject;
private int mRequestCode;
private String[] mRequestPermission;
/**
* 构造方法私有,防止外部new对象
*/
private PermissionHelper(Object object) {
this.mObject = object;
}
//1.我们经常使用的传递形式
public void requestPermission(Activity activity, int requestCode, String[] permissions) {
PermissionHelper.with(activity).requestCode(requestCode).requestPermission(permissions).request();
}
public void requestPermission(Fragment fragment, int requestCode, String[] permissions) {
PermissionHelper.with(fragment).requestCode(requestCode).requestPermission(permissions).request();
}
//2.使用链式调用形式
public static PermissionHelper with(Activity activity) {
return new PermissionHelper(activity);
}
//传Fragment
public static PermissionHelper with(Fragment fragment) {
return new PermissionHelper(fragment);
}
//传请求码,都会返回我们自身对象
public PermissionHelper requestCode(int requestCode) {
this.mRequestCode = requestCode;
return this;
}
//添加请求权限数组 String[] 都会返回我们自身对象
public PermissionHelper requestPermission(String... permissions) {
this.mRequestPermission = permissions;
return this;
}
//执行链式调用,执行我们的权限处理
public void request() {
//....代码省略
}
}
4. 手动实现一个导航栏
步骤:1.创建AbsNavigationBar我们的基类,利于后期的扩展
2.Builder设计模式的内部静态Builder类
3.存储Builder参数信息的类Params(今天就不写这个了,直接都写在Builder中)
4.创建我们AbsNavigationBar的实现类NavigationBar
/**
* NavigationBar的基类
*/
public class AbsNavigationBar<B extends AbsNavigationBar.Builder> implements INavigationBar{
private B mBuilder;
private View mNavigationBar;
/**
* 子类创建Navigation是会调用父类的
* 1.创建NavigationBar
* 2.添加到父容器的第一个位置
* 3.绑定参数
* @param mBuilder
*/
public AbsNavigationBar(B mBuilder) {
this.mBuilder = mBuilder;
createNavigationBar();
}
/**
* 1.创建NavigationBar
*/
@Override
public void createNavigationBar(){
//创建
mNavigationBar = LayoutInflater.from(mBuilder.mContext).inflate(mBuilder.mLayoutId,mBuilder.mParent,false);
//2.添加
attachParent(mNavigationBar,mBuilder.mParent);
//3.绑定参数
attachNavigationParams();
}
/**
* 2.添加到父容器的第一个位置
* @param navigationBar
* @param parent
*/
@Override
public void attachParent(View navigationBar, ViewGroup parent) {
parent.addView(navigationBar,0);
}
/**
* 3.绑定参数
*/
@Override
public void attachNavigationParams() {
//进行循环设置文本
Map<Integer,CharSequence> textMaps = mBuilder.mTextMaps;
for (Map.Entry<Integer,CharSequence> entry : textMaps.entrySet()){
TextView textView = findViewById(entry.getKey());
textView.setText(entry.getValue());
}
//进行循环设置点击事件
Map<Integer,View.OnClickListener> clickListenerMaps = mBuilder.mClickListenerMaps;
for (Map.Entry<Integer,View.OnClickListener> entry : clickListenerMaps.entrySet()){
View view = findViewById(entry.getKey());
view.setOnClickListener(entry.getValue());
}
}
public <T extends View> T findViewById(int viewId) {
return (T) mNavigationBar.findViewById(viewId);
}
/**
* 返回Builder
* @return
*/
public B getBuilder() {
return mBuilder;
}
/**
* 这次没有写保存数据的Params类,直接用Builder进行保存
* @param <B>
*/
public abstract static class Builder<B extends Builder> {
public Context mContext; //上下文
public int mLayoutId; //自定义布局文件
public ViewGroup mParent; //父容器
public Map<Integer,CharSequence> mTextMaps; //文本集合
public Map<Integer,View.OnClickListener> mClickListenerMaps; //点击事件集合
public Builder(Context mContext, int mLayoutId, ViewGroup mParent) {
this.mContext = mContext;
this.mLayoutId = mLayoutId;
this.mParent = mParent;
mTextMaps = new HashMap<>();
mClickListenerMaps = new HashMap<>();
}
/**
* 创建我们的NavigationBar
* @return
*/
public abstract AbsNavigationBar create();
public B setText(int viewId, String text){
mTextMaps.put(viewId,text);
return (B) this;
}
public B setOnClickListener(int viewId, View.OnClickListener clickListener){
mClickListenerMaps.put(viewId,clickListener);
return (B) this;
}
}
}
public interface INavigationBar {
/**
* 1.创建NavigationBar
*/
void createNavigationBar();
/**
* 2.添加到父容器的第一个位置
* @param navigationBar
* @param parent
*/
void attachParent(View navigationBar, ViewGroup parent);
/**
* 3.绑定参数
*/
void attachNavigationParams();
}
public class NavigationBar extends AbsNavigationBar {
public NavigationBar(Builder mBuilder) {
/**
* 调用父类的构造方法
* 1.创建NavigationBar
* 2.添加到父容器的第一个位置
* 3.绑定参数
*/
super(mBuilder);
}
public static class Builder extends AbsNavigationBar.Builder<NavigationBar.Builder>{
/**
* @param mContext 上下文
* @param mLayoutId 布局文件
* @param mParent 父容器(需要将我们的布局添加到哪里)
*/
public Builder(Context mContext, int mLayoutId, ViewGroup mParent) {
//调用父类的Builder,进行保存参数信息
super(mContext, mLayoutId, mParent);
}
/**
* 创建NavigationBar
* @return
*/
@Override
public NavigationBar create() {
return new NavigationBar(this);
}
}
}
调用:
//获取父容器
ViewGroup parent = findViewById(R.id.parent);
NavigationBar navigationBar = new NavigationBar.Builder(this,R.layout.navigation_bar,parent)
.setText(R.id.tv_back,"返回")
.setText(R.id.tv_title,"NavigationBar")
.setOnClickListener(R.id.tv_back,new View.OnClickListener(){
@Override
public void onClick(View v) {
finish();
}
})
.create();
也可以设置默认导航栏,我们不需要关心控件id,只需要在NavigationBar中添加该方法就可以。
public Builder(Context mContext, ViewGroup mParent) {
super(mContext, R.layout.default_navigation_bar, mParent);
}
5.总结
5.1 优点
1.有良好的封装性,用户完全不必知道内部组成细节,完美结合了Java的迪米特原则(最少知识原则)
2.容易扩展
5.2 缺点
1.内部变化复杂,会有很多的Builder对象,消耗内存。
6.UML图
