很多时候,我们去写东西的时候,都是闷头写,写到哪算哪,知道一些重构技巧的同学可能会在边写的时候遇到重复就提取出来成方法这样,但这样一开始是蛮好的,因为你自己写的东西,你都知道,但是写久了,你回头一看,卧槽,怎么这么多小方法?!时间久了,你再回来定位问题的时候,你就会发现方法太多,导致了一个很重要的问题就是,调用层次很混乱,你很难找到你想要的那个方法,总之就这个意思,有遇到这个问题的,自然心里有所体会。

我举个例子,比如说,产品需求让我们点下一个按钮,就发个网络请求,然后让UI显示“请求中”,数据回来的时候,再显示正确的UI。好,那么应该是这样的:

void onClick(View v) {
    switch (v.getId()) {
        case myButtonId:
            requestData();
            mButton.setText("请求中");
            break;
    }
}

void requestData() {
    // ...
}

void onRequestCallback(Data data) {
    // 回调来了,更新UI
    mButton.setText(data.toString());
}

首先,这样写是有问题的,我上一篇文章已经说过这样的问题了。问题在于就地去更新UI了。我们要用一个统一的地方来更新UI,于是这个要改写成:

Data mData = null;
boolean mIsRequesting = false;

void onClick(View v) {
    switch (v.getId()) {
        case myButtonId:
            requestData();
            break;
    }
}

void requestData() {
    mIsRequesting = true;
    // 发起请求
    updateView()    
}

void onRequestCallback(Data data) {
    // 回调来了,更新UI
    mIsRequesting = false;
    mData = data;
    updateView();
}

// 用统一的方法更新UI,并且按照View分类
void updateView() {
    if (mIsRequesting) {
        mButton.setText("请求中");
    } else {
        if (mData != null) {
            mButton.setText(data.toString());
        }
    }
}

用统一的方法去更新UI,这个说法是很通俗的,我想谁都可以听明白吧,其实这个事情,是可以更加抽象的,也就是说,它可以是一种抽象,适应别的情况,最近我刚好又做了一些重构的工作,已经成功领悟到这个抽象,那就是:一个方法只去做一件事情,更准确的说,只做一类事情,或者说,一类事情只丢到一个方法里去做。不知道这样说,大家听明白没有。

说一个Android里面的范本吧。想想onMeasure,onLayout,onDraw方法,单看onDraw方法,就是我上次说到的用统一的方法去更新UI,你三个方法一起看,你就明白了,一个方法只做一类事情,一类事情只丢到一个方法里去做

onLayout里面需要的数据,都在onMeasure里去做好了,存到成员变量上,onLayout直接用,onDraw需要的东西,前两个步骤都做好了,onDraw直接用成员变量上的东西。换句话说,UI只关心它要展示的数据源,而不关心数据源的改变

UI上的代码,至始至终都是直接拿着数据源来搞,而逻辑,改变数据源后,不要就地去更新UI,而应该调用更新UI的方法,这样,维护数据,展示数据,两个部分就得到了分离,维护性就是这样来的。

那么,我们写一个界面程序,每次都闷头写吗?有没有什么套路呢?我就在想,是不是有一种共性,有一种通用的做法,一种高屋建瓴的理解,可以在一开始就让代码漂亮呢。经过我仔细思考和总结后,有了如下的体会。大概是这样:

其实界面程序主要分为两个部分:处理数据处理UI

处理数据其实是很宽泛的,包括处理内存的数据,处理本地的数据,处理网络的数据。你存成员变量,一个列表排序,其实都是处理内存中的数据,下载文件,存配置到文件,其实就是处理本地的数据,发送网络请求,处理回包,其实就是处理网络的数据,处理回包其实是从网络到内存。

处理UI其实就是更新UI,或者动画什么的,动画什么的其实没关系,随便搞吧,没啥。更新UI其实是时刻关注着内存中的数据,内存中的数据就决定UI要展示成什么样。

接下来再思考一下,方法的层次/分类。这个就是说,方法应该分成哪些类别,哪些方法是有相同的特点。其实可以分为三类:

  • 时机/用户操作

  • 逻辑/处理数据

  • 更新UI

时机/用户操作,是整个界面程序的驱动力,整个界面能运作起来,完全是靠这些方法。比如

onCreate
onStart
onClick
onDestory
onCallback // 网络数据回调
onTimer    // timer,定时器

这一类方法,是第一层,最先被调用的方法。这些方法被调用后,里面就是我们的逻辑部分,或者数据部分,比如用户输入了什么要存起来啊,点了个按钮发起网络请求啊。这一层方法,我们可以自己命名,自己建,随便自己搞。比如:

requestData
handleData
doXXXXXX

最后一类,就是更新UI的方法,按我现在的理解,只需要一个就行了。

updateView

因此第一层方法,可以调用第二层方法,和第三层方法。第二层方法,可以调用第三层方法。按照这样的逻辑进行组织,逻辑就会清晰的多。

总结:

  • 一类工作放到一个方法中处理,一个方法只处理一类工作

  • 要注意整个界面中所有方法的层次,要有整体的把握,不要无谓的为了抽象,重用增加方法,应当减少方法,让整体逻辑和调用顺序清晰起来

  • 在实际操作之前,先整理下,有哪些网络请求,数据要如何处理,UI关心哪些数据,然后再着手去做


krosshj
152 声望16 粉丝

Developer, Gamer, Artist


引用和评论

0 条评论