美文网首页
Android系统编程思想篇:建造者模式

Android系统编程思想篇:建造者模式

作者: 江湖再见2024 | 来源:发表于2017-03-23 15:21 被阅读41次

    Android系统编程思想篇:建造者模式

    关于作者

    郭孝星,程序员,吉他手,主要从事Android平台基础架构方面的工作,欢迎交流技术方面的问题,可以去我的Github提issue或者发邮件至guoxiaoxingse@163.com与我交流。

    文章目录:https://github.com/guoxiaoxing/android-open-source-project-analysis/blob/master/README.md

    建造者模式属于创建型模式的一种,它允许用户在不知道内部构建细节的情况下,可以更精细的控制对象的构造流程。该模式为了将构建复杂对象的
    过程和它的部件解耦,使构建的过程和部件的表示隔离开来。

    模式定义

    将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

    模式角色

    Product:产品的抽象类
    Builder:抽象Builder类,规范构建过程,一般由子类实现具体构建过程
    ConcreteBuilder:具体的Builder类
    Director:统一组装过程
    

    使用场景

    1 相同的方法,不同的执行顺序,产生不同的事件结果。
    2 多个部件或者零件,都可以装配到同一个对象中,但产生的运行结果又不相同。
    3 对象构建过程特别复杂,参数多,默认值也需要配置。
    

    优点

    1 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
    2 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。
    3 可以更加精细地控制产品的创建过程,将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
    4 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,方便系统扩展,符合“开闭原则”。
    

    缺点

    1 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
    2 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
    

    另外,以下情况可以做模式简化处理

    省略抽象建造者角色:如果系统中只需要一个具体建造者的话,可以省略掉抽象建造者。
    省略指挥者角色:在具体建造者只有一个的情况下,如果抽象建造者角色已经被省略掉,那么还可以省略指挥者角色,让Builder角色扮演指挥者与建造者双重角色。

    模式实现

    我们先来看看完整的建造者模式的实现

    抽象产品角色

    package com.guoxiaoxing.android.sdk.design.builder.whole;
    
    /**
     * 抽象产品角色,定位一类产品的特性与行为。
     * <p>
     * For more information, you can visit https://github.com/guoxiaoxing or contact me by
     * guoxiaoxingse@163.com
     *
     * @author guoxiaoxing
     * @since 2017/3/23 上午10:13
     */
    public abstract class AbstractProduct {
    
        private String board;
        private String display;
        private String os;
    
    
        public void setBoard(String board) {
            this.board = board;
        }
    
    
        public void setDisplay(String display) {
            this.display = display;
        }
    
    
        public abstract void setOs(String os);
    }
    

    具体产品角色

    package com.guoxiaoxing.android.sdk.design.builder.whole;
    
    /**
     * 具体产品角色,实现具体的产品特性与行为
     * <p>
     * For more information, you can visit https://github.com/guoxiaoxing or contact me by
     * guoxiaoxingse@163.com
     *
     * @author guoxiaoxing
     * @since 2017/3/23 上午10:06
     */
    public class RealProduct extends AbstractProduct {
    
        @Override
        public void setOs(String os) {
            os = "mac osx";
        }
    
        /**
         * 设置参数内部类,构建的时候先构建参数,完成后再创建实例并装配参数
         */
        public static class RealProductParams {
            public String board;
            public String display;
            public String os;
    
            public void applyParams(RealProduct product) {
                product.setBoard(board);
                product.setDisplay(display);
                product.setOs(os);
            }
        }
    }
    
    

    抽象建造者角色

    package com.guoxiaoxing.android.sdk.design.builder.whole;
    
    /**
     * 抽象建造者橘色,抽象构建行为,具体实现交由子类完成。
     * <p>
     * For more information, you can visit https://github.com/guoxiaoxing or contact me by
     * guoxiaoxingse@163.com
     *
     * @author guoxiaoxing
     * @since 2017/3/22 下午6:27
     */
    public abstract class AbstractBuilder {
    
        public abstract void setBoard(String board);
    
        public abstract void setDisplay(String display);
    
        public abstract void setOs(String os);
    }
    
    

    具体建造者角色

    package com.guoxiaoxing.android.sdk.design.builder.whole;
    
    /**
     * 具体建造者橘色,完成具体构建行为。一般持有产品的引用,并在build()方法中返回该引用。
     * <p>
     * For more information, you can visit https://github.com/guoxiaoxing or contact me by
     * guoxiaoxingse@163.com
     *
     * @author guoxiaoxing
     * @since 2017/3/23 上午10:11
     */
    public class RealBuilder extends AbstractBuilder {
    
        private RealProduct.RealProductParams productParams;
    
        public RealBuilder() {
            productParams = new RealProduct.RealProductParams();
        }
    
        @Override
        public void setBoard(String board) {
            productParams.board = board;
        }
    
        @Override
        public void setDisplay(String display) {
            productParams.display = display;
        }
    
        @Override
        public void setOs(String os) {
            productParams.os = os;
        }
    
        public RealProduct build() {
            RealProduct product = new RealProduct();
            productParams.applyParams(product);
            return product;
        }
    }
    
    

    指挥者角色

    package com.guoxiaoxing.android.sdk.design.builder.whole;
    
    /**
     * 指挥者角色,指挥多个建造者进行构建过程,当只有一个建造者时此角色可以省略。
     * <p>
     * For more information, you can visit https://github.com/guoxiaoxing or contact me by
     * guoxiaoxingse@163.com
     *
     * @author guoxiaoxing
     * @since 2017/3/23 上午10:18
     */
    public class Direactor {
    
        private AbstractBuilder builder;
    
        public void setBuilder(AbstractBuilder builder) {
            builder = builder;
        }
    
        private void buildProduct(String board, String display, String os) {
            builder.setBoard(board);
            builder.setDisplay(display);
            builder.setOs(os);
        }
    }
    
    

    日常编程中,我们的建造者通常只有一个,这个时候我们可以使用简化版的建造者模式

    package com.guoxiaoxing.android.sdk.design.builder.simple;
    
    /**
     * 如果系统中只需要一个具体建造者的话,可以省略掉抽象建造者。在具体建造者只有一个的情况下,如果抽象建造者角色已经被省略
     * 掉,那么还可以省略指挥者角色,让Builder角色扮演指挥者与建造者双重角色。
     * <p>
     * For more information, you can visit https://github.com/guoxiaoxing or contact me by
     * guoxiaoxingse@163.com
     *
     * @author guoxiaoxing
     * @since 2017/3/23 上午10:05
     */
    public class Product {
    
        public String board;
        public String display;
        public String os;
    
        class Builder {
    
            private Product product;
    
            public Builder() {
                product = new Product();
            }
    
            private String board;
            private String display;
            private String os;
    
            public void setBoard(String board) {
                product.board = board;
            }
    
            public void setDisplay(String display) {
                product.display = display;
            }
    
            public void setOs(String os) {
                product.os = os;
            }
        }
    
    }
    
    

    模式实践

    我们再来看看,我们平时见过的类库都有哪些建造者模式的具体实践。

    最为常见的莫过于AlertDialog了吧,在AlertDialog创建的过程中

    AlertDialog:内置Builder类,完成参数构建。

    AlertController:AlertDialog的代理类,完成具体的构建过程,内置AlertParams类,由Builder类传递过来的参数都会设置到AlertParams中。

    AlertDialog内部调用逻辑

    AlertController内部调用逻辑

    好,我们来看看AlertDialog的具体构建流程。

    1 调用Builder,构建参数。

    public class Demo{
        AlertDialog dialog = new AlertDialog.Builder(context)
            .setTitle("标题")
            .setMessage("消息")
            .create();
    }
    

    2 调用create()方法,创建AlertDialog实例,同时创建AlertController实例,Builder里的参数传递到了AlertParams里。

    public class AlertDialog extends Dialog implements DialogInterface {
    
        protected AlertDialog(Context context, boolean cancelable, OnCancelListener cancelListener) {
            super(context, com.android.internal.R.style.Theme_Dialog_Alert);
            setCancelable(cancelable);
            setOnCancelListener(cancelListener);
            mAlert = new AlertController(context, this, getWindow());
        }
    
        public AlertDialog create() {
            final AlertDialog dialog = new AlertDialog(P.mContext);
            P.apply(dialog.mAlert);
            dialog.setCancelable(P.mCancelable);
            dialog.setOnCancelListener(P.mOnCancelListener);
            if (P.mOnKeyListener != null) {
                dialog.setOnKeyListener(P.mOnKeyListener);
            }
            return dialog;
        }
        
    }
    

    3 调用apply()方法,执行Dialog的创建,设置按钮监听、文字等。

    public static class AlertParams {
    
        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 (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 cursor
            if ((mItems != null) || (mCursor != null) || (mAdapter != null)) {
                createListView(dialog);
            }
            if (mView != null) {
                if (mViewSpacingSpecified) {
                    dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight,
                            mViewSpacingBottom);
                } else {
                    dialog.setView(mView);
                }
            }
            
            /*
            dialog.setCancelable(mCancelable);
            dialog.setOnCancelListener(mOnCancelListener);
            if (mOnKeyListener != null) {
                dialog.setOnKeyListener(mOnKeyListener);
            }
            */
        }
        
    }
    

    以上便是AlertDialog德创建过程,可以看出综合使用了代理模式与建造者模式,当create()方法被调用时才创建出了对象,当apply()方法被
    调用的时候,才对对象进行属性设置。

    事实上,Builder在创建时它只创建了AlertParams的实例,并没有去创建AlertDialog,这种写法也是懒加载的一种体现,有利于节省资源。

    
    public static class Builder {
        
        private final AlertController.AlertParams P;
    
        public Builder(@NonNull Context context, @StyleRes int themeResId) {
            P = new AlertController.AlertParams(new ContextThemeWrapper(
                    context, resolveDialogTheme(context, themeResId)));
            mTheme = themeResId;
        }
    }
    
    

    以上便是Android系统中对建造者模式的应用,可以发现该实践中并没有抽象产品角色、指挥者角色,这是Android对建造者模式的一种简化,在后续的阅读源码过程中还会发现
    很多这样的简化与变通,并不是刻板的按照标准实现来做,这也正是编程的魅力所在:因地制宜,灵活多变。

    关于Dialog的进一步的内容以及WindowManager等相关原理,我们在这里不再进一步展开,后续的Android源码分析系列文章中会做进一步的详尽分析。

    相关文章

      网友评论

          本文标题:Android系统编程思想篇:建造者模式

          本文链接:https://www.haomeiwen.com/subject/eeaznttx.html