美文网首页
leobert重构代码二三事--一.可怕的低级代码

leobert重构代码二三事--一.可怕的低级代码

作者: leobert | 来源:发表于2018-06-13 01:20 被阅读66次

    接下来会在简书演绎一些代码重构以及架构设计上的小故事,毫不要脸的用作者自身故事改编

    leobert是一位从事Android开发的IT engineer,机缘巧合之下来到了motorfans和一群有梦想的人一起奋斗。

    leobert不仅仅对Android客户端开发有过硬的基础,对项目管理,产品运营,架构设计都有一定的了解,在来公司之前,leobert对motorfans做了充分的分析,并对项目实现做了一定的预测。直到leobert真正接触到代码资产。噩梦开始了。

    无意于攻击项目最初始的开发者Tony,Tony copy了一部分以前项目中的框架,并设计了一部分抽象体系作为基础框架。

    按照经验来说,如果公司高薪聘请了一位架构师负责架构,并将他与实际生产隔离,那么很有可能造就空中楼阁式的架构。而Tony是实际开发者,团队leader,按理来说应该不会出现这样的情况,设计的框架应该是接地气的

    然而真实情况是这样的

    • Tony创建了一个庞大的助手类类,将各种可能(或者是他觉得可能)有用的代码全部放在了助手类中,并且没有unit test。
    • Tony创建了一个无比复杂的activity基类,他具有这样的特性:
      • 创建布局
      • 支持自定义布局
      • 绑定且必须绑定一个内容fragment

      该fragment具有以下特性:

      • 具有页面刷新功能和到底部加载功能
      • 通过模板调用刷新和加载的webservice
      • 通过模板创建列表等集合类型视图的子view
      • 以及一些其他零碎的UI交互

    除此之外,就没了!

    这带来的问题就比较明显了,我们说OOP的三大基本特征:“封装”,“继承”,“多态”;从这一点来看的话,Tony是在尝试处理封装,但是Tony对封装的理解可能有点畸化,先不谈MVP、MVC、MVVM等架构概念,Tony所定义的主要角色只有这些:

    • 各种自定义View控件、Dialog等
    • Activity页面
    • Fragment页面
    • 包含各种可能重复使用到的代码段的Utility

    封装的目的是:把客观事物封装成抽象的类,并且类可以把自己的数据、方法只让可信的类或者类对象操作,对不可信的进行隐藏。
    但是Tony所做的工作就是在描述一系列的客观页面事物,并且并没有任何的对象“配合”概念,即根本没有真正使用到“组合”,他的Activity、Fragment几乎对一切都知晓、对一切特定领域的行为细节无所不知,他们知道DB是怎么做CURD的,他们知道WebAPI请求中各种细节对View所带来的影响,甚至可以说如果没有其他基础项目的支持,这些类还会知晓Http三次握手、断开连接等等等等。

    再说继承,Tony的代码中,继承体系非常的薄,只有两代:BaseXXX 和 具体的ABCXXX
    举个实际例子:
    Tony定义且只定义了两个关于Fragment的抽象基类:

    • BasePtrFragment,顾名思义,这是一个抽象Fragment、具有下拉、上拉的行为操作。如果具体的页面不允许存在上拉下拉操作就通过覆写方法来禁止。实际情景中直接实现它的子类并没几个。
    • 继承BasePTRFragment的BasePTRListFragment,更多的都是该类的子类。

    那么可以判断:Tony认为在描述人类的时候,并不需要定义:Human、Male、Female三个类,只需要定义Human,并且存在成员方法boolean isMale()就可以了,当需要定义Boy这样的类的时候,直接覆写方法就行了。我并不能说这是错的,但这是非常不合适的。当然这不是最致命的。

    最致命的还在于封装,我怀疑Tony在自觉继承中不需要定义Male,Female并不能说是错的这一点上进一步认为自己的封装已经成功了。

    我们还以Human为例子,我们忽略掉医学、生物学的严谨知识系统,可能有这样一层基类XXXXAnimal,它依赖了消化系统,传入一些食物(肉类、植物类)得到脂肪、蛋白质等产物(忽略掉一系列不知道、忘掉的知识),在Human中,我们注入的依赖(具体的消化系统)和牛类中注入的会有所区别(牛不止一个胃);哪怕忽略掉此处的物种差异,毕竟Tony认为一个Fragment都是有上拉下拉的、如果哪个类不具备该属性覆写成员方法就行。OK进一步往下思考人的消化系统也是有所差别的,成人能够接受的食物和一个月的baby是不一致的。那么是在Human中依赖一个HumanDigestiveSystem呢还是直接描述一下消化系统的具体细节呢?

    我猜这时候诸位一定毫不犹豫的选择依赖一个HumanDigestiveSystem,毕竟已经拆解的很赤裸裸了,而且消化系统的细节太复杂了,想脱离对象直接写也太TMD开玩笑了。

    而Tony呢?他在实际生产中选择的是在Human中直接写消化系统的细节,以BasePTRListFragment为例,他在其中描述了一些内容,我抽取重点:

    • 模板方法-获取页面刷新接口地址,http方法行为
    • 模板方法-序列化接口返回的数据、返回一个List数据集
    • 模板方法-获取列表控件adapter
    • webapi调用的callback实现类,解析数据见2,解析到的数据填充到adapter
    • 下拉的事件回调中调用一个类似如下伪代码的方法
    protected void refreshPage() {
    String method = getApiInvokeMethod();
    String url = getApiUrl();
    Map<String,Object> params = getApiParams();
    WebApi.request(method,url,new Callback() {
          void onSuccess(byte[] res) {
               boolean succsee= 解析res取出基本json结构中的返回码判断成功失败;
                if(success){
                    List<?> data =  parseResponse(res)
                    adapter.resetData(data);
                    .....
                } else {
                    提示错误信息;
                    显示空视图
                }
          };
          void onFailure(int httpCode,byte[] res) {
                  ToastUtils.httpFailure(httpCode);
                  onRefreshFailure();//错误视图
          };
    })
    }
    

    等等。

    看起来这个抽象类描述了下拉刷新式列表页面的数据刷新流程。但如果他就此收手,那么一切都还是可接受的。实际情况中绝大多数页面下拉不仅仅是牵涉一个接口。

    于是Tony通过覆写refreshPage方法,除了callSuper,还将额外要请求的接口细节全部写一遍。我的天、我已经不想再回忆那种代码了。

    那是一个描述不了实际情况的抽象Fragment,具有3k行代码;
    那是一系列重写父类的具体行为的子类,重写的内容是模板方法过程;
    简而言之就是拿一个并不合适的BasePTRListFragment强行做基类

    哪怕退一步说,就算是那种只有一个web接口的页面,我们真的特别需要这个抽象类吗?或许吧,有一个大约100行的抽象类描述一下过程细节也就足够了。

    实在是太晚了,我也不想再去描述哪些令人懊恼的代码了,文末给出Leobert的一些经验:

    • 系统最初的设计、不一定能够直接面面俱到,也不一定要面面俱到,但是重点一定要明确;----定义好消化系统类(先忽略掉食道胃小肠大肠等),表现出Human可以通过消化系统进食、消化的特征即可(同理还有运动系统、循环系统等),不要直接在Human中尝试写消化的过程细节(模板方法),这不是描述Human的重点。
    • 组合优于继承,这是对上面一条的一个补充,如果一个类中的一些细节具有差异化,值得考虑是否有封装的必要;----定义健康的消化系统和有病灶的消化系统(前提是已经定义消化系统)要比:忽略消化系统类、定义消化系统健康的人和消化系统有病灶的人要好;在消化系统中依赖胃、肠等类实例,通过他们是否有病灶要比:继承消化系统类、重写类特征要好
    • 不停的利用里氏代换原则思考集成体系是否合理。

    考虑到本篇所介绍的项目背景,其他内容不强行往上靠。

    持续更新,但有空再说

    相关文章

      网友评论

          本文标题:leobert重构代码二三事--一.可怕的低级代码

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