美文网首页Android 设计模式Android知识Android开发
Jianwoo中的设计模式(3) — Builder模式

Jianwoo中的设计模式(3) — Builder模式

作者: 狗蛋哥 | 来源:发表于2017-06-11 16:35 被阅读30次

    前言

    高仿Pinterest交互的实现思路这篇文章中,其实对于封装后的入口类,是需要提供很多参数的,那对于需要设置很多参数的类,我们如果全部在方法中提供set方法来设置,那将会显得非常凌乱,这个时候应该要将提供参数的方法封装起来作为一个单独的类,然后供给需要这些参数的产品类,这样可以统一提供参数入口,让这个本来需要参数就多的类减少与外部的调用关系,即方便日后扩展自身功能又不影响到与外部的调用

    简物中的Builder模式

    高仿Pinterest交互入口类在没有使用Builder模式是怎么传参的呢?以下为部分代码

    public class PinterestSelector implements PinterestSelectorContent.OnTouchToSelectorListener, PinterestSelectorContent.OnItemSelectListener{
    
        int mScrollViewId;
        View mView;
        View mItemLayout;
        boolean onlyShowItemLayout;
        String mShowItemViewBackgroundColor;
        List<ITouchView> mITouchViews;
    
        ....
    
        private PinterestSelector(){
        }
    
        /**
         * 该方法调用不存在线程安全问题
         */
        public static PinterestSelector newInstance(){
            if(instance == null){
                instance = new PinterestSelector();
            }
            return instance;
        }
    
        public PinterestSelector scroll(int scrollviewId){
            this.mScrollViewId = scrollviewId;
            return this;
        }
    
        /**
         * 设置长按item时候周边的背景色
         * eg: #f2ffffff
         * @param color
         * @return
         */
        public PinterestSelector backgroundColor(String color){
            this.mShowItemViewBackgroundColor = color;
            return this;
        }
    
        public PinterestSelector show(View itemLayout){
            this.mItemLayout = itemLayout;
            this.onlyShowItemLayout = true;
            return this;
        }
    
        public PinterestSelector addITouchView(ITouchView iTouchView){
            if(mITouchViews == null){
                mITouchViews = new ArrayList<>();
            }
            this.mITouchViews.add(iTouchView);
            return this;
        }
    
        public PinterestSelector addITouchViews(List<ITouchView> iTouchViews){
            this.mITouchViews.clear();
            this.mITouchViews.addAll(iTouchViews);
            return this;
        }
    
        ....
    
    }
    

    调用方式为

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if(!v.isClickable()){
                return false;
            }
            PinterestSelector.newInstance().
                    show((View)v.getParent()).
                    scroll(getBindId()).
                    backgroundColor("#f0ffffff").
                    addITouchView(new ShoppingCartPinterestViewFactory().create()).
                    addITouchView(new LikePinterestViewFactory().withLike(getAdapter().getItem(position).isLike()).create()).
                    setOnLongClickListener(new PinterestSelector.OnLongClickListener() {
                    @Override
                    public void onLongClick(View v) {
                        handleLongClick();
                    }
                    }).
                    setOnCancelListener(new PinterestSelector.OnCancelListener() {
                    @Override
                    public void onCancel() {
                    }
    
                    @Override
                    public void onSyncCancel() {
                        handleSyncCancel();
                    }
                    }).
                    setOnItemSelectListener(new PinterestSelector.OnItemSelectListener() {
                        @Override
                        public void onItemSelect(int index) {
                            switch (index){
                                case IPinterestView.LIKE:
                                    handleCollectSelect();
                                    break;
                                case IPinterestView.CART:
                                    handleSlideToCart();
                                    break;
                            }
                        }
                    }).
                    onTouch(v, event);
    }
    

    其实也是链式编程调用形式,但这样有什么弊端呢,我需要在PinterestSelector产品类维护大量供外界调用的方法,如果哪个函数中赋值变量的地方需要修改,那大量调用这个类的地方都要修改,这不是我所希望的,我希望在我维护PinterestSelector产品的时候不因为内部功能的修改而牵扯到外部类的调用,类与类之间调用关系越密切,其耦合度约会越来越大,我们学习编程的时候总是听到什么“低耦合、高内聚”,无论是做什么功能,各个模块之间的耦合度应该尽量降低,六大设计原则中有一条叫“迪米特法则”,另一种叫法叫“最少知道原则”说的就是这个意思
    那对于这种情况我们应该怎么做呢,我们应该单独建一个类,作为建造者类提供外界设置参数,然后将设置好的参数“打包”给产品类,这样外界与产品之间的供给关系就交给建造者去完成了,产品做了什么内部修改,都不影响参数的传递,如果后面有什么新的参数增加,那也只需要在建造者内添加即可,打包的入口不用做任何修改,添加赋值即可
    那我们应该这样修改代码:

    public class PinterestSelector implements PinterestSelectorContent.OnTouchToSelectorListener, PinterestSelectorContent.OnItemSelectListener{
    
        int mScrollViewId;
        View mView;
        View mItemLayout;
        boolean onlyShowItemLayout;
        String mShowItemViewBackgroundColor;
        List<ITouchView> mITouchViews;
    
        ....
    
        private PinterestSelector(){
        }
    
        /**
         * 该方法调用不存在线程安全问题
         */
        public static PinterestSelector newInstance(){
            if(instance == null){
                instance = new PinterestSelector();
            }
            return instance;
        }
    
        public PinterestSelector with(Builder builder){
            this.mItemLayout = builder.mItemView;
            this.mScrollViewId = builder.mScrollViewId;
            this.mShowItemViewBackgroundColor = builder.mColor;
            this.mITouchViews = builder.mITouchViews;
            this.onLongClickListener = builder.mOnLongClickListener;
            this.onCancelListener = builder.mOnCancelListener;
            this.onItemSelectListener = builder.mOnItemSelectListener;
            this.mDialogMode = builder.mDialogMode;
            return this;
        }
    
        public static class Builder {
    
            /**
             * Dialog模式
             */
            private boolean mDialogMode;
    
            /**
             * 空白部分之外要显示的View
             */
            private View mItemView;
    
            /**
             * 当前选中view外层可滚动的view(如果没有就是Activity)
             */
            private int mScrollViewId;
    
            /**
             * 不显示区域背景色
             */
            private String mColor;
    
            /**
             * TouchViews
             */
            private List<IPinterestView> mITouchViews;
    
            /**
             * 长按监听
             */
            private PinterestSelector.OnLongClickListener mOnLongClickListener;
    
            /**
             * 选择监听
             */
            private OnItemSelectListener mOnItemSelectListener;
    
            /**
             * 退出监听
             */
            private OnCancelListener mOnCancelListener;
    
            public Builder(){
                mITouchViews = new ArrayList<>();
            }
    
            /**
             * 空白部分外部的view
             * @param itemView
             */
            public Builder show(View itemView){
                this.mItemView = itemView;
                return this;
            }
    
            public Builder scroll(int viewId){
                this.mScrollViewId = viewId;
                return this;
            }
    
            public Builder backgroundColor(String color){
                this.mColor = color;
                return this;
            }
    
            public Builder addITouchView(IPinterestView iTouchView){
                if(mITouchViews != null){
                    mITouchViews.add(iTouchView);
                }
                return this;
            }
    
            public Builder setOnLongClickListener(OnLongClickListener onLongClickListener){
                this.mOnLongClickListener = onLongClickListener;
                return this;
            }
    
            public Builder setOnCancelListener(OnCancelListener onCancelListener){
                this.mOnCancelListener = onCancelListener;
                return this;
            }
    
            public Builder setOnItemSelectListener(PinterestSelector.OnItemSelectListener onItemSelectListener){
                this.mOnItemSelectListener = onItemSelectListener;
                return this;
            }
    
            public Builder dialogMode() {
                this.mDialogMode = true;
                return this;
            }
    
            public PinterestSelector create(){
                return PinterestSelector.newInstance().with(this);
            }
    
            ....
    
        }
    
    

    那外部的调用就变成这样了

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if(!v.isClickable()){
                return false;
            }
            new PinterestSelector.Builder()
                    .show((View)v.getParent())
                    .scroll(getBindId())
                    .backgroundColor("#f0ffffff")
                    .dialogMode()
                    .addITouchView(new ShoppingCartPinterestViewFactory().create())
                    .addITouchView(new LikePinterestViewFactory().withLike(getAdapter().getItem(position).isLike()).create())
                    .setOnLongClickListener(new PinterestSelector.OnLongClickListener() {
                        @Override
                        public void onLongClick(View v) {
                            handleLongClick();
                        }
                    })
                    .setOnCancelListener(new PinterestSelector.OnCancelListener() {
                        @Override
                        public void onCancel() {
                        }
    
                        @Override
                        public void onSyncCancel() {
                            handleSyncCancel();
                        }
                    })
                    .setOnItemSelectListener(new PinterestSelector.OnItemSelectListener() {
                        @Override
                        public void onItemSelect(int index) {
                            switch (index){
                                case IPinterestView.LIKE:
                                    handleCollectSelect();
                                    break;
                                case IPinterestView.CART:
                                    handleSlideToCart();
                                    break;
                            }
                        }
                    })
                    .create()
                    .onTouch(v, event);
    
            return super.onTouch(v, event);
        }
    

    这样一修改是不是觉得这个功能的两个模块非常清晰,传参的模块只负责接收外部类提供的参数,而产品类只需要给参数赋值并且做自己的产品实现,其它的与外界没有任何关联,这就是Builder模式的巧妙之处!
    Android中有什么地方用到了Builder模式呢,最常见的就是AlertDialog,在AlertDialog使用中我们这样写过(以下代码来自网络

    protected void showDialog() {
       AlertDialog.Builder builder = new Builder(Main.this);
       builder.setMessage("确认退出吗?");
       builder.setTitle("提示");
       builder.setPositiveButton("确认", new OnClickListener() {
           @Override
           public void onClick(DialogInterface dialog, int which) {
               dialog.dismiss();
               Main.this.finish();
           }
       });
       builder.setNegativeButton("取消", new OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
       });
       builder.create().show();
    }
    

    还有OkHttp的创建也用到了Builder模式

    OkHttpClient okHttpClient = new OkHttpClient.Builder()
                     .cache(getCache()) 
                     .addInterceptor(new HttpCacheInterceptor())
                     .addInterceptor(new LogInterceptor())
                     .addNetworkInterceptor(new HttpRequestInterceptor()) 
                     .build();
    

    好了,现在你对Builder(建造者)模式应该也有一个大概的了解了,如果你喜欢这篇文章,那请不要吝啬给个like吧!

    相关文章

      网友评论

        本文标题:Jianwoo中的设计模式(3) — Builder模式

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