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

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

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的重点。
  • 组合优于继承,这是对上面一条的一个补充,如果一个类中的一些细节具有差异化,值得考虑是否有封装的必要;----定义健康的消化系统和有病灶的消化系统(前提是已经定义消化系统)要比:忽略消化系统类、定义消化系统健康的人和消化系统有病灶的人要好;在消化系统中依赖胃、肠等类实例,通过他们是否有病灶要比:继承消化系统类、重写类特征要好
  • 不停的利用里氏代换原则思考集成体系是否合理。

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

持续更新,但有空再说

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,060评论 25 707
  • 星期一,我和姐姐和妹妹早上一起玩我们的不只到玩什么我们想呀想呀!姐姐想出了一个游戏是猫抓老鼠我不想我说我们玩你坐动...
    亲宝贝阅读 440评论 0 0
  • 立春·橘子洲(首稿) 戊戌年贰月·力图 冬去春初,湘江滔滔,橘子洲游。察万物复苏,挂新梢头;春江水暖,众赏舟游。群...
    力图说阅读 299评论 0 1
  • 文/兮木公子 图/兮木公子 男子汉 文/兮木公子 希望,我 是个男孩子 有帅气的短发,和 不会流泪的眼睛,还有 心...
    兮木公子阅读 248评论 0 2
  • 声声慢.行行立立(清照体) 文/蜀山倦客 行行立立。哭哭啼啼,孙孙子子戚戚。似暗还明昏暮,草衰风瑟。空山雨打岸...
    寺咀山主人阅读 728评论 2 7