Kata04:重构开始

Kata04地址

第四个Kata,名字叫“Data Munging”,一看Data大家就知道了,这次是要处理数据。

任务分三步:

  • 处理weather.dat,找到温差最小的一天,第二列和第三列分别对应每天的最高温度和最低温度
  • 处理football.dat,找到进球数和丢球数之差最小的一只球队,“-”符号左侧是进球数,右侧是丢球数
  • 重构上面两步的代码

输入数据

我知道你们肯定懒得去点数据链接,所以我直接把数据贴在这里

weather.dat:

  Dy MxT   MnT   AvT   HDDay  AvDP 1HrP TPcpn WxType PDir AvSp Dir MxS SkyC MxR MnR AvSLP

   1  88    59    74          53.8       0.00 F       280  9.6 270  17  1.6  93 23 1004.5
   2  79    63    71          46.5       0.00         330  8.7 340  23  3.3  70 28 1004.5
   3  77    55    66          39.6       0.00         350  5.0 350   9  2.8  59 24 1016.8
   4  77    59    68          51.1       0.00         110  9.1 130  12  8.6  62 40 1021.1
   5  90    66    78          68.3       0.00 TFH     220  8.3 260  12  6.9  84 55 1014.4
   6  81    61    71          63.7       0.00 RFH     030  6.2 030  13  9.7  93 60 1012.7
   7  73    57    65          53.0       0.00 RF      050  9.5 050  17  5.3  90 48 1021.8
   8  75    54    65          50.0       0.00 FH      160  4.2 150  10  2.6  93 41 1026.3
   9  86    32*   59       6  61.5       0.00         240  7.6 220  12  6.0  78 46 1018.6
  10  84    64    74          57.5       0.00 F       210  6.6 050   9  3.4  84 40 1019.0
  11  91    59    75          66.3       0.00 H       250  7.1 230  12  2.5  93 45 1012.6
  12  88    73    81          68.7       0.00 RTH     250  8.1 270  21  7.9  94 51 1007.0
  13  70    59    65          55.0       0.00 H       150  3.0 150   8 10.0  83 59 1012.6
  14  61    59    60       5  55.9       0.00 RF      060  6.7 080   9 10.0  93 87 1008.6
  15  64    55    60       5  54.9       0.00 F       040  4.3 200   7  9.6  96 70 1006.1
  16  79    59    69          56.7       0.00 F       250  7.6 240  21  7.8  87 44 1007.0
  17  81    57    69          51.7       0.00 T       260  9.1 270  29* 5.2  90 34 1012.5
  18  82    52    67          52.6       0.00         230  4.0 190  12  5.0  93 34 1021.3
  19  81    61    71          58.9       0.00 H       250  5.2 230  12  5.3  87 44 1028.5
  20  84    57    71          58.9       0.00 FH      150  6.3 160  13  3.6  90 43 1032.5
  21  86    59    73          57.7       0.00 F       240  6.1 250  12  1.0  87 35 1030.7
  22  90    64    77          61.1       0.00 H       250  6.4 230   9  0.2  78 38 1026.4
  23  90    68    79          63.1       0.00 H       240  8.3 230  12  0.2  68 42 1021.3
  24  90    77    84          67.5       0.00 H       350  8.5 010  14  6.9  74 48 1018.2
  25  90    72    81          61.3       0.00         190  4.9 230   9  5.6  81 29 1019.6
  26  97*   64    81          70.4       0.00 H       050  5.1 200  12  4.0 107 45 1014.9
  27  91    72    82          69.7       0.00 RTH     250 12.1 230  17  7.1  90 47 1009.0
  28  84    68    76          65.6       0.00 RTFH    280  7.6 340  16  7.0 100 51 1011.0
  29  88    66    77          59.7       0.00         040  5.4 020   9  5.3  84 33 1020.6
  30  90    45    68          63.6       0.00 H       240  6.0 220  17  4.8 200 41 1022.7
  mo  82.9  60.5  71.7    16  58.8       0.00              6.9          5.3

football.dat:

       Team            P     W    L   D    F      A     Pts
    1. Arsenal         38    26   9   3    79  -  36    87
    2. Liverpool       38    24   8   6    67  -  30    80
    3. Manchester_U    38    24   5   9    87  -  45    77
    4. Newcastle       38    21   8   9    74  -  52    71
    5. Leeds           38    18  12   8    53  -  37    66
    6. Chelsea         38    17  13   8    66  -  38    64
    7. West_Ham        38    15   8  15    48  -  57    53
    8. Aston_Villa     38    12  14  12    46  -  47    50
    9. Tottenham       38    14   8  16    49  -  53    50
   10. Blackburn       38    12  10  16    55  -  51    46
   11. Southampton     38    12   9  17    46  -  54    45
   12. Middlesbrough   38    12   9  17    35  -  47    45
   13. Fulham          38    10  14  14    36  -  44    44
   14. Charlton        38    10  14  14    38  -  49    44
   15. Everton         38    11  10  17    45  -  57    43
   16. Bolton          38     9  13  16    44  -  62    40
   17. Sunderland      38    10  10  18    29  -  51    40
   -------------------------------------------------------
   18. Ipswich         38     9   9  20    41  -  64    36
   19. Derby           38     8   6  24    33  -  63    30
   20. Leicester       38     5  13  20    30  -  64    28

可以看到,这个题目其实很简单,只要掌握基本的读文件和split功能很轻易就能实现。

初步实现

按照题目要求,我们先完成了前两步,我用的是Python,小伙伴用的是Nodejs,上代码。

Python版:

def answer_1():
    min_1 = None
    with open("weather.dat") as f:
        for line in f.readlines():
            temp = line.strip().split()
            if line[0] == "mo":
                continue
            try:
                temp_min = abs(int(temp[1].replace("*", "")) - int(temp[2].replace("*", "")))
            except:
                continue
            if not min_1:
                min_1 = temp_min
            elif temp_min < min_1:
                min_1 = temp_min

    print min_1

def answer_2():
    min_2 = None
    with open("football.dat") as f:
        for line in f.readlines():
            temp = line.strip().split()
            if len(temp) != 10:
                continue
            temp_min = abs(int(temp[6]) - int(temp[8]))
            if not min_2:
                min_2 = temp_min
            elif temp_min < min_2:
                min_2 = temp_min
    print min_2

answer_1()
answer_2()

Nodejs版:

// 问题1
var  fs = require('fs');
fs.readFile("weather.dat", "utf8", function(err, data) {
    var i;
    var mSpread = 100;
    var spread;
    var result;
    if (err) throw err;
    lines = data.trim().split(/\n/);
    lines = lines.splice(2, 30);
    lines = lines.map(function(line) {
        line = line.trim().split(/\s+/);
        return line;
    });
    for (var i = 0; i < 30; i++) {
        spread = lines[i][1] - lines[i][2];
        console.log(spread);
        if (spread < mSpread) {
            mSpread = spread;
            result = lines[i][0];
        }
    }
    console.log("最小温差是第" + result + "天。" + "最小温差是" + mSpread);
});
// 问题2
var  fs = require('fs');
fs.readFile("football.dat", "utf8", function(err, data) {
    var i;
    var l;
    var mSpread = 100;
    var spread;
    var result;
    if (err) throw err;
    lines = data.trim().split(/\n/);
    lines = lines.splice(1, 20);
    lines = lines.map(function(line) {
        line = line.trim().split(/\s+/);
        return line;
    });
    for (i = 0, l = lines.length; i < l; i++) {
        spread = Math.abs(lines[i][6] - lines[i][8]);
        console.log(lines[i]);
        if (spread < mSpread) {
            mSpread = spread;
            result = lines[i][0];
        }
    }
    console.log("第" + result + "队。")
});
nsole.log("第" + result + "队。")
});

和我们想的一样,代码非常简单。我对Python很熟悉,两个小程序基本上10分钟搞定,小伙伴Nodejs连查带写也是很快就完成了。下面就是重构了。

重构

这个练习的目的就是重构。

我觉得重构大体上有两个层面吧,一个是具体的代码层面,一个是思想层面。前者落实的时候主要是一些方法,比如提取公共函数、修改变量名可读性、优化内存使用等等,后者主要体现在编程模式上。

编程模式的话我之前看过《大话编程模式》,很不错的一本书,推荐给大家。

纸上得来终觉浅,尤其是模式这种东西,自己不写个十遍八遍的很难体会到其中的奥妙,也就更难发现模式存在的问题。世界上没有完美的模式,学习模式的终极目标其实就是抛弃模式,不断用新模式替换旧模式,不断让模式更专注于某个领域。

回到这个Kata。

由于代码太简单了,用模式来改写就意义不大了,所以我们基本上是采用了代码层面的重构。不过即使是这么简单的代码,我和小伙伴的重构结果也是差异极大。

Python重构:

def common_answer(filename, filterfunc, generate, compare):
    result = None
    with open(filename) as f:
        for line in f.readlines():
            temp = line.strip().split()
            if filterfunc(temp):
                continue
            temp = generate(temp)
            if not result:
                result = temp
            elif compare(result, temp):
                result = temp

    return result

print common_answer("weather.dat", lambda b: not b or b[0] in ["Dy", "mo"], lambda b: abs(int(b[1].replace("*", "")) - int(b[2].replace("*", ""))), lambda a, b: a > b)
print common_answer("football.dat", lambda b: "." not in b[0], lambda b: abs(int(b[6]) - int(b[8])), lambda a, b: a > b)

Nodejs重构出了一个新文件common.js

function parseTable(tableData, startline, lineNum) {
    var lines;
    lines = tableData.trim().split(/\n/);
    lines = lines.splice(startline, lineNum);
    lines = lines.map(function(line) {
        line = line.trim().split(/\s+/);
        return line;
    });
    return lines;
}

function findMinSpread(data, col1, col2) {
    var i;
    var l;
    var mSpread = 1000;
    var result;
    for (i = 0, l = data.length; i < l; i++) {
        spread = Math.abs(data[i][col1] - data[i][col2]);
        if (spread < mSpread) {
            mSpread = spread;
            result = data[i][0];
        }
    }
    return result;
}
exports.parseTable = parseTable;
exports.findMinSpread = findMinSpread;

具体解决问题的代码如下:

// 问题1
var  fs = require('fs');
var myCommon = require('./common.js');
fs.readFile("weather.dat", "utf8", function(err, data) {
    var lines;
    if (err) throw err;
    lines = myCommon.parseTable(data, 1, 20);
    console.log("最小温差是第" + myCommon.findMinSpread(lines, 1, 2) + "天。");
});
// 问题2
var  fs = require('fs');
var myCommon = require('./common.js');
fs.readFile("football.dat", "utf8", function(err, data) {
    var lines;
    if (err) throw err;
    lines = myCommon.parseTable(data, 1, 20);
    console.log("第" + myCommon.findMinSpread(lines, 6, 8) + "队。")
});

可以看出,Python的重构比较偏向整体架构调整,将处理逻辑分成多步,每一步都由传入的函数处理,相对来说偏向函数式编程一点。而Nodejs的重构比较偏向提取公共函数,处理流程的重心仍然放在代码本身。

我觉得两种方法本质上没有孰优孰劣,我重构Python的时候更多考虑的是通用性,整体的架构更像一个框架,使用的时候传入各部分的处理函数,框架负责运行。小伙伴重构Nodejs的时候更多考虑的是易用性,整体的架构更像一个工具库,使用的时候直接调用公共函数,由用户代码负责运行。

我的思路是大一统,小伙伴的思路是各司其职。相对来说,我的思路会很大地限制使用范围,但是在范围内使用是非常方便的,小伙伴的思路可以应用在非常大的范围中,但是给用户带来的便利是有限的。

一句话,区别就是深和广,具体选择哪个还是要看使用场景以及个人习惯。

反思重构

尽管这个Kata很小,并没有用各种高端的设计模式,但是仍然给我们带来很多思考。

首先,重构是否有必要。

代码重构完,从逻辑上来说确实更加清晰,代码量更少,但是重构本质是抽象,对于阅读代码的人来说实际上会增加一定的理解难度,大家从Python例子就可以看出,重构之前读一遍代码就能读懂,重构之后需要不断比对参数和函数内容才能理解作用,并没有那么直观。

不过重构还是需要的,不仅可以使代码逻辑更加清晰,代码量更少,还可以增加代码的可维护性,可以说重构带来的副作用主要就体现在代码的理解难度上,而这恰恰就是程序员的职责所在。

其次,重构思路不一样。

上文已经详细介绍了重构思路的区别,可以看到,即使是这样一个简单的例子都可以有完全不同的重构方法,那大项目就更不用说了。如何选择重构思路其实和如何选择编程语言是一样的,关键是合适不合适,而不是思路优越不优越或者语言牛逼不牛逼。

抓住本质,不要浮在表面,否则迟早有一天你会被冲走。

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,398评论 25 707
  • 本文把程序员所需掌握的关键知识总结为三大类19个关键概念,然后给出了掌握每个关键概念所需的入门书籍,必读书籍,以及...
    dle_oxio阅读 11,075评论 6 244
  • “你相信爱情吗?” “不信” “哦!我也不信……哈哈”
    不玩游戏阅读 306评论 0 0
  • 刘艳芳: 不知道你怎么样了,不知道你过的怎么样,也不知道你和他是否幸福,有没有吵架,你是在准备考试吗?你是在...
    巴图鲁阅读 258评论 1 2
  • CNBLUEone阅读 359评论 0 0