From f8e2c85bf2acffbadbbbfd1208c6cf97110d1827 Mon Sep 17 00:00:00 2001 From: Fengda HUANG Date: Fri, 23 Oct 2015 23:46:25 +0800 Subject: [PATCH] add tdd & refactor --- chapters/10-tdd-with-autotest.md | 79 +++++++- chapters/11-refactor-project.md | 261 +++++++++++++++++++++++- github-roam.md | 336 +++++++++++++++++++++++++++++++ img/replace.jpg | Bin 0 -> 54130 bytes index.html | 247 ++++++++++++++++++++++- 5 files changed, 919 insertions(+), 4 deletions(-) create mode 100644 img/replace.jpg diff --git a/chapters/10-tdd-with-autotest.md b/chapters/10-tdd-with-autotest.md index cf07f65..a3726be 100644 --- a/chapters/10-tdd-with-autotest.md +++ b/chapters/10-tdd-with-autotest.md @@ -1 +1,78 @@ -#测试 \ No newline at end of file +#测试 + +##一次测试驱动开发 + +虽然接触的TDD时间不算短,然而真正在实践TDD上的时候少之又少。除去怎么教人TDD,就是与人结对编程时的switch,或许是受限于当前的开发流程。 + +偶然间在开发一个物联网相关的开源项目——[Lan](https://github.com/phodal/lan)的时候,重拾了这个过程。不得不说提到的一点是,在我们的开发流程中**测试是由相关功能开发人员写的**,有时候测试是一种很具挑战性的工作。久而久之,为自己的开源项目写测试变成一种自然而然的事。有时没有测试,反而变得**没有安全感**。 + +###故事 + +之前正在重写一个[物联网](http://www.phodal.com/iot)的服务端,主要便是结合CoAP、MQTT、HTTP等协议构成一个物联网的云服务。现在,主要的任务是集中于协议与授权。由于,不同协议间的授权是不一样的,最开始的时候我先写了一个http put授权的功能,而在起先的时候是如何测试的呢? + + curl --user root:root -X PUT -d '{ "dream": 1 }' -H "Content-Type: application/json" http://localhost:8899/topics/test + +我只要顺利在request中看有无``req.headers.authorization``,我便可以继续往下,接着给个判断。毕竟,我们对HTTP协议还是蛮清楚的。 + +```javascript +if (!req.headers.authorization) { + res.statusCode = 401; + res.setHeader('WWW-Authenticate', 'Basic realm="Secure Area"'); + return res.end('Unauthorized'); +} +``` + +可是除了HTTP协议,还有MQTT和CoAP。对于MQTT协议来说,那还算好,毕竟自带授权,如: + +```bash +mosquitto_pub -u root -P root -h localhost -d -t lettuce -m "Hello, MQTT. This is my first message." +``` + +便可以让我们简单地完成这个功能,然而有的协议是没有这样的功能如CoAP协议中是用Option来进行授权的。现在的工具如libcoap只能有如下的简单功能 + +```bash +coap-client -m get coap://127.0.0.1:5683/topics/zero -T +``` + +于是,先写了个测试脚本来验证功能。 + +```javascript +var coap = require('coap'); +var request = coap.request; +var req = request({hostname: 'localhost',port:5683,pathname: '',method: 'POST'}); + +... + +req.setHeader("Accept", "application/json"); +req.setOption('Block2', [new Buffer('phodal'), new Buffer('phodal')]); + +... + +req.end(); +``` + +写完测试脚本后发现不对了,这个不应该是测试的代码吗? 于是将其放到了spec中,接着发现了上面的全部功能的实现过程为什么不用TDD实现呢? + +###说说测试驱动开发 + +测试驱动开发是一个很"古老"的程序开发方法,然而由于国内的开发流程的问题——即开发人员负责功能的测试,导致这么好的一项技术没有在国内推广。 + +测试驱动开发的主要过程是: + +1. 先写功能的测试 +2. 实现功能代码 +3. 提交代码(commit -> 保证功能正常) +4. 重构功能代码 + +而对于这样的一个物联网项目来说,我已经有了几个有利的前提: + +1. 已经有了原型 +2. 框架设计 + +###思考 + +通常在我的理解下,TDD是可有可无的。既然我知道了我要实现的大部分功能,而且我也知道如何实现。与此同时,对Code Smell也保持着警惕、要保证功能被测试覆盖。那么,总的来说TDD带来的价值并不大。 + +然而,在当前这种情况下,我知道我想要的功能,但是我并不理解其深层次的功能。我需要花费大量的时候来理解,它为什么是这样的,需要先有一些脚本来知道它是怎么工作的。TDD变显得很有价值,换句话来说,在现有的情况下,TDD对于我们不了解的一些事情,可以驱动出更多的开发。毕竟在我们完成测试脚本之后,我们也会发现这些测试脚本成为了代码的一部分。 + +在这种理想的情况下,我们为什么不TDD呢? \ No newline at end of file diff --git a/chapters/11-refactor-project.md b/chapters/11-refactor-project.md index 8d5bd4b..7f379a0 100644 --- a/chapters/11-refactor-project.md +++ b/chapters/11-refactor-project.md @@ -151,4 +151,263 @@ str = tableHandler(str, execStr, strict); ✓ should return correctly class name ``` -快来试试吧, [https://github.com/artisanstack/js-refactor](https://github.com/artisanstack/js-refactor) \ No newline at end of file +快来试试吧, [https://github.com/artisanstack/js-refactor](https://github.com/artisanstack/js-refactor) + +是时候讨论这个Refactor利器了,最初看到这个重构的过程是从ThoughtWorks郑大晔校开始的,只是之前对于Java的另外一个编辑器Eclipse的坏感。。这些在目前已经不是很重要了,试试这个公司里面应用广泛的编辑器。 + +##Interllij Idea重构 + +开发的流程大致就是这样子的,测试先行算是推荐的。 + + 编写测试->功能代码->修改测试->重构 + +上次在和buddy聊天的时候,才知道测试在功能简单的时候是后行的,在功能复杂不知道怎么手手的时候是先行的。 + + +开始之前请原谅我对于Java语言的一些无知,然后,看一下我写的Main函数: + +```java +package com.phodal.learing; + +public class Main { + + public static void main(String[] args) { + int c=new Cal().add(1,2); + int d=new Cal2().sub(2,1); + System.out.println("Hello,s"); + System.out.println(c); + System.out.println(d); + } +} +``` + +代码写得还好(自我感觉),先不管Cal和Cal2两个类。大部分都能看懂,除了c,d不知道他们表达的是什么意思,于是。 + +###Rename + +**快捷键:Shift+F6** + +**作用:重命名** + + - 把光标丢到int c中的c,按下shift+f6,输入result_add + - 把光标移到int d中的d,按下shift+f6,输入result_sub + +于是就有 + +```java +package com.phodal.learing; + +public class Main { + + public static void main(String[] args) { + int result_add=new Cal().add(1,2); + int result_sub=new Cal2().sub(2,1); + System.out.println("Hello,s"); + System.out.println(result_add); + System.out.println(result_sub); + } +} +``` + +###Extract Method + +**快捷键:alt+command+m** + +**作用:扩展方法** + +- 选中System.out.println(result_add); +- 按下alt+command+m +- 在弹出的窗口中输入mprint + +于是有了 + +```java +public static void main(String[] args) { + int result_add=new Cal().add(1,2); + int result_sub=new Cal2().sub(2,1); + System.out.println("Hello,s"); + mprint(result_add); + mprint(result_sub); +} + +private static void mprint(int result_sub) { + System.out.println(result_sub); +} +``` + +似乎我们不应该这样对待System.out.println,那么让我们内联回去 + +###Inline Method + +**快捷键:alt+command+n** + +**作用:内联方法** + +- 选中main中的mprint +- alt+command+n +- 选中Inline all invocations and remove the method(2 occurrences) 点确定 + +然后我们等于什么也没有做了~~: + +```java +public static void main(String[] args) { + int result_add=new Cal().add(1,2); + int result_sub=new Cal2().sub(2,1); + System.out.println("Hello,s"); + System.out.println(result_add); + System.out.println(result_sub); +} +``` + +似乎这个例子不是很好,但是够用来说明了。 + +###Pull Members Up + +开始之前让我们先看看Cal2类: + +```java +public class Cal2 extends Cal { + + public int sub(int a,int b){ + return a-b; + } +} +``` + +以及Cal2的父类Cal + +```java +public class Cal { + + public int add(int a,int b){ + return a+b; + } + +} +``` + +最后的结果,就是将Cal2类中的sub方法,提到父类: + +```java +public class Cal { + + public int add(int a,int b){ + return a+b; + } + + public int sub(int a,int b){ + return a-b; + } +} +``` + +而我们所要做的就是鼠标右键 + +###重构之以查询取代临时变量 + +快捷键 + +Mac: 木有 + +Windows/Linux: 木有 + +或者: ``Shift``+``alt``+``command``+``T`` 再选择 ``Replace Temp with Query`` + +鼠标: **Refactor** | ``Replace Temp with Query`` + +####重构之前 + +过多的临时变量会让我们写出更长的函数,函数不应该太多,以便使功能单一。这也是重构的另外的目的所在,只有函数专注于其功能,才会更容易读懂。 + +以书中的代码为例 + +```java +import java.lang.System; + +public class replaceTemp { + public void count() { + double basePrice = _quantity * _itemPrice; + if (basePrice > 1000) { + return basePrice * 0.95; + } else { + return basePrice * 0.98; + } + } +} +``` + +####重构 + +选中``basePrice``很愉快地拿鼠标点上面的重构 + +![Replace Temp With Query](./img/replace.jpg) + +便会返回 + +```java +import java.lang.System; + +public class replaceTemp { + public void count() { + if (basePrice() > 1000) { + return basePrice() * 0.95; + } else { + return basePrice() * 0.98; + } + } + + private double basePrice() { + return _quantity * _itemPrice; + } +} +``` + +而实际上我们也可以 + +1. 选中 + + _quantity * _itemPrice + +2. 对其进行``Extrace Method`` + +3. 选择``basePrice``再``Inline Method`` + +####Intellij IDEA重构 + +在Intellij IDEA的文档中对此是这样的例子 + +```java +public class replaceTemp { + + public void method() { + String str = "str"; + String aString = returnString().concat(str); + System.out.println(aString); + } + +} +``` + +接着我们选中``aString``,再打开重构菜单,或者 + +``Command``+``Alt``+``Shift``+``T`` 再选中Replace Temp with Query + +便会有下面的结果: + + +```javas +import java.lang.String; + +public class replaceTemp { + + public void method() { + String str = "str"; + System.out.println(aString(str)); + } + + private String aString(String str) { + return returnString().concat(str); + } + +} +``` \ No newline at end of file diff --git a/github-roam.md b/github-roam.md index d5cdc14..1914773 100644 --- a/github-roam.md +++ b/github-roam.md @@ -1588,6 +1588,83 @@ SQLiteHelper.prototype.getData = function (url, callback) { #测试 +##一次测试驱动开发 + +虽然接触的TDD时间不算短,然而真正在实践TDD上的时候少之又少。除去怎么教人TDD,就是与人结对编程时的switch,或许是受限于当前的开发流程。 + +偶然间在开发一个物联网相关的开源项目——[Lan](https://github.com/phodal/lan)的时候,重拾了这个过程。不得不说提到的一点是,在我们的开发流程中**测试是由相关功能开发人员写的**,有时候测试是一种很具挑战性的工作。久而久之,为自己的开源项目写测试变成一种自然而然的事。有时没有测试,反而变得**没有安全感**。 + +###故事 + +之前正在重写一个[物联网](http://www.phodal.com/iot)的服务端,主要便是结合CoAP、MQTT、HTTP等协议构成一个物联网的云服务。现在,主要的任务是集中于协议与授权。由于,不同协议间的授权是不一样的,最开始的时候我先写了一个http put授权的功能,而在起先的时候是如何测试的呢? + + curl --user root:root -X PUT -d '{ "dream": 1 }' -H "Content-Type: application/json" http://localhost:8899/topics/test + +我只要顺利在request中看有无``req.headers.authorization``,我便可以继续往下,接着给个判断。毕竟,我们对HTTP协议还是蛮清楚的。 + +```javascript +if (!req.headers.authorization) { + res.statusCode = 401; + res.setHeader('WWW-Authenticate', 'Basic realm="Secure Area"'); + return res.end('Unauthorized'); +} +``` + +可是除了HTTP协议,还有MQTT和CoAP。对于MQTT协议来说,那还算好,毕竟自带授权,如: + +```bash +mosquitto_pub -u root -P root -h localhost -d -t lettuce -m "Hello, MQTT. This is my first message." +``` + +便可以让我们简单地完成这个功能,然而有的协议是没有这样的功能如CoAP协议中是用Option来进行授权的。现在的工具如libcoap只能有如下的简单功能 + +```bash +coap-client -m get coap://127.0.0.1:5683/topics/zero -T +``` + +于是,先写了个测试脚本来验证功能。 + +```javascript +var coap = require('coap'); +var request = coap.request; +var req = request({hostname: 'localhost',port:5683,pathname: '',method: 'POST'}); + +... + +req.setHeader("Accept", "application/json"); +req.setOption('Block2', [new Buffer('phodal'), new Buffer('phodal')]); + +... + +req.end(); +``` + +写完测试脚本后发现不对了,这个不应该是测试的代码吗? 于是将其放到了spec中,接着发现了上面的全部功能的实现过程为什么不用TDD实现呢? + +###说说测试驱动开发 + +测试驱动开发是一个很"古老"的程序开发方法,然而由于国内的开发流程的问题——即开发人员负责功能的测试,导致这么好的一项技术没有在国内推广。 + +测试驱动开发的主要过程是: + +1. 先写功能的测试 +2. 实现功能代码 +3. 提交代码(commit -> 保证功能正常) +4. 重构功能代码 + +而对于这样的一个物联网项目来说,我已经有了几个有利的前提: + +1. 已经有了原型 +2. 框架设计 + +###思考 + +通常在我的理解下,TDD是可有可无的。既然我知道了我要实现的大部分功能,而且我也知道如何实现。与此同时,对Code Smell也保持着警惕、要保证功能被测试覆盖。那么,总的来说TDD带来的价值并不大。 + +然而,在当前这种情况下,我知道我想要的功能,但是我并不理解其深层次的功能。我需要花费大量的时候来理解,它为什么是这样的,需要先有一些脚本来知道它是怎么工作的。TDD变显得很有价值,换句话来说,在现有的情况下,TDD对于我们不了解的一些事情,可以驱动出更多的开发。毕竟在我们完成测试脚本之后,我们也会发现这些测试脚本成为了代码的一部分。 + +在这种理想的情况下,我们为什么不TDD呢? + #重构 或许你应该知道了,重构是怎样的,你也知道重构能带来什么。在我刚开始学重构和设计模式的时候,我需要去找一些好的示例,以便于我更好的学习。有时候不得不创造一些更好的场景,来实现这些功能。 @@ -1743,6 +1820,265 @@ str = tableHandler(str, execStr, strict); 快来试试吧, [https://github.com/artisanstack/js-refactor](https://github.com/artisanstack/js-refactor) +是时候讨论这个Refactor利器了,最初看到这个重构的过程是从ThoughtWorks郑大晔校开始的,只是之前对于Java的另外一个编辑器Eclipse的坏感。。这些在目前已经不是很重要了,试试这个公司里面应用广泛的编辑器。 + +##Interllij Idea重构 + +开发的流程大致就是这样子的,测试先行算是推荐的。 + + 编写测试->功能代码->修改测试->重构 + +上次在和buddy聊天的时候,才知道测试在功能简单的时候是后行的,在功能复杂不知道怎么手手的时候是先行的。 + + +开始之前请原谅我对于Java语言的一些无知,然后,看一下我写的Main函数: + +```java +package com.phodal.learing; + +public class Main { + + public static void main(String[] args) { + int c=new Cal().add(1,2); + int d=new Cal2().sub(2,1); + System.out.println("Hello,s"); + System.out.println(c); + System.out.println(d); + } +} +``` + +代码写得还好(自我感觉),先不管Cal和Cal2两个类。大部分都能看懂,除了c,d不知道他们表达的是什么意思,于是。 + +###Rename + +**快捷键:Shift+F6** + +**作用:重命名** + + - 把光标丢到int c中的c,按下shift+f6,输入result_add + - 把光标移到int d中的d,按下shift+f6,输入result_sub + +于是就有 + +```java +package com.phodal.learing; + +public class Main { + + public static void main(String[] args) { + int result_add=new Cal().add(1,2); + int result_sub=new Cal2().sub(2,1); + System.out.println("Hello,s"); + System.out.println(result_add); + System.out.println(result_sub); + } +} +``` + +###Extract Method + +**快捷键:alt+command+m** + +**作用:扩展方法** + +- 选中System.out.println(result_add); +- 按下alt+command+m +- 在弹出的窗口中输入mprint + +于是有了 + +```java +public static void main(String[] args) { + int result_add=new Cal().add(1,2); + int result_sub=new Cal2().sub(2,1); + System.out.println("Hello,s"); + mprint(result_add); + mprint(result_sub); +} + +private static void mprint(int result_sub) { + System.out.println(result_sub); +} +``` + +似乎我们不应该这样对待System.out.println,那么让我们内联回去 + +###Inline Method + +**快捷键:alt+command+n** + +**作用:内联方法** + +- 选中main中的mprint +- alt+command+n +- 选中Inline all invocations and remove the method(2 occurrences) 点确定 + +然后我们等于什么也没有做了~~: + +```java +public static void main(String[] args) { + int result_add=new Cal().add(1,2); + int result_sub=new Cal2().sub(2,1); + System.out.println("Hello,s"); + System.out.println(result_add); + System.out.println(result_sub); +} +``` + +似乎这个例子不是很好,但是够用来说明了。 + +###Pull Members Up + +开始之前让我们先看看Cal2类: + +```java +public class Cal2 extends Cal { + + public int sub(int a,int b){ + return a-b; + } +} +``` + +以及Cal2的父类Cal + +```java +public class Cal { + + public int add(int a,int b){ + return a+b; + } + +} +``` + +最后的结果,就是将Cal2类中的sub方法,提到父类: + +```java +public class Cal { + + public int add(int a,int b){ + return a+b; + } + + public int sub(int a,int b){ + return a-b; + } +} +``` + +而我们所要做的就是鼠标右键 + +###重构之以查询取代临时变量 + +快捷键 + +Mac: 木有 + +Windows/Linux: 木有 + +或者: ``Shift``+``alt``+``command``+``T`` 再选择 ``Replace Temp with Query`` + +鼠标: **Refactor** | ``Replace Temp with Query`` + +####重构之前 + +过多的临时变量会让我们写出更长的函数,函数不应该太多,以便使功能单一。这也是重构的另外的目的所在,只有函数专注于其功能,才会更容易读懂。 + +以书中的代码为例 + +```java +import java.lang.System; + +public class replaceTemp { + public void count() { + double basePrice = _quantity * _itemPrice; + if (basePrice > 1000) { + return basePrice * 0.95; + } else { + return basePrice * 0.98; + } + } +} +``` + +####重构 + +选中``basePrice``很愉快地拿鼠标点上面的重构 + +![Replace Temp With Query](./img/replace.jpg) + +便会返回 + +```java +import java.lang.System; + +public class replaceTemp { + public void count() { + if (basePrice() > 1000) { + return basePrice() * 0.95; + } else { + return basePrice() * 0.98; + } + } + + private double basePrice() { + return _quantity * _itemPrice; + } +} +``` + +而实际上我们也可以 + +1. 选中 + + _quantity * _itemPrice + +2. 对其进行``Extrace Method`` + +3. 选择``basePrice``再``Inline Method`` + +####Intellij IDEA重构 + +在Intellij IDEA的文档中对此是这样的例子 + +```java +public class replaceTemp { + + public void method() { + String str = "str"; + String aString = returnString().concat(str); + System.out.println(aString); + } + +} +``` + +接着我们选中``aString``,再打开重构菜单,或者 + +``Command``+``Alt``+``Shift``+``T`` 再选中Replace Temp with Query + +便会有下面的结果: + + +```javas +import java.lang.String; + +public class replaceTemp { + + public void method() { + String str = "str"; + System.out.println(aString(str)); + } + + private String aString(String str) { + return returnString().concat(str); + } + +} +``` + #Github连击 ##100天 diff --git a/img/replace.jpg b/img/replace.jpg new file mode 100644 index 0000000000000000000000000000000000000000..08d55efe94aeebcf79d4a6faee2292d210a4a396 GIT binary patch literal 54130 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 RAfU!6>6%7 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 span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
  • 创建项目文档
  • -
  • 测试
  • +
  • 测试
  • 重构
  • 前端技能训练: 重构一
  • +
  • Interllij Idea重构
  • Github连击
    • 100天
        @@ -1515,6 +1528,54 @@ line 21 col 62 Strings must use singlequote.

        重构完后的代码比原来还长,这似乎是个问题~~

        创建项目文档

        测试

        +

        一次测试驱动开发

        +

        虽然接触的TDD时间不算短,然而真正在实践TDD上的时候少之又少。除去怎么教人TDD,就是与人结对编程时的switch,或许是受限于当前的开发流程。

        +

        偶然间在开发一个物联网相关的开源项目——Lan的时候,重拾了这个过程。不得不说提到的一点是,在我们的开发流程中测试是由相关功能开发人员写的,有时候测试是一种很具挑战性的工作。久而久之,为自己的开源项目写测试变成一种自然而然的事。有时没有测试,反而变得没有安全感

        +

        故事

        +

        之前正在重写一个物联网的服务端,主要便是结合CoAP、MQTT、HTTP等协议构成一个物联网的云服务。现在,主要的任务是集中于协议与授权。由于,不同协议间的授权是不一样的,最开始的时候我先写了一个http put授权的功能,而在起先的时候是如何测试的呢?

        +
        curl --user root:root -X PUT -d '{ "dream": 1 }' -H "Content-Type: application/json" http://localhost:8899/topics/test
        +

        我只要顺利在request中看有无req.headers.authorization,我便可以继续往下,接着给个判断。毕竟,我们对HTTP协议还是蛮清楚的。

        +
        if (!req.headers.authorization) {
        +  res.statusCode = 401;
        +  res.setHeader('WWW-Authenticate', 'Basic realm="Secure Area"');
        +  return res.end('Unauthorized');
        +}
        +

        可是除了HTTP协议,还有MQTT和CoAP。对于MQTT协议来说,那还算好,毕竟自带授权,如:

        +
        mosquitto_pub -u root -P root -h localhost -d -t lettuce -m "Hello, MQTT. This is my first message."
        +

        便可以让我们简单地完成这个功能,然而有的协议是没有这样的功能如CoAP协议中是用Option来进行授权的。现在的工具如libcoap只能有如下的简单功能

        +
        coap-client -m get coap://127.0.0.1:5683/topics/zero -T
        +

        于是,先写了个测试脚本来验证功能。

        +
        var coap     = require('coap');
        +var request  = coap.request;
        +var req = request({hostname: 'localhost',port:5683,pathname: '',method: 'POST'});
        +
        +...
        +
        +req.setHeader("Accept", "application/json");
        +req.setOption('Block2',  [new Buffer('phodal'), new Buffer('phodal')]);
        +
        +...
        +
        +req.end();
        +

        写完测试脚本后发现不对了,这个不应该是测试的代码吗? 于是将其放到了spec中,接着发现了上面的全部功能的实现过程为什么不用TDD实现呢?

        +

        说说测试驱动开发

        +

        测试驱动开发是一个很“古老”的程序开发方法,然而由于国内的开发流程的问题——即开发人员负责功能的测试,导致这么好的一项技术没有在国内推广。

        +

        测试驱动开发的主要过程是:

        +
          +
        1. 先写功能的测试
        2. +
        3. 实现功能代码
        4. +
        5. 提交代码(commit -> 保证功能正常)
        6. +
        7. 重构功能代码
        8. +
        +

        而对于这样的一个物联网项目来说,我已经有了几个有利的前提:

        +
          +
        1. 已经有了原型
        2. +
        3. 框架设计
        4. +
        +

        思考

        +

        通常在我的理解下,TDD是可有可无的。既然我知道了我要实现的大部分功能,而且我也知道如何实现。与此同时,对Code Smell也保持着警惕、要保证功能被测试覆盖。那么,总的来说TDD带来的价值并不大。

        +

        然而,在当前这种情况下,我知道我想要的功能,但是我并不理解其深层次的功能。我需要花费大量的时候来理解,它为什么是这样的,需要先有一些脚本来知道它是怎么工作的。TDD变显得很有价值,换句话来说,在现有的情况下,TDD对于我们不了解的一些事情,可以驱动出更多的开发。毕竟在我们完成测试脚本之后,我们也会发现这些测试脚本成为了代码的一部分。

        +

        在这种理想的情况下,我们为什么不TDD呢?

        重构

        或许你应该知道了,重构是怎样的,你也知道重构能带来什么。在我刚开始学重构和设计模式的时候,我需要去找一些好的示例,以便于我更好的学习。有时候不得不创造一些更好的场景,来实现这些功能。

        有一天,我发现当我需要我一次又一次地重复讲述某些内容,于是我就计划着把这些应该掌握的技能放到Github上,也就有了Artisan Stack 计划。

        @@ -1614,6 +1675,188 @@ str = tableHandler(strreturn correctly class name

        快来试试吧, https://github.com/artisanstack/js-refactor

        +

        是时候讨论这个Refactor利器了,最初看到这个重构的过程是从ThoughtWorks郑大晔校开始的,只是之前对于Java的另外一个编辑器Eclipse的坏感。。这些在目前已经不是很重要了,试试这个公司里面应用广泛的编辑器。

        +

        Interllij Idea重构

        +

        开发的流程大致就是这样子的,测试先行算是推荐的。

        +
        编写测试->功能代码->修改测试->重构
        +

        上次在和buddy聊天的时候,才知道测试在功能简单的时候是后行的,在功能复杂不知道怎么手手的时候是先行的。

        +

        开始之前请原谅我对于Java语言的一些无知,然后,看一下我写的Main函数:

        +
        package com.phodal.learing;
        +
        +public class Main {
        +
        +    public static void main(String[] args) {
        +        int c=new Cal().add(1,2);
        +        int d=new Cal2().sub(2,1);
        +        System.out.println("Hello,s");
        +        System.out.println(c);
        +        System.out.println(d);
        +    }
        +}
        +

        代码写得还好(自我感觉),先不管Cal和Cal2两个类。大部分都能看懂,除了c,d不知道他们表达的是什么意思,于是。

        +

        Rename

        +

        快捷键:Shift+F6

        +

        作用:重命名

        +
          +
        • 把光标丢到int c中的c,按下shift+f6,输入result_add
        • +
        • 把光标移到int d中的d,按下shift+f6,输入result_sub
        • +
        +

        于是就有

        +
        package com.phodal.learing;
        +
        +public class Main {
        +
        +    public static void main(String[] args) {
        +        int result_add=new Cal().add(1,2);
        +        int result_sub=new Cal2().sub(2,1);
        +        System.out.println("Hello,s");
        +        System.out.println(result_add);
        +        System.out.println(result_sub);
        +    }
        +}
        +

        Extract Method

        +

        快捷键:alt+command+m

        +

        作用:扩展方法

        +
          +
        • 选中System.out.println(result_add);
        • +
        • 按下alt+command+m
        • +
        • 在弹出的窗口中输入mprint
        • +
        +

        于是有了

        +
        public static void main(String[] args) {
        +    int result_add=new Cal().add(1,2);
        +    int result_sub=new Cal2().sub(2,1);
        +    System.out.println("Hello,s");
        +    mprint(result_add);
        +    mprint(result_sub);
        +}
        +
        +private static void mprint(int result_sub) {
        +    System.out.println(result_sub);
        +}
        +

        似乎我们不应该这样对待System.out.println,那么让我们内联回去

        +

        Inline Method

        +

        快捷键:alt+command+n

        +

        作用:内联方法

        +
          +
        • 选中main中的mprint
        • +
        • alt+command+n
        • +
        • 选中Inline all invocations and remove the method(2 occurrences) 点确定
        • +
        +

        然后我们等于什么也没有做了~~:

        +
        public static void main(String[] args) {
        +    int result_add=new Cal().add(1,2);
        +    int result_sub=new Cal2().sub(2,1);
        +    System.out.println("Hello,s");
        +    System.out.println(result_add);
        +    System.out.println(result_sub);
        +}
        +

        似乎这个例子不是很好,但是够用来说明了。

        +

        Pull Members Up

        +

        开始之前让我们先看看Cal2类:

        +
        public class Cal2 extends Cal {
        +
        +    public int sub(int a,int b){
        +        return a-b;
        +    }
        +}
        +

        以及Cal2的父类Cal

        +
        public class Cal {
        +
        +    public int add(int a,int b){
        +        return a+b;
        +    }
        +
        +}
        +

        最后的结果,就是将Cal2类中的sub方法,提到父类:

        +
        public class Cal {
        +
        +    public int add(int a,int b){
        +        return a+b;
        +    }
        +
        +    public int sub(int a,int b){
        +        return a-b;
        +    }
        +}
        +

        而我们所要做的就是鼠标右键

        +

        重构之以查询取代临时变量

        +

        快捷键

        +

        Mac: 木有

        +

        Windows/Linux: 木有

        +

        或者: Shift+alt+command+T 再选择 Replace Temp with Query

        +

        鼠标: Refactor | Replace Temp with Query

        +

        重构之前

        +

        过多的临时变量会让我们写出更长的函数,函数不应该太多,以便使功能单一。这也是重构的另外的目的所在,只有函数专注于其功能,才会更容易读懂。

        +

        以书中的代码为例

        +
        import java.lang.System;
        +
        +public class replaceTemp {
        +    public void count() {
        +        double basePrice = _quantity * _itemPrice;
        +        if (basePrice > 1000) {
        +            return basePrice * 0.95;
        +        } else {
        +            return basePrice * 0.98;
        +        }
        +    }
        +}
        +

        重构

        +

        选中basePrice很愉快地拿鼠标点上面的重构

        +
        +Replace Temp With Query
        Replace Temp With Query
        +
        +

        便会返回

        +
        import java.lang.System;
        +
        +public class replaceTemp {
        +    public void count() {
        +        if (basePrice() > 1000) {
        +            return basePrice() * 0.95;
        +        } else {
        +            return basePrice() * 0.98;
        +        }
        +    }
        +
        +    private double basePrice() {
        +        return _quantity * _itemPrice;
        +    }
        +}
        +

        而实际上我们也可以

        +
          +
        1. 选中

          +

          _quantity * _itemPrice

        2. +
        3. 对其进行Extrace Method

        4. +
        5. 选择basePriceInline Method

        6. +
        +

        Intellij IDEA重构

        +

        在Intellij IDEA的文档中对此是这样的例子

        +
        public class replaceTemp {
        +
        +    public void method() {
        +        String str = "str";
        +        String aString = returnString().concat(str);
        +        System.out.println(aString);
        +    }
        +
        +}
        +

        接着我们选中aString,再打开重构菜单,或者

        +

        Command+Alt+Shift+T 再选中Replace Temp with Query

        +

        便会有下面的结果:

        +
        import java.lang.String;
        +
        +public class replaceTemp {
        +
        +    public void method() {
        +        String str = "str";
        +        System.out.println(aString(str));
        +    }
        +
        +    private String aString(String str) {
        +        return returnString().concat(str);
        +    }
        +
        +}

        Github连击

        100天

        我也是蛮拼的,虽然我想的只是在Github上连击100~200天,然而到了今天也算不错。

        @@ -1819,7 +2062,7 @@ str = tableHandler(str编程的基础能力

        虽说算法很重要,但是编码才是基础能力。算法与编程在某种程度上是不同的领域,算法编程是在编程上面的一级。算法写得再好,如果别人很难直接拿来复用,在别人眼里就是shit。想出能work的代码一件简单的事,学会对其重构,使之变得更易读就是一件有意义的事。

        于是,在某一时刻在Github上创建了一个组织,叫Artisan Stack。当时想的是在Github寻找一些JavaScript项目,对其代码进行重构。但是到底是影响力不够哈,参与的人数比较少。

        -

        重构

        +

        重构

        如果你懂得如何写出高可读的代码,那么我想你是不需要这个的,但是这意味着你花了更多的时候在思考上了。当谈论重构的时候,让我想起了TDD(测试驱动开发)。即使不是TDD,那么如果你写着测试,那也是可以重构的。(之前写过一些利用Intellij IDEA重构的文章:提炼函数以查询取代临时变量重构与Intellij Idea初探内联函数)

        在各种各样的文章里,我们看到过一些相关的内容,最好的参考莫过于《重构》一书。最基础不过的原则便是函数名,取名字很难,取别人能读懂的名字更难。其他的便有诸如长函数、过大的类、重复代码等等。在我有限的面试别人的经历里,这些问题都是最常见的。

        测试