热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

当Android遇上设计模式之建造者(Builder)模式

转载请声明出处:https:blog.csdn.netAndrExpertarticledetails79665082众所周知,无论是大部分书籍还是博客,对设计模式的介

转载请声明出处:https://blog.csdn.net/AndrExpert/article/details/79665082

众所周知,无论是大部分书籍还是博客,对设计模式的介绍也仅表现在简单的java举例层面,虽然是看懂了,但是在开发的过程中就是不知道如何应用到项目中,时间久了也容易忘记。因此从今天开始,我计划从Android开发者角度写一些关于设计模式系列文章,希望通过这个系列的文章我们不仅能够理解这些设计模式的原理,更能够将其应用到我们的实际项目中。

一、Builder模式原理剖析

 Builder,译为”建造者”,我们可以很容易联想想到建筑工人建房子,其大概的流程由打地基、修建主体框架、装修等部分组成,且每个部分是由不同的建筑工人完成。虽说不同的建筑工人修建的风格不一样,但是只要按照上述的修建流程,就能够得到一栋完整的房子,这就是传说中的Builder模式!

1. Builder模式

 所谓建造者(Builder)模式,即将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。通俗一点理解,该模式是为了将构建复杂对象的过程和它的部件解耦,使用构建过程和部件都可以自由扩展,同时对外部(客户端)隐藏实现细节,客户端只需调用相同的构建过程,就可以得到不同的产品(对象)。
根据Builder模式理论,以上述建房子为例,房子为要构建的复杂对象(Product),”打地基-修建主体框架-装修”为构建过程(Director),”打地基”、”修建主体框架”、”装修”为构建对象的部件(Builder),可以看出构建过程与它的部件是相互独立的,而对于业主来说,他无法(无需)知道修建的整个流程和每个部分的具体细节,只需要事先告诉建筑工人哪里用什么材料或怎么设计,就可以得到他预想中的房子,并且每个部分建筑工人还可以加入他自己的idea,业主最终可以获取风格不同的房子。

2. UML类图

 根据Builder模式的定义,可以得到该模式的UML类图,有以下几个角色:

image
- (1) Builder:抽象Builder类,规定产品有那些部件,且部件的具体实现由其子类实现;
- (2) ConcreteBuilder:Builder子类,产品部件的具体实现;
- (3) Director:规定构建对象的过程;
- (4) Product:具体产品类;

 从UML类图可知,Builder和Director为聚合关系,Director类持有Builder的引用用于调用创建部件相关方法,而Builder是可以脱离Director而单独存在的,即它们是相互独立的;ConcreteBuilder和Builder为继承关系,前者继承于后者,且是后者的具体实现;ConcreteBuilder依赖于Product,ConcreteBuilder持有Product的引用才能完成其工作。

3. 时序图

image

二、Android项目实战

 在Android源码中,建造者(Builder)模式应用非常广泛,比较经典的就是AlertDialog内部实现,它就是通过Builder对象来组装Dialog的各个部分,如title、Message、Button等,将Dialog的构造和表示进行分离。因此,为了进一步理解Builder模式,我们在不看AlertDialog源码的情况下,尝试着根据Builder模式的原理来实现Dialog的创建。该项目的UML类图如下:

image

  • Builder,建造者抽象类。
    Builder类是建造者抽象类,该类规定了一个Dialog对象由图标、标题、内容、Button操作部件组成,并提供相关的抽象方法。代码如下:
/**Builder,建造一个Dialog对象的各个部分(部件)的抽象接口 * Created by jianddongguo on 2018/3/4. */
public abstract class Builder {
    // 图标
    public abstract void setIcon(int id);
    // 标题
    public abstract void setTitle(String title);
    // 内容
    public abstract void setMessage(String[] content);
    // 操作
    public abstract void setOnDialogClickListener(String confirmText,String cancelText,
                 Dialog.OnDialogClickListener listener);
    // 创建Dialog
    public abstract Dialog create();
}
  • AlertDialogBuilder,建造者具体类
    继承于Builder,用于创建一个Dialog对象各个组成部分的具体实现。比如我们需要创建的Dialog类型为提示对话框(AlertDialog),那么对话框的ContentView应该是一个TextView,再给该TextView设置文本。也就是说,随着创建Dialog类型不同,建造者具体类可以不同,比如我们需要创建一个列表对话框,那么ContentView应该是一个ListView,该类的名字改为ListDialogBuilder。
/** * Builder具体实现类,用于建造AlertDialog各部件 * Created by jianddongguo on 2018/3/4. */

public class AlertDialogBuilder extends Builder {
    private Dialog mDialog = new AlertDialog();
    private Context mContext;

    public AlertDialogBuilder(Context ctx) {
        mCOntext= ctx.getApplicationContext();
    }

    @Override
    public void setIcon(int id) {
        mDialog.setIcon(id);
    }

    @Override
    public void setTitle(String title) {
        mDialog.setTitle(title);
    }

    @Override
    public void setMessage(String[] content) {
        if (content != null || content.length > 0) {
            TextView tv = new TextView(mContext);
            tv.setText(content[0]);
            tv.setTextSize(16);
            mDialog.setContentView(tv);
        }
    }

    @Override
    public void setOnDialogClickListener(String confirmText, String cancelText, Dialog.OnDialogClickListener listener) {
        mDialog.setClickButton(confirmText, cancelText,listener);
    }


    @Override
    public Dialog create() {
        return mDialog;
    }
}
  • Director
    顾名思义,Director类是这整个故事的”导演”,虽然整个”剧组”的各个部分已经全部就绪,指挥棒(Builder)也已经交到Director手中,”导演”就会根据剧本安排好每个情节流程开始拍摄(construct),最终将所有情节根据预设好的剧本(流程)拍摄成电影(Dialog)。
/**Director层, * Created by jianddongguo on 2018/3/4. */
public class Director {
    private Builder mBuilder;

    public Director(Builder builder) {
        this.mBuilder = builder;
    }

    public void construct(int iconId, String title, String[] content, String confirmText, String cancelText,    Dialog.OnDialogClickListener listener) {
        mBuilder.setIcon(iconId);
        mBuilder.setTitle(title);
        mBuilder.setMessage(content);
        mBuilder.setOnDialogClickListener(confirmText,cancelText,listener);
    }
}
  • Dialog,产品(抽象)类
 /**Product层,产品类 * Created by jianddongguo on 2018/3/4. */
public abstract class Dialog {
    protected int mIcon;
    protected String mTitle;
    protected View mContentView;
    protected String mCancelText;
    protected String mConfirmText;
    protected Context mCtx;
    protected OnDialogClickListener mClickListener;

    public interface OnDialogClickListener {
        void onCancle();
        void onConfirm(String content);
    }

    public void setIcon(int icon) {
        this.mIcon = icon;
    }

    public void setTitle(String title) {
        this.mTitle = title;
    }


    public void setContentView(View view) {
        this.mCtx = view.getContext();
        this.mCOntentView= view;
    }

    public void setClickButton(String confirmText,String cancelText,OnDialogClickListener listener) {
        this.mCancelText = cancelText;
        this.mCOnfirmText= confirmText;
        this.mClickListener = listener;
    }

    public abstract void show();

    public abstract void dismiss();
}
  • AlertDialog
    继承于Dialog的产品类,它不会理会一个Dialog产品创建流程和每个部分的实现具体细节,只需要关注自己提供创建所需信息即可。代码如下:
/** * Dialog,产品具体实现类 * Created by jianddongguo on 2018/3/4. */

public class AlertDialog extends Dialog {
    private WindowManager mWindowManager;
    private View mRootView;

    @Override
    public void show() {
        // 使用WindowManager添加View到Window
        mWindowManager = (WindowManager) mCtx.getSystemService(Context.WINDOW_SERVICE);
        WindowManager.LayoutParams params = new WindowManager.LayoutParams(WindowManager.LayoutParams.MATCH_PARENT
                , WindowManager.LayoutParams.WRAP_CONTENT);
        params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
        params.type = WindowManager.LayoutParams.TYPE_PHONE;
        params.width = 800;
        params.height = 600;
        params.gravity = Gravity.CENTER;

        mRootView = LayoutInflater.from(mCtx).inflate(R.layout.layout_dialog, null);
        ImageView ivIcon = (ImageView) mRootView.findViewById(R.id.iv_dialog_icon);
        ivIcon.setImageResource(mIcon);
        TextView tvTitle = (TextView) mRootView.findViewById(R.id.tv_dialog_title);
        tvTitle.setText(mTitle);
        LinearLayout cOntentLayout= (LinearLayout) mRootView.findViewById(R.id.llayout_content);
        contentLayout.addView(mContentView);
        Button btnCancel = (Button) mRootView.findViewById(R.id.btn_cancel);
        btnCancel.setText(mCancelText);
        btnCancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mClickListener != null) {
                    mClickListener.onCancle();
                }
                dismiss();
            }
        });
        Button btnCOnfirm= (Button) mRootView.findViewById(R.id.btn_confirm);
        btnConfirm.setText(mConfirmText);
        btnConfirm.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mContentView instanceof TextView) {
                    if(mClickListener != null) {
                        mClickListener.onConfirm((String) ((TextView) mContentView).getText());
                    }
                }
                dismiss();
            }
        });

        mWindowManager.addView(mRootView, params);
    }

    @Override
    public void dismiss() {
        if(mWindowManager != null) {
            mWindowManager.removeView(mRootView);
        }
    }
}
  • 客户端调用(Activity)

    // 实例化Builder构建对象
    AlertDialogBuilder alertBuidler = new AlertDialogBuilder(this);
    // 指定构建流程,开始构建
    Director alertDirect = new Director(alertBuidler);
    alertDirect.construct(R.mipmap.ic_launcher, "AlertDialog标准版", new String[]{"这是标准版测试"},
                "确认", "取消", new Dialog.OnDialogClickListener() {
            @Override
            public void onCancle() {
                showToast("取消操作");
            }
    
            @Override
            public void onConfirm(String content) {
                showToast("确认,打印内容:"+content);
            }
     });
    // 得到产品
    alertBuidler.create().show();

 至此,一个标准的Builder模式流程实战就完成了。由于Builder模式构建过程与部件高度解耦的特点,我们可以再不修改Director类任何代码的情况下,通过创建不同的Buidler子类,来创建风格不同的Dialog。比如,我需要创建一个列表对话框(ListDialog),只需要创建一个ListDialogBuilder子类(继承于Builder)重新设计对话框组成部分就可以实现。当然,实际应用中可能不会按照这种标准的流程来使用Builder模式,会对其进行简化,通常Builder类会直接承担Director、Builder、ConcreteBuilder的责任。通过对上述项目重构,你会发现重构后的AlertDialog同Android源码中的实现结构是如此相似,代码如下:

public class AlertDialog2 {
    protected int mIcon;
    protected String mTitle;
    protected String mCancelText;
    protected String mConfirmText;
    protected Context mCtx;
    protected Dialog.OnDialogClickListener mClickListener;
    private View mContentView;
    private WindowManager mWindowManager;
    private View mRootView;

    public AlertDialog2(Context ctx) {
        this.mCtx = ctx.getApplicationContext();
    }

    public interface OnDialogClickListener {
        void onCancle();

        void onConfirm(String content);
    }

    public void setIcon(int icon) {
        this.mIcon = icon;
    }

    public void setTitle(String title) {
        this.mTitle = title;
    }


    public void setContentView(View view) {
        this.mCOntentView= view;
    }

    public void setClickButton(String confirmText, String cancelText, Dialog.OnDialogClickListener listener) {
        this.mCancelText = cancelText;
        this.mCOnfirmText= confirmText;
        this.mClickListener = listener;
    }

    public void show() {
        // 使用WindowManager添加View到Window
        mWindowManager = (WindowManager) mCtx.getSystemService(Context.WINDOW_SERVICE);
        WindowManager.LayoutParams params = new WindowManager.LayoutParams(WindowManager.LayoutParams.MATCH_PARENT
                , WindowManager.LayoutParams.WRAP_CONTENT);
        params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
        params.type = WindowManager.LayoutParams.TYPE_PHONE;
        params.width = 800;
        params.height = 600;
        params.gravity = Gravity.CENTER;

        mRootView = LayoutInflater.from(mCtx).inflate(R.layout.layout_dialog, null);
        ImageView ivIcon = (ImageView) mRootView.findViewById(R.id.iv_dialog_icon);
        ivIcon.setImageResource(mIcon);
        TextView tvTitle = (TextView) mRootView.findViewById(R.id.tv_dialog_title);
        tvTitle.setText(mTitle);
        LinearLayout cOntentLayout= (LinearLayout) mRootView.findViewById(R.id.llayout_content);
        contentLayout.addView(mContentView);
        Button btnCancel = (Button) mRootView.findViewById(R.id.btn_cancel);
        btnCancel.setText(mCancelText);
        btnCancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mClickListener != null) {
                    mClickListener.onCancle();
                }
                dismiss();
            }
        });
        Button btnCOnfirm= (Button) mRootView.findViewById(R.id.btn_confirm);
        btnConfirm.setText(mConfirmText);
        btnConfirm.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mContentView instanceof TextView) {
                    if (mClickListener != null) {
                        mClickListener.onConfirm((String) ((TextView) mContentView).getText());
                    }
                }
                dismiss();
            }
        });

        mWindowManager.addView(mRootView, params);
    }

    public void dismiss() {
        if (mWindowManager != null) {
            mWindowManager.removeView(mRootView);
        }
    }

    /** * 建造者,AlertDialog内部类 * 此处采用链式调用 */
    public static class Builder {
        private Context mCtx;
        private AlertDialog2 mDialog;

        public Builder(Context ctx) {
            this.mCtx = ctx;
            mDialog = new AlertDialog2(mCtx);
        }

        public Builder setIcon(int id) {
            mDialog.setIcon(id);
            return this;
        }

        public Builder setTitle(String title) {
            mDialog.setTitle(title);
            return this;
        }

        public Builder setMessage(String[] content) {
            if (content != null || content.length > 0) {
                TextView tv = new TextView(mCtx);
                tv.setText(content[0]);
                tv.setTextSize(16);
                mDialog.setContentView(tv);
            }
            return this;
        }

        public Builder setOnDialogClickListener(String confirmText, String cancelText, Dialog.OnDialogClickListener listener) {
            mDialog.setClickButton(confirmText, cancelText, listener);
            return this;
        }

        public AlertDialog2 create() {
            return mDialog;
        }
    }
}

最后说一句:
 Builder模式的最大优势是构建过程与组成解耦,两者可自由扩展,且对外部隐藏实现细节。如果需要实现不同的内部细节,只需要创建一个新的Builder子类即可。
基于此,常用的场合有:
 (1) 当初始化一个对象特别复杂,如参数多,且很多参数都具有默认值时;
 (2) 当建造过程相对稳定,但对象内部的构建通常面临着复杂的变化;

本项目Github地址
设计模式Android实战汇总


推荐阅读
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
  • JavaScript和HTML之间的交互是经由过程事宜完成的。事宜:文档或浏览器窗口中发作的一些特定的交互霎时。能够运用侦听器(或处置惩罚递次来预订事宜),以便事宜发作时实行相应的 ... [详细]
  • 本文介绍了pack布局管理器在Perl/Tk中的使用方法及注意事项。通过调用pack()方法,可以控制部件在显示窗口中的位置和大小。同时,本文还提到了在使用pack布局管理器时,应注意将部件分组以便在水平和垂直方向上进行堆放。此外,还介绍了使用Frame部件或Toplevel部件来组织部件在窗口内的方法。最后,本文强调了在使用pack布局管理器时,应避免在中间切换到grid布局管理器,以免造成混乱。 ... [详细]
  • 本文介绍了H5游戏性能优化和调试技巧,包括从问题表象出发进行优化、排除外部问题导致的卡顿、帧率设定、减少drawcall的方法、UI优化和图集渲染等八个理念。对于游戏程序员来说,解决游戏性能问题是一个关键的任务,本文提供了一些有用的参考价值。摘要长度为183字。 ... [详细]
  • Android源码中的Builder模式及其作用
    本文主要解释了什么是Builder模式以及其作用,并结合Android源码来分析Builder模式的实现。Builder模式是将产品的设计、表示和构建进行分离,通过引入建造者角色,简化了构建复杂产品的流程,并且使得产品的构建可以灵活适应变化。使用Builder模式可以解决开发者需要关注产品表示和构建步骤的问题,并且当构建流程发生变化时,无需修改代码即可适配新的构建流程。 ... [详细]
  • Java图形化计算器设计与实现
    本文介绍了使用Java编程语言设计和实现图形化计算器的方法。通过使用swing包和awt包中的组件,作者创建了一个具有按钮监听器和自定义界面尺寸和布局的计算器。文章还分享了在图形化界面设计中的一些心得体会。 ... [详细]
  • 详解Android  自定义UI模板设计_由浅入深
    学习安卓已有一些日子,前段时间整理了不少笔记,但是发现笔记不变分享与携带。今天开始整理博客,全当是与大家分享交流与自身学习理解的过程吧。结合最近在做的一个新闻类app及学习中的问题,一点一点整理一下, ... [详细]
  • 详解react组件通讯方式(多种)
    这篇文章主要介绍了详解react组件通讯方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着 ... [详细]
  • 装饰模式(Deocrator)     动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。    所谓装饰,就是一些对象给主题 ... [详细]
author-avatar
lingling2502852417
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有