Android设计模式学习之Builder模式

2024-06-24 05:32

本文主要是介绍Android设计模式学习之Builder模式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Android设计模式学习之观察者模式

建造者模式(Builder Pattern),是创造性模式之一,Builder 模式的目的则是为了将对象的构建与展示分离。Builder 模式是一步一步创建一个复杂对象的创建型模式,它允许用户在不知道内部构建细节的情况下,可以更精细地控制对象的构造流程。

模式的使用场景

1.相同的方法,不同的执行顺序,产生不同的事件结果时;
2.多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时;
3.产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,这个时候使用建造者模式非常合适。

UML类图

这里写图片描述

角色介绍

Product 产品类 : 产品的抽象类;
Builder : 抽象类, 规范产品的组建,一般是由子类实现具体的组件过程;
ConcreteBuilder : 具体的构建器;
Director : 统一组装过程(可省略)。

Builder模式简单实现

Builder模式最典型的例子,就是组装电脑的例子了
创建产品类

public class Computer {private String mCpu;private String mRam;public void setmCpu(String mCpu) {this.mCpu = mCpu;}public void setmRam(String mRam) {this.mRam = mRam;}
}

创建Builder类
组装电脑有一套组装方法的模版,就是一个抽象的Builder类,里面提供了安装CPU、内存的方法,以及组装成电脑的create方法:

public abstract class Builder {public abstract void buildCpu(String cpu);public abstract void buildRam(String ram);public abstract Computer create();
}

实现了抽象的Builder类,ComputerBuilder类用于组装电脑:

public class ComputerBuilder extends Builder {private Computer mComputer = new Computer();@Overridepublic void buildCpu(String cpu) {mComputer.setmCpu(cpu);}@Overridepublic void buildRam(String ram) {mComputer.setmRam(ram);}@Overridepublic Computer create() {return mComputer;}
}

用Dirextor指挥者类来统一组装过程

public class Direcror {Builder mBuild=null;public Direcror(Builder build){this.mBuild=build;}public Computer CreateComputer(String cpu,String mainboard,String ram){//规范建造流程this.mBuild.buildMainboard(mainboard);     this.mBuild.buildRam(ram);return mBuild.create();}
}

客户端调用指挥者类

最后商家用指挥者类组装电脑。我们只需要提供我们想要的CPU,内存就可以了,至于商家怎样组装的电脑我们无需知道。

public class CreatComputer {public static void main(String[]args){Builder mBuilder=new MoonComputerBuilder();Direcror mDirecror=new Direcror(mBuilder);//组装电脑mDirecror.CreateComputer("i5-3210","DDR4");}
}

Android源码中的Builder模式

在Android源码中,我们最常用到的Builder模式就是AlertDialog.Builder, 使用该Builder来构建复杂的AlertDialog对象。简单示例如下 :

   //显示基本的AlertDialog  private void showDialog(Context context) {  AlertDialog.Builder builder = new AlertDialog.Builder(context);  builder.setIcon(R.drawable.icon);  builder.setTitle("头部");  builder.setMessage("内容");  builder.setPositiveButton("Button1",  new DialogInterface.OnClickListener() {  public void onClick(DialogInterface dialog, int whichButton) {  setTitle("点击了对话框上的Button1");  }  }).setNeutralButton("Button2",  new DialogInterface.OnClickListener() {  public void onClick(DialogInterface dialog, int whichButton) {  setTitle("点击了对话框上的Button2");  }  });  builder.create().show();  // 构建AlertDialog, 并且显示}

下面我们看看AlertDialog的相关源码

// AlertDialog
public class AlertDialog extends Dialog implements DialogInterface {// Controller, 接受Builder成员变量P中的各个参数private AlertController mAlert;// 构造函数protected AlertDialog(Context context, int theme) {this(context, theme, true);}// 4 : 构造AlertDialogAlertDialog(Context context, int theme, boolean createContextWrapper) {super(context, resolveDialogTheme(context, theme), createContextWrapper);mWindow.alwaysReadCloseOnTouchAttr();mAlert = new AlertController(getContext(), this, getWindow());}// 实际上调用的是mAlert的setTitle方法@Overridepublic void setTitle(CharSequence title) {super.setTitle(title);mAlert.setTitle(title);}// 实际上调用的是mAlert的setCustomTitle方法public void setCustomTitle(View customTitleView) {mAlert.setCustomTitle(customTitleView);}public void setMessage(CharSequence message) {mAlert.setMessage(message);}// AlertDialog其他的代码省略// ************  Builder为AlertDialog的内部类   *******************public static class Builder {// 1 : 存储AlertDialog的各个参数, 例如title, message, icon等.private final AlertController.AlertParams P;// 属性省略/*** Constructor using a context for this builder and the {@link AlertDialog} it creates.*/public Builder(Context context) {this(context, resolveDialogTheme(context, 0));}public Builder(Context context, int theme) {P = new AlertController.AlertParams(new ContextThemeWrapper(context, resolveDialogTheme(context, theme)));mTheme = theme;}// Builder的其他代码省略 ......// 2 : 设置各种参数public Builder setTitle(CharSequence title) {P.mTitle = title;return this;}public Builder setMessage(CharSequence message) {P.mMessage = message;return this;}public Builder setIcon(int iconId) {P.mIconId = iconId;return this;}public Builder setPositiveButton(CharSequence text, final OnClickListener listener) {P.mPositiveButtonText = text;P.mPositiveButtonListener = listener;return this;}public Builder setView(View view) {P.mView = view;P.mViewSpacingSpecified = false;return this;}// 3 : 构建AlertDialog, 传递参数public AlertDialog create() {// 调用new AlertDialog构造对象, 并且将参数传递个体AlertDialog final AlertDialog dialog = new AlertDialog(P.mContext, mTheme, false);// 5 : 将P中的参数应用的dialog中的mAlert对象中P.apply(dialog.mAlert);dialog.setCancelable(P.mCancelable);if (P.mCancelable) {dialog.setCanceledOnTouchOutside(true);}dialog.setOnCancelListener(P.mOnCancelListener);if (P.mOnKeyListener != null) {dialog.setOnKeyListener(P.mOnKeyListener);}return dialog;}}}

可以看到,通过Builder来设置AlertDialog中的title, message, button等参数, 这些参数都存储在类型为AlertController.AlertParams的成员变量P中,AlertController.AlertParams中包含了与之对应的成员变量。在调用Builder类的create函数时才创建AlertDialog, 并且将Builder成员变量P中保存的参数应用到AlertDialog的mAlert对象中,即P.apply(dialog.mAlert)代码段。我们看看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);}if (mForceInverseBackground) {dialog.setInverseBackgroundForced(true);}// For a list, the client can either supply an array of items or an// adapter or a cursorif ((mItems != null) || (mCursor != null) || (mAdapter != null)) {createListView(dialog);}if (mView != null) {if (mViewSpacingSpecified) {dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight,mViewSpacingBottom);} else {dialog.setView(mView);}}}

实际上就是把P中的参数挨个的设置到AlertController中, 也就是AlertDialog中的mAlert对象。从AlertDialog的各个setter方法中我们也可以看到,实际上也都是调用了mAlert对应的setter方法。在这里,Builder同时扮演了上文中提到的builder、ConcreteBuilder、Director的角色,简化了Builder模式的设计。

在实际项目中的应用

我们可以采用系统已经提供好的Builder设计模式构建整个应用的万能Dialog,代码可以参考系统的AlertDialog

public static class Builder {private AlertController.AlertParams P;public Builder(Context context) {this(context, 0);}public Builder(Context context, int themeResId) {P = new AlertController.AlertParams();P.themeResId = themeResId;P.context = context;}public Builder setText(int viewId, CharSequence text) {P.textArray.put(viewId, text);return this;}public Builder setOnClickListener(int viewId, View.OnClickListener listener) {P.clickArray.put(viewId, listener);return this;}public Builder setContentView(int layoutId) {P.view = null;P.layoutId = layoutId;return this;}public Builder setContentView(View view) {P.layoutId = 0;P.view = view;return this;}/*** Sets whether the dialog is cancelable or not.  Default is true.** @return This Builder object to allow for chaining of calls to set methods*/public Builder setCancelable(boolean cancelable) {P.cancelable = cancelable;return this;}/*** Sets the callback that will be called if the dialog is canceled.* <p>* <p>Even in a cancelable dialog, the dialog may be dismissed for reasons other than* being canceled or one of the supplied choices being selected.* If you are interested in listening for all cases where the dialog is dismissed* and not just when it is canceled, see* {@link #setOnDismissListener(OnDismissListener) setOnDismissListener}.</p>** @return This Builder object to allow for chaining of calls to set methods* @see #setCancelable(boolean)* @see #setOnDismissListener(OnDismissListener)*/public Builder setOnCancelListener(OnCancelListener onCancelListener) {P.onCancelListener = onCancelListener;return this;}/*** Sets the callback that will be called when the dialog is dismissed for any reason.** @return This Builder object to allow for chaining of calls to set methods*/public Builder setOnDismissListener(OnDismissListener onDismissListener) {P.onDismissListener = onDismissListener;return this;}/*** Sets the callback that will be called if a key is dispatched to the dialog.** @return This Builder object to allow for chaining of calls to set methods*/public Builder setOnKeyListener(OnKeyListener onKeyListener) {P.onKeyListener = onKeyListener;return this;}/*** 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 BaseDialog create() {// Context has already been wrapped with the appropriate theme.final BaseDialog dialog = new BaseDialog(P.context, P.themeResId);P.apply(dialog.mAlert);dialog.setCancelable(P.cancelable);if (P.cancelable) {dialog.setCanceledOnTouchOutside(true);}dialog.setOnCancelListener(P.onCancelListener);dialog.setOnDismissListener(P.onDismissListener);if (P.onKeyListener != null) {dialog.setOnKeyListener(P.onKeyListener);}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 BaseDialog show() {final BaseDialog dialog = create();dialog.show();return dialog;}}
class AlertController {private DialogViewHelper mViewHelper;private BaseDialog mDialog;private Window mWindow;public AlertController(BaseDialog dialog, Window window) {mDialog = dialog;mWindow = window;}/*** 获取Dialog* @return*/public BaseDialog getDialog() {return mDialog;}/*** 获取window* @return*/public Window getWindow() {return mWindow;}public DialogViewHelper getViewHelper() {return mViewHelper;}/*** 设置View的辅助* @param viewHelper*/public void setDialogViewHelper(DialogViewHelper viewHelper) {this.mViewHelper = viewHelper;}/*** 设置文本* @param viewId* @param text*/public void setText(int viewId, CharSequence text) {mViewHelper.setText(viewId, text);}/*** 设置点击事件* @param viewId* @param listener*/public void setOnClickListener(int viewId, View.OnClickListener listener) {mViewHelper.setOnClickListener(viewId, listener);}/*** 通过id获取View* @param viewId* @param <T>* @return*/public <T extends View> T getView(int viewId) {return mViewHelper.getView(viewId);}
}

代码调用

  @Overridepublic void onClick(View v) {BaseDialog dialog = new BaseDialog.Builder(this).setContentView(R.layout.detail_dialog).fullWith().fromBottom(false).show();}

最后总结一下Buider模式的优缺点:

Builder 模式的优点:
1.将一个复杂对象的创建过程封装起来,使得客户端不必知道产品内部组成的细节;
2.允许对象通过多个步骤来创建,并且可以改变过程和选择需要的过程;
3.产品的实现可以被替换,因为客户端只看到一个抽象的接口;
创建者独立,容易扩展。
Builder 模式缺点:
1.会产生多余的 Builder 对象以及 Director 对象,消耗内存;
2.与工厂模式相比,采用 Builder 模式创建对象的客户,需要具备更多的领域知识。

这篇关于Android设计模式学习之Builder模式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1089289

相关文章

Android数据库Room的实际使用过程总结

《Android数据库Room的实际使用过程总结》这篇文章主要给大家介绍了关于Android数据库Room的实际使用过程,详细介绍了如何创建实体类、数据访问对象(DAO)和数据库抽象类,需要的朋友可以... 目录前言一、Room的基本使用1.项目配置2.创建实体类(Entity)3.创建数据访问对象(DAO

Android WebView的加载超时处理方案

《AndroidWebView的加载超时处理方案》在Android开发中,WebView是一个常用的组件,用于在应用中嵌入网页,然而,当网络状况不佳或页面加载过慢时,用户可能会遇到加载超时的问题,本... 目录引言一、WebView加载超时的原因二、加载超时处理方案1. 使用Handler和Timer进行超

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影