为了账号安全,请及时绑定邮箱和手机立即绑定

我是如何在5 天内,完成 60 个类的核心模块的重构

代码是如何越写越烂的?

你是否经常听同事自嘲,“开始还想好好写,不知怎滴,后面越写越烂”?

[](http://img1.sycdn.imooc.com/5e78bd88000111d406050340.jpg)让我们来快速浏览一下重新导入前项目里吗?代码越写越烂,果真是个没有端倪,无法干预的魔咒玄学吗?!的代码是怎么写的。

保护无效initView(){ 
        PagerAdapter pagerAdapter =新PagerAdapter(); 
        viewPagerFix.setOffscreenPageLimit(4); 
        viewPagerFix.setAdapter(pagerAdapter);
        mFragmentBinding.tabLayout.setTabData(pagerAdapter.titles);
        mFragmentBinding.tabLayout.setOnTabSelectListener(新OnTabSelectListener(){ 
            @覆盖
            公共无效onTabSelect(INT位置){ 
                viewPagerFix.setCurrentItem(位置);
            } @覆盖公共无效onTabReselect(INT位置){}});viewPagerFix.addOnPageChangeListener(新的ViewPagerFix.OnPageChangeListener(){             @覆盖            公共无效onPageScrolled(INT位置,浮动positionOffset,INT positionOffsetPixels){









                KeyboardUtils.hideSoftInput(getActivity());
            } @覆盖公共无效使用onPageSelected(INT位置){mFragmentBinding.tabLayout.setCurrentTab(位置);是否(mViewModel.getXXXDetailTouchManager()。isZZBG()){zzbgPageSelected(位置); } else if(mViewModel.getXXXDetailTouchManager()。isYBJZ()){...} else {...}} @Override
















            public void onPageScrollStateChanged(int state){}});viewPagerFix.setCurrentItem(0); setOnClickListener(新View.OnClickListener()){@重写public void onClick(查看v){如果(mViewModel.getXXXDetailTouchManager()。isZZBG()){返回;} mViewModel.changeWyhcrwMajorState(); EventBus。.getDefault()后(新RefreshItemEventBus(mViewModel.getXXXDetailTouchManager()getCurrentWyhcrw())。);}}); }


















    私人void zzbgPageSelected(int位置){if(mScreenNum == 3){switch(position){case 0:case 1:mViewModel.removeAllArrows();如果(mAttachmentFragment!= null){mAttachmentFragment.hideClickHighLight(ALBUM_ALL); }; 情况2:mViewModel.showAllArrows();打破 缺省值:}}其他{...};

























    } / ** * viewPagerAdapter * /私有类PagerAdapter扩展FragmentPagerAdapter {字符串[]标题; PagerAdapter(){super(getChildFragmentManager());isZZBG()){如果(mScreenNum == 3){标题= getResources()。getStringArray(R.array.XXX_detail_tabs_for_no_tbjt); } else {title = getResources()。getStringArray(R.array.XXX_detail_tabs_for_zzbg);}} if(mViewModel.getXXXDetailTouchManager()。isYBJZ()){                 标题= getResources()。getStringArray(R.array.XXX_detail_tabs_for_ybjz);             } else {                 title = getResources()。getStringArray(R.array.XXX_detail_tabs);             }         }






























        @Override 
        public Fragment getItem(int position){ 
            if(mViewModel.getXXXDetailTouchManager()。isZZBG()){                 return zzbgGetItem(position);             } else if(mViewModel.getXXXDetailTouchManager()。isYBJZ()){                 开关(位置){                     情况0:                        if(mXXXTuBanPicFragment == null){                             mXXXTuBanPicFragment = XXXTuBanPicFragment.newInstance(                                    mViewModel.getUniqueCode(),                                    mViewModel.getXXXTouchManger()                            );                         }












                        return mXXXTuBanPicFragment;
                    case 1:
                        if (mRecordFragment == null) {
                            mRecordFragment = XXXRecordFragment.newInstance(mViewModel.getXXXDetailTouchManager());
                        }
                        return mRecordFragment;

                    default:
                        if (mAttachmentFragment == null) {
                            mAttachmentFragment = XXXAttachmentFragment.newInstance(
                                    mViewModel.getAttachments(),
                                    mViewModel.getOriginalAttachments(),
                                    mViewModel.getUniqueCode(),
                                    mViewModel.getXXXTouchManger(),
                                    XXXDetailFragment.this
                            );
                        }
                        return mAttachmentFragment;
                }
            } else {
               ...
            }
        }

        private Fragment zzbgGetItem(int position) {

            if (mScreenNum == 3) {

                switch (position) {
                    case 0:
                        if (mAttributeFragment == null) {
                            mAttributeFragment = XXXAttributeFragment.newInstance(
                                    mViewModel.getUniqueCode(),
                                    mViewModel.getXXXTouchManger()
                            );
                        }
                        return mAttributeFragment;
                    case 1:
                        if (mRecordFragment == null) {
                            mRecordFragment = XXXRecordFragment.newInstance(
                                    mViewModel.getXXXDetailTouchManager());
                        }
                        return mRecordFragment;
                    default:
                        if (mAttachmentFragment == null) {
                            mAttachmentFragment = XXXAttachmentFragment.newInstance(
                                    mViewModel.getAttachments(),
                                    mViewModel.getOriginalAttachments(),
                                    mViewModel.getUniqueCode(),
                                    mViewModel.getXXXTouchManger(),
                                    XXXDetailFragment.this
                            );
                        }
                        return mAttachmentFragment;
                }

            } else {
                ....
            }

        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            Object object = super.instantiateItem(container, position);
            if (mViewModel.getXXXDetailTouchManager().isZZBG()) {
                if (mScreenNum == 3) {

                    switch (position) {
                        case 0:
                            mAttributeFragment = (XXXAttributeFragment) object;
                            break;
                        case 1:
                            mRecordFragment = (XXXRecordFragment) object;
                            break;
                        default:
                            mAttachmentFragment = (XXXAttachmentFragment) object;
                            break;
                    }

                } else {
                    switch (position) {
                        case 0:
                            mRecordFragment = (XXXRecordFragment) object;
                            break;
                        default:
                            mAttachmentFragment = (XXXAttachmentFragment) object;
                            break;
                    }
                }

                return object;
            } else if (mViewModel.getXXXDetailTouchManager().isYBJZ()) {
                ...
            } else {
                switch (position) {
                    case 0:
                        mXXXTuBanPicFragment = (XXXTuBanPicFragment) object;
                        break;
                    case 1:
                        mAttributeFragment = (XXXAttributeFragment) object;
                        break;
                    case 2:
                        mRecordFragment = (XXXRecordFragment) object;
                        break;
                    default:
                        mAttachmentFragment = (XXXAttachmentFragment) object;
                        break;
                }
                return object;
            }
        }

        @Override
        public int getCount() {
            if (mViewModel != null) {
                if (mViewModel.getXXXDetailTouchManager().isZZBG()) {
                    if (mScreenNum == 3) {
                        return 3;
                    }
                    返回2; 
                } 
                如果(mViewModel.getXXXDetailTouchManager()。isYBJZ()){ 
                    返回3;
                } else { 
                    返回4;
                } 
            } 
            返回0; 
        }     } ``



(为保护隐私,模块类名已替换为“ XXX”)

可以看到,该主人当前服务于3个地区,每个地区对子页面的展示都有定制需求。

如果在其他情况下进行切换,则仅在乎功能实现的代码农就是这么写的。

一个地区50行,那要是10个地区呢?公司领导放话要支持全国100个乡镇地区!那100个地区呢??? !!!![!](http://img4.sycdn.imooc.com/5e78bd89000109c603510350。 jpg)**抽象,顺应的是“开闭原则” **



这是一帮对“抽象”无感的码农。

他们听到“抽象”,就像不爱锻炼的我听到父母,朋友劝我“健身”一样被动。(笑)

正如我并不真的理解健身的意义所在,他们也当抽象是“耳边风”。

“ 100个地区”这种,天然的就是用工厂模式来抽象和定制,这原本是一目了然,毫无疑问的事。

修正后的代码,主页抬头特意标注了警告。

/

  • 友情提示:本类涂有防腐药品,切勿触碰,切勿触碰,切勿触碰!
  • 地区定制功能,包括特色的布局等,请继承于 AbstractDetailChildFragmentManager 单独编写!
    */

public class XXXDetailFragment extends BaseFragment implements IResponse {
protected void initView() {
initViewPagerManager();
PagerAdapter pagerAdapter = new PagerAdapter();
viewPagerFix.setOffscreenPageLimit(4);
viewPagerFix.setAdapter(pagerAdapter);
mFragmentBinding.tabLayout.setTabData(pagerAdapter.titles);
mFragmentBinding.tabLayout.setOnTabSelectListener(new OnTabSelectListener() {
@Override
public void onTabSelect(int position) {
viewPagerFix.setCurrentItem(position);
}

        @Override
        public void onTabReselect(int position) {

        }
    });
    viewPagerFix.addOnPageChangeListener(new ViewPagerFix.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            KeyboardUtils.hideSoftInput(getActivity());
        }

        @Override
        public void onPageSelected(int position) {
            mFragmentBinding.tabLayout.setCurrentTab(position);
            mDetailChildFragmentManager.onPageSelected(position);
        }

        @Override
        public void onPageScrollStateChanged(int state) {

        }
    });
}

/**
 * viewPager适配器
 */
private class PagerAdapter extends FragmentPagerAdapter {

    String[] titles;

    PagerAdapter() {
        super(getChildFragmentManager());
        titles = mDetailChildFragmentManager.getTitles();
    }

    @Override
    public Fragment getItem(int position) {
        return mDetailChildFragmentManager.getItem(position);
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Object object = super.instantiateItem(container, position);
        return mDetailChildFragmentManager.instantiateItem(container, position, object);
    }

    @Override
    public int getCount() {
        return mDetailChildFragmentManager.getCount();
    }
}

}

**代码是如何剪不断理还乱的?**

听说过“代码耦合”和“解耦”的人很多,但真正理解这是怎么一回事的,恐怕只有你 ~


因为哪怕你不知,你也即将见证一位帅哥如何手把手带你解耦 ~

我们先来看下重构前的代码!

public interface XXXListNavigator {

void updateRecyclerView();

void showProgressDialog();

void dismissProgressDialog();

void updateListView();

void updateLayerWrapperList(List<LayerWrapper> list);

boolean isAnimationFinish();

void resetCount();

}

public class XXXListViewModel extends BaseViewModel {
public void multiAddOrRemove(ArrayList bsms, boolean isAdd) {
if (null != mNavigator) {
mNavigator.showProgressDialog();
}
if (null == mMultiAddOrRemoveUseCase) {
mMultiAddOrRemoveUseCase = new MultiAddOrRemoveUseCase();
}
mUseCaseHandler.execute(mMultiAddOrRemoveUseCase, new MultiAddOrRemoveUseCase.RequestValues(isAdd, bsms,
mLayerWrapperObservableField.get()),
new UseCase.UseCaseCallback<MultiAddOrRemoveUseCase.ResponseValue>() {
@Override
public void onSuccess(MultiAddOrRemoveUseCase.ResponseValue response) {
ToastUtils.showShort(getApplicationContext(), “操作成功”);
clearData();
loadData(true, true);
if (null != mNavigator) {
mNavigator.dismissProgressDialog();
}
}

                @Override
                public void onError() {
                    ToastUtils.showShort(getApplicationContext(), "操作失败");
                    if (null != mNavigator) {
                        mNavigator.dismissProgressDialog();
                    }
                }
            });
}

}

可以看到,UI 过度暴露了“处理 UI 逻辑所依赖的过程 API”,并在业务中直接干预了 UI 逻辑,这是典型的 MVP 写法,这造成了耦合。一旦 UI 的需求有变动,View 和 Presenter 的编写者都会受到牵连。

而且,职责过多造成了依赖过多,这个 Presenter 会因为过多的依赖,而越写越臃肿:受“破窗效应”的驱使,别的码农会因为此处已经有某个依赖,而不假思索的接着往下写。

**到底怎样才算解耦**

所谓解耦,是符合工程设计、符合设计模式原则的编码。

解耦的本质,我只说一遍:

**职责边界明确,职责边界明确,职责边界明确。**
![viabus_flow_flow.png](http://img3.sycdn.imooc.com/5e78bd890001c1bd09660481.jpg)
**符合单一职责原则:**
UI 的职责仅限于“展示”,也就是发送请求、处理 UI 逻辑。业务的职责仅限于“提供数据”,也就是接收请求、处理业务逻辑、响应结果数据。

**符合依赖倒置原则、最小知识原则:**
UI 不需要知道数据是经过怎样的周转得来的,它只需发送请求,并在拿到结果数据后,自己内部消化 UI 逻辑。业务只需处理数据并响应数据给 UI,它不需要知道 UI 会怎样使用数据,更无权干预。

综上,无论是 UI 还是业务,都不应过度暴露内部逻辑 API 而受控于人,**它们应只暴露请求 API,来响应外部的请求。过程逻辑应只在自己内部独立消化。**

public class XXXListBusinessProxy extends BaseBusiness implements IXXXListFragmentRequest {

@Override
public void multiAddOrRemove(final XXXListDTO dto) {
    handleRequest((e) -> {
            ...
            if (TextUtils.isEmpty(existBsms)) {
                sendMessage(e, new Result(XXXDataResultCode.XXX_LIST_FRAGMENT_MULTI_ADD_OR_REMOVE, false));
            } else {
                wyhcJgDBManager.insertAllTaskOfMine(existBsms, layersConfig);
                sendMessage(e, new Result(XXXDataResultCode.XXX_LIST_FRAGMENT_MULTI_ADD_OR_REMOVE, true));
            }
            return null;
    });
}

@Override
public void refreshPatternOfXXXList(final XXXListDTO dto) {
    handleRequest((e) -> {
            ...
            count.setMyXXXCount(wyhcJgDBManager.getMyXXXPatternCount());
            return new Result(XXXDataResultCode.XXX_LIST_FRAGMENT_REFRESH_COUNT, count);
    });
}

@Override
public void changeXXXPatternOfMine(final XXXListDTO dto) {
    handleRequest((e) -> {
            if (toMine) {
                ...
            } else {
                ...     
                sendMessage(e, new Result(XXXDataResultCode.XXX_LIST_FRAGMENT_GET_ALL_PATTERN_OF_MINE, count));
            }
            return null;
    });
}

}

public class XXXListFragment extends BaseFragment implements IResponse {

XXXBus.XXX().queryList(mDto);

XXXBus.XXX().multiAddOrRemove(mDto);

XXXBus.XXX().queryPattern(mDto);

...

@Override
public void onResult(Result testResult) {
    String code = (String) testResult.getResultCode();
    switch (code) {
        case XXXDataResultCode.XXX_LIST_FRAGMENT_REFRESH_LIST:
            updateRecyclerView((List<Wyhcrw>) testResult.getResultObject());
            if (isNeedUpdateCount()) {
                ...
            } else {
                finishLoading();
            }
            break;
        case XXXDataResultCode.XXX_LIST_FRAGMENT_MULTI_ADD_OR_REMOVE:
            if ((boolean) testResult.getResultObject()) {
                loadData(true, true);
            } else {
                ToastUtils.showShort(getContext(), "操作失败");
            }
            dismissProgressDialog();
            break;
        case XXXDataResultCode.XXX_LIST_FRAGMENT_REFRESH_PATTERN:
            ...
            break;
        default:
    }
}

}

**解耦有什么好处?**

解耦的好处,福特最有话语权。

100 多年前,福特发明了世界上第一条流水线,让工人职责边界明确,从而得以分工和专注各自领域。

原先装配一辆车需 700 小时,通过流水线分工后,平均一辆 12.5 小时,这使得生产效率提升了近 60 倍!
![](http://img2.sycdn.imooc.com/5e78bd8a00011c7f04950225.jpg)
软件工程同理。

由于 UI 和业务职责边界明确,且相互通过接口通信,使得 UI 和业务的编写者能够真正的分工。

写 UI 的人,不会被业务的编写打断,他可以一气呵成的写自己的 UI。写业务的人,同样不会被打断,他可以专注于业务逻辑、数据结构和算法的优化。

写 UI 和写业务的人,都可以自己实现接口,去独立的完成单元测试,完全不必依赖和等候对方的实现。

最后,在职责边界明确的情况下,UI 就算写 100 个 UI 逻辑,那也是 UI,业务就算写 100 个业务,那也是业务,纯种,所以不会杂乱,何况我们还可以借助“接口隔离原则”继续往下分工!

**总结**

综上,本文介绍了两个重构思路:

**1.顺应开闭原则,对定制化功能进行抽象。**

**2.顺应单一职能,最小知识,依赖倒置原则,让任务边界明确,防止代码交换。**看完这篇文章,如你觉得有所收获和启发,请不吝点赞,你的点赞就是对我最大的支持!本次项目改造用到的,符合设计模式原则的viabus架构,已在GitHub开源:GitHub:KunMinX / android-viabus-architecture








点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
Python工程师
手记
粉丝
21
获赞与收藏
151

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消