diff --git a/chapters/05-analytics-project.md b/chapters/05-analytics-project.md
index 3697f38..39bf1ff 100644
--- a/chapters/05-analytics-project.md
+++ b/chapters/05-analytics-project.md
@@ -1,4 +1,4 @@
-#Github项目分析
+#Github流行项目分析
之前曾经分析过一些Github的用户行为,现在我们先来说说Github上的Star吧。(截止: 2015年3月9日23时。)
diff --git a/chapters/08-build-github-project.md b/chapters/08-build-github-project.md
index 2023e60..6b3e0a4 100644
--- a/chapters/08-build-github-project.md
+++ b/chapters/08-build-github-project.md
@@ -8,8 +8,6 @@
今天就来说说是怎样做的。
-##Github项目组成
-
以之前造的[Lettuce](https://github.com/phodal/lettuce)为例,里面有:
- 代码质量(Code Climate)
@@ -25,7 +23,7 @@
等等。
-##Skillock模块化
+###Skillock模块化
在SkillTree的源码里,大致分为三部分:
@@ -122,8 +120,6 @@ return {
当然函数也是一个对象。
-##Skillock测试
-
###自动化测试
一直习惯用Travis CI,于是也继续用Travis Ci,``.travis.yml``配置如下所示:
@@ -161,7 +157,7 @@ branches:
最后的``test/spec``是指定测试的目录。
-##Jshint
+###Jshint
> JSLint定义了一组编码约定,这比ECMA定义的语言更为严格。这些编码约定汲取了多年来的丰富编码经验,并以一条年代久远的编程原则 作为宗旨:能做并不意味着应该做。JSLint会对它认为有的编码实践加标志,另外还会指出哪些是明显的错误,从而促使你养成好的 JavaScript编码习惯。
@@ -254,7 +250,7 @@ it('should return book label & url', function () {
这就是个问题了,于是偶然间看到了一个叫code climate的网站。
-##Code Climate
+###Code Climate
> Code Climate consolidates the results from a suite of static analysis tools into a single, real-time report, giving your team the information it needs to identify hotspots, evaluate new approaches, and improve code quality.
@@ -284,11 +280,11 @@ A | lib/url_handler.js | 9 | 0 | 5 | 2.2 | 94.1% | 0
![Coverage][1]
-##代码的坏味道
+###代码的坏味道
于是我们就打开``lib/database/sqlite_helper.js``,因为其中有两个坏味道
-###Similar code found in two :expression_statement nodes (mass = 86)
+Similar code found in two :expression_statement nodes (mass = 86)
在代码的 ``lib/database/sqlite_helper.js:58…61 < >``
diff --git a/github-roam.md b/github-roam.md
index 0066b27..d5cdc14 100644
--- a/github-roam.md
+++ b/github-roam.md
@@ -1133,7 +1133,7 @@ def get_points(usernames):
真看不出来两者有什么相似的地方 。。。。
-#Github项目分析
+#Github流行项目分析
之前曾经分析过一些Github的用户行为,现在我们先来说说Github上的Star吧。(截止: 2015年3月9日23时。)
@@ -1199,12 +1199,550 @@ C | 2
#构建Github项目
+##从模块分离到测试
+
+在之前说到
+
+> 奋斗了近半个月后,将fork的代码读懂、重构、升级版本、调整,添加新功能、添加测试、添加CI、添加分享之后,终于almost finish。
+
+今天就来说说是怎样做的。
+
+以之前造的[Lettuce](https://github.com/phodal/lettuce)为例,里面有:
+
+ - 代码质量(Code Climate)
+ - CI状态(Travis CI)
+ - 测试覆盖率(96%)
+ - 自动化测试(npm test)
+ - 文档
+
+按照[Web Developer路线图](https://github.com/phodal/awesome-developer)来说,我们还需要有:
+
+ - 版本管理
+ - 自动部署
+
+等等。
+
+###Skillock模块化
+
+在SkillTree的源码里,大致分为三部分:
+
+ - namespace函数: 故名思意
+ - Calculator也就是TalentTree,主要负责解析、生成url,头像,依赖等等
+ - Skill 主要是tips部分。
+
+而这一些都在一个js里,对于一个库来说,是一件好事,但是对于一个项目来说,并非如此。
+
+依赖的库有
+
+ - jQuery
+ - Knockout
+
+好在Knockout可以用Require.js进行管理,于是,使用了``Require.js``进行管理:
+
+```html
+
+```
+
+``main.js``配置如下:
+
+```javascript
+require.config({
+ baseUrl: 'app',
+ paths:{
+ jquery: 'lib/jquery',
+ json: 'lib/json',
+ text: 'lib/text'
+ }
+});
+
+require(['scripts/ko-bindings']);
+
+require(['lib/knockout', 'scripts/TalentTree', 'json!data/web.json'], function(ko, TalentTree, TalentData) {
+ 'use strict';
+ var vm = new TalentTree(TalentData);
+ ko.applyBindings(vm);
+});
+```
+
+text、json插件主要是用于处理web.json,即用json来处理技能,于是不同的类到了不同的js文件。
+
+ .
+ |____Book.js
+ |____Doc.js
+ |____ko-bindings.js
+ |____Link.js
+ |____main.js
+ |____Skill.js
+ |____TalentTree.js
+ |____Utils.js
+
+加上了后来的推荐阅读书籍等等。而Book和Link都是继承自Doc。
+
+```javascript
+define(['scripts/Doc'], function(Doc) {
+ 'use strict';
+ function Book(_e) {
+ Doc.apply(this, arguments);
+ }
+ Book.prototype = new Doc();
+
+ return Book;
+});
+```
+
+而这里便是后面对其进行重构的内容。Doc类则是Skillock中类的一个缩影
+
+```javascript
+define([], function() {
+ 'use strict';
+ var Doc = function (_e) {
+ var e = _e || {};
+ var self = this;
+
+ self.label = e.label || (e.url || 'Learn more');
+ self.url = e.url || 'javascript:void(0)';
+ };
+
+ return Doc;
+});
+```
+
+或者说这是一个AMD的Class应该有的样子。考虑到this的隐性绑定,作者用了self=this来避免这个问题。最后Return了这个对象,我们在调用的就需要new一个。大部分在代码中返回的都是对象,除了在Utils类里面返回的是函数:
+
+```javascript
+return {
+ getSkillsByHash: getSkillsByHash,
+ getSkillById: getSkillById,
+ prettyJoin: prettyJoin
+};
+```
+
+当然函数也是一个对象。
+
+###自动化测试
+
+一直习惯用Travis CI,于是也继续用Travis Ci,``.travis.yml``配置如下所示:
+
+```yml
+language: node_js
+node_js:
+ - "0.10"
+
+notifications:
+ email: false
+
+branches:
+ only:
+ - gh-pages
+```
+
+使用gh-pages的原因是,我们一push代码的时候,就可以自动测试、部署等等,好处一堆堆的。
+
+接着我们需要在``package.json``里面添加脚本
+
+```javascript
+"scripts": {
+ "test": "mocha"
+ }
+```
+
+这样当我们push代码的时候便会自动跑所有的测试。因为mocha的主要配置是用``mocha.opts``,所以我们还需要配置一下``mocha.opts``
+
+ --reporter spec
+ --ui bdd
+ --growl
+ --colors
+ test/spec
+
+最后的``test/spec``是指定测试的目录。
+
+###Jshint
+
+> JSLint定义了一组编码约定,这比ECMA定义的语言更为严格。这些编码约定汲取了多年来的丰富编码经验,并以一条年代久远的编程原则 作为宗旨:能做并不意味着应该做。JSLint会对它认为有的编码实践加标志,另外还会指出哪些是明显的错误,从而促使你养成好的 JavaScript编码习惯。
+
+当我们的js写得不合理的时候,这时测试就无法通过:
+
+ line 5 col 25 A constructor name should start with an uppercase letter.
+ line 21 col 62 Strings must use singlequote.
+
+这是一种驱动写出更规范js的方法。
+
+
+###Mocha
+
+> Mocha 是一个优秀的JS测试框架,支持TDD/BDD,结合 should.js/expect/chai/better-assert,能轻松构建各种风格的测试用例。
+
+最后的效果如下所示:
+
+ Book,Link
+ Book Test
+ ✓ should return book label & url
+ Link Test
+ ✓ should return link label & url
+
+###测试用例
+
+简单地看一下Book的测试:
+
+```javascript
+/* global describe, it */
+
+var requirejs = require("requirejs");
+var assert = require("assert");
+var should = require("should");
+requirejs.config({
+ baseUrl: 'app/',
+ nodeRequire: require
+});
+
+describe('Book,Link', function () {
+ var Book, Link;
+ before(function (done) {
+ requirejs(['scripts/Book'、], function (Book_Class) {
+ Book = Book_Class;
+ done();
+ });
+ });
+
+ describe('Book Test', function () {
+ it('should return book label & url', function () {
+ var book_name = 'Head First HTML与CSS';
+ var url = 'http://www.phodal.com';
+ var books = {
+ label: book_name,
+ url: url
+ };
+
+ var _book = new Book(books);
+ _book.label.should.equal(book_name);
+ _book.url.should.equal(url);
+ });
+ });
+});
+```
+
+因为我们用``require.js``来管理浏览器端,在后台写测试来测试的时候,我们也需要用他来管理我们的依赖,这也就是为什么这个测试这么从的原因,多数情况下一个测试类似于这样子的。(用Jasmine似乎会是一个更好的主意,但是用习惯Jasmine了)
+
+```javascript
+describe('Book Test', function () {
+it('should return book label & url', function () {
+ var book_name = 'Head First HTML与CSS';
+ var url = 'http://www.phodal.com';
+ var books = {
+ label: book_name,
+ url: url
+ };
+
+ var _book = new Book(books);
+ _book.label.should.equal(book_name);
+ _book.url.should.equal(url);
+});
+});
+```
+
+最后的断言,也算是测试的核心,保证测试是有用的。
+
+##Code Climate来clean code与重构
+
+ - 当你写了一大堆代码,你没有意识到里面有一大堆重复。
+ - 当你写了一大堆测试,却不知道覆盖率有多少。
+
+这就是个问题了,于是偶然间看到了一个叫code climate的网站。
+
+###Code Climate
+
+> Code Climate consolidates the results from a suite of static analysis tools into a single, real-time report, giving your team the information it needs to identify hotspots, evaluate new approaches, and improve code quality.
+
+Code Climate整合一组静态分析工具的结果到一个单一的,实时的报告,让您的团队需要识别热点,探讨新的方法,提高代码质量的信息。
+
+简单地来说:
+
+- 对我们的代码评分
+- 找出代码中的坏味道
+
+于是,我们先来了个例子
+
+Rating | Name | Complexity | Duplication | Churn | C/M | Coverage | Smells
+--------|------|--------------|-------------|----------|---------|---------------------
+A | lib/coap/coap_request_handler.js | 24 | 0 | 6 | 2.6 | 46.4% | 0
+A | lib/coap/coap_result_helper.js | 14 | 0 | 2 | 3.4 | 80.0% | 0
+A | lib/coap/coap_server.js | 16 | 0 | 5 | 5.2 | 44.0% | 0
+A | lib/database/db_factory.js | 8 | 0 | 3 | 3.8 | 92.3% | 0
+A | lib/database/iot_db.js | 7 | 0 | 6 | 1.0 | 58.8% | 0
+A | lib/database/mongodb_helper.js | 63 | 0 | 11 | 4.5 | 35.0% | 0
+C | lib/database/sqlite_helper.js | 32 | 86 | 10 | 4.5 | 35.0% | 2
+B | lib/rest/rest_helper.js | 19 | 62 | 3 | 4.7 | 37.5% | 2
+A | lib/rest/rest_server.js | 17 | 0 | 2 | 8.6 | 88.9% | 0
+A | lib/url_handler.js | 9 | 0 | 5 | 2.2 | 94.1% | 0
+
+分享得到的最后的结果是:
+
+![Coverage][1]
+
+###代码的坏味道
+
+于是我们就打开``lib/database/sqlite_helper.js``,因为其中有两个坏味道
+
+Similar code found in two :expression_statement nodes (mass = 86)
+
+在代码的 ``lib/database/sqlite_helper.js:58…61 < >``
+
+```javascript
+ SQLiteHelper.prototype.deleteData = function (url, callback) {
+ 'use strict';
+ var sql_command = "DELETE FROM " + config.table_name + " where " + URLHandler.getKeyFromURL(url) + "=" + URLHandler.getValueFromURL(url);
+ SQLiteHelper.prototype.basic(sql_command, callback);
+```
+
+lib/database/sqlite_helper.js:64…67 < >
+
+与
+
+```javascript
+SQLiteHelper.prototype.getData = function (url, callback) {
+ 'use strict';
+ var sql_command = "SELECT * FROM " + config.table_name + " where " + URLHandler.getKeyFromURL(url) + "=" + URLHandler.getValueFromURL(url);
+ SQLiteHelper.prototype.basic(sql_command, callback);
+```
+
+只是这是之前修改过的重复。。
+
+原来的代码是这样的
+
+```javascript
+SQLiteHelper.prototype.postData = function (block, callback) {
+ 'use strict';
+ var db = new sqlite3.Database(config.db_name);
+ var str = this.parseData(config.keys);
+ var string = this.parseData(block);
+
+ var sql_command = "insert or replace into " + config.table_name + " (" + str + ") VALUES (" + string + ");";
+ db.all(sql_command, function (err) {
+ SQLiteHelper.prototype.errorHandler(err);
+ db.close();
+ callback();
+ });
+};
+
+SQLiteHelper.prototype.deleteData = function (url, callback) {
+ 'use strict';
+ var db = new sqlite3.Database(config.db_name);
+ var sql_command = "DELETE FROM " + config.table_name + " where " + URLHandler.getKeyFromURL(url) + "=" + URLHandler.getValueFromURL(url);
+ db.all(sql_command, function (err) {
+ SQLiteHelper.prototype.errorHandler(err);
+ db.close();
+ callback();
+ });
+};
+
+SQLiteHelper.prototype.getData = function (url, callback) {
+ 'use strict';
+ var db = new sqlite3.Database(config.db_name);
+ var sql_command = "SELECT * FROM " + config.table_name + " where " + URLHandler.getKeyFromURL(url) + "=" + URLHandler.getValueFromURL(url);
+ db.all(sql_command, function (err, rows) {
+ SQLiteHelper.prototype.errorHandler(err);
+ db.close();
+ callback(JSON.stringify(rows));
+ });
+};
+```
+说的也是大量的重复,重构完的代码
+
+```javascript
+SQLiteHelper.prototype.basic = function(sql, db_callback){
+ 'use strict';
+ var db = new sqlite3.Database(config.db_name);
+ db.all(sql, function (err, rows) {
+ SQLiteHelper.prototype.errorHandler(err);
+ db.close();
+ db_callback(JSON.stringify(rows));
+ });
+
+};
+
+SQLiteHelper.prototype.postData = function (block, callback) {
+ 'use strict';
+ var str = this.parseData(config.keys);
+ var string = this.parseData(block);
+
+ var sql_command = "insert or replace into " + config.table_name + " (" + str + ") VALUES (" + string + ");";
+ SQLiteHelper.prototype.basic(sql_command, callback);
+};
+
+SQLiteHelper.prototype.deleteData = function (url, callback) {
+ 'use strict';
+ var sql_command = "DELETE FROM " + config.table_name + " where " + URLHandler.getKeyFromURL(url) + "=" + URLHandler.getValueFromURL(url);
+ SQLiteHelper.prototype.basic(sql_command, callback);
+};
+
+SQLiteHelper.prototype.getData = function (url, callback) {
+ 'use strict';
+ var sql_command = "SELECT * FROM " + config.table_name + " where " + URLHandler.getKeyFromURL(url) + "=" + URLHandler.getValueFromURL(url);
+ SQLiteHelper.prototype.basic(sql_command, callback);
+};
+```
+
+重构完后的代码比原来还长,这似乎是个问题~~
+
#创建项目文档
#测试
#重构
+或许你应该知道了,重构是怎样的,你也知道重构能带来什么。在我刚开始学重构和设计模式的时候,我需要去找一些好的示例,以便于我更好的学习。有时候不得不创造一些更好的场景,来实现这些功能。
+
+有一天,我发现当我需要我一次又一次地重复讲述某些内容,于是我就计划着把这些应该掌握的技能放到Github上,也就有了[Artisan Stack](https://github.com/artisanstack) 计划。
+
+每个程序员都不可避免地是一个Coder,一个没有掌握好技能的Coder,算不上是手工艺人,但是是手工人。
+
+艺,需要有创造性的方法。
+
+#[前端技能训练: 重构一](http://www.phodal.com/blog/frontend-improve-refactor-javascript-code/)
+
+##为什么重构?
+
+> 为了更好的代码。
+
+在经历了一年多的工作之后,我平时的主要工作就是修Bug。刚开始的时候觉得无聊,后来才发现修Bug需要更好的技术。有时候你可能要面对着一坨一坨的代码,有时候你可能要花几天的时间去阅读代码。而,你重写那几十代码可能只会花上你不到一天的时间。但是如果你没办法理解当时为什么这么做,你的修改只会带来更多的bug。修Bug,更多的是维护代码。还是前人总结的那句话对:
+
+> 写代码容易,读代码难。
+
+假设我们写这些代码只要半天,而别人读起来要一天。为什么不试着用一天的时候去写这些代码,让别人花半天或者更少的时间来理解。
+
+如果你的代码已经上线,虽然是一坨坨的。但是不要轻易尝试,``没有测试的重构``。
+
+从前端开始的原因在于,写得一坨坨且最不容易测试的代码都在前端。
+
+让我们来看看我们的第一个训练,相当有挑战性。
+
+##重构uMarkdown
+
+代码及setup请见github: [js-refactor](https://github.com/artisanstack/js-refactor)
+
+###代码说明
+
+``uMarkdown``是一个用于将Markdown转化为HTML的库。代码看上去就像一个很典型的过程代码:
+
+```javascript
+/* code */
+while ((stra = micromarkdown.regexobject.code.exec(str)) !== null) {
+ str = str.replace(stra[0], '\n' + micromarkdown.htmlEncode(stra[1]).replace(/\n/gm, '\n');
+}
+
+/* headlines */
+while ((stra = micromarkdown.regexobject.headline.exec(str)) !== null) {
+ count = stra[1].length;
+ str = str.replace(stra[0], '
').replace(/\ /gm, ' ') + '\n' + micromarkdown.htmlEncode(stra[1]).replace(/\n/gm, '\n');
+}
+```
+
+提取方法成
+
+```javascript
+codeFilter: function (str, stra) {
+ return str.replace(stra[0], '
').replace(/\ /gm, ' ') + '\n' + micromarkdown.htmlEncode(stra[1]).replace(/\n/gm, '\n');
+ },
+```
+
+while语句就成了
+
+```javascript
+while ((stra = regexobject.code.exec(str)) !== null) {
+ str = this.codeFilter(str, stra);
+}
+```
+
+然后,运行所有的测试。
+
+```
+grunt test
+```
+
+同理我们就可以``mail``、``headline``等方法进行重构。接着就会变成类似于下面的代码,
+
+```javascript
+/* code */
+while ((execStr = regExpObject.code.exec(str)) !== null) {
+str = codeHandler(str, execStr);
+}
+
+/* headlines */
+while ((execStr = regExpObject.headline.exec(str)) !== null) {
+str = headlineHandler(str, execStr);
+}
+
+/* lists */
+while ((execStr = regExpObject.lists.exec(str)) !== null) {
+str = listHandler(str, execStr);
+}
+
+/* tables */
+while ((execStr = regExpObject.tables.exec(str)) !== null) {
+str = tableHandler(str, execStr, strict);
+}
+```
+
+然后你也看到了,上面有一堆重复的代码,接着让我们用JavaScript的``奇技浮巧``,即apply方法,把上面的重复代码变成。
+
+```javascript
+['code', 'headline', 'lists', 'tables', 'links', 'mail', 'url', 'smlinks', 'hr'].forEach(function (type) {
+ while ((stra = regexobject[type].exec(str)) !== null) {
+ str = that[(type + 'Handler')].apply(that, [stra, str, strict]);
+ }
+});
+```
+
+进行测试,blabla,都是过的。
+
+```javascript
+ Markdown
+ ✓ should parse h1~h3
+ ✓ should parse link
+ ✓ should special link
+ ✓ should parse font style
+ ✓ should parse code
+ ✓ should parse ul list
+ ✓ should parse ul table
+ ✓ should return correctly class name
+```
+
+快来试试吧, [https://github.com/artisanstack/js-refactor](https://github.com/artisanstack/js-refactor)
+
#Github连击
##100天
diff --git a/index.html b/index.html
index 632cd55..e840010 100644
--- a/index.html
+++ b/index.html
@@ -117,16 +117,35 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
').replace(/\ /gm, ' ') + '
真看不出来两者有什么相似的地方 。。。。
-之前曾经分析过一些Github的用户行为,现在我们先来说说Github上的Star吧。(截止: 2015年3月9日23时。)
| Rating | +Name | +Complexity | +Duplication | +Churn | +C/M | +Coverage | +
|---|---|---|---|---|---|---|
| A | +lib/coap/coap_request_handler.js | +24 | +0 | +6 | +2.6 | +46.4% | +
| A | +lib/coap/coap_result_helper.js | +14 | +0 | +2 | +3.4 | +80.0% | +
| A | +lib/coap/coap_server.js | +16 | +0 | +5 | +5.2 | +44.0% | +
| A | +lib/database/db_factory.js | +8 | +0 | +3 | +3.8 | +92.3% | +
| A | +lib/database/iot_db.js | +7 | +0 | +6 | +1.0 | +58.8% | +
| A | +lib/database/mongodb_helper.js | +63 | +0 | +11 | +4.5 | +35.0% | +
| C | +lib/database/sqlite_helper.js | +32 | +86 | +10 | +4.5 | +35.0% | +
| B | +lib/rest/rest_helper.js | +19 | +62 | +3 | +4.7 | +37.5% | +
| A | +lib/rest/rest_server.js | +17 | +0 | +2 | +8.6 | +88.9% | +
| A | +lib/url_handler.js | +9 | +0 | +5 | +2.2 | +94.1% | +
分享得到的最后的结果是:
+[Coverage][1]
+于是我们就打开lib/database/sqlite_helper.js,因为其中有两个坏味道
Similar code found in two :expression_statement nodes (mass = 86)
+在代码的 lib/database/sqlite_helper.js:58…61 < >
SQLiteHelper.prototype.deleteData = function (url, callback) {
+ 'use strict';
+ var sql_command = "DELETE FROM " + config.table_name + " where " + URLHandler.getKeyFromURL(url) + "=" + URLHandler.getValueFromURL(url);
+ SQLiteHelper.prototype.basic(sql_command, callback);lib/database/sqlite_helper.js:64…67 < >
+与
+SQLiteHelper.prototype.getData = function (url, callback) {
+ 'use strict';
+ var sql_command = "SELECT * FROM " + config.table_name + " where " + URLHandler.getKeyFromURL(url) + "=" + URLHandler.getValueFromURL(url);
+ SQLiteHelper.prototype.basic(sql_command, callback);只是这是之前修改过的重复。。
+原来的代码是这样的
+SQLiteHelper.prototype.postData = function (block, callback) {
+ 'use strict';
+ var db = new sqlite3.Database(config.db_name);
+ var str = this.parseData(config.keys);
+ var string = this.parseData(block);
+
+ var sql_command = "insert or replace into " + config.table_name + " (" + str + ") VALUES (" + string + ");";
+ db.all(sql_command, function (err) {
+ SQLiteHelper.prototype.errorHandler(err);
+ db.close();
+ callback();
+ });
+};
+
+SQLiteHelper.prototype.deleteData = function (url, callback) {
+ 'use strict';
+ var db = new sqlite3.Database(config.db_name);
+ var sql_command = "DELETE FROM " + config.table_name + " where " + URLHandler.getKeyFromURL(url) + "=" + URLHandler.getValueFromURL(url);
+ db.all(sql_command, function (err) {
+ SQLiteHelper.prototype.errorHandler(err);
+ db.close();
+ callback();
+ });
+};
+
+SQLiteHelper.prototype.getData = function (url, callback) {
+ 'use strict';
+ var db = new sqlite3.Database(config.db_name);
+ var sql_command = "SELECT * FROM " + config.table_name + " where " + URLHandler.getKeyFromURL(url) + "=" + URLHandler.getValueFromURL(url);
+ db.all(sql_command, function (err, rows) {
+ SQLiteHelper.prototype.errorHandler(err);
+ db.close();
+ callback(JSON.stringify(rows));
+ });
+};说的也是大量的重复,重构完的代码
+SQLiteHelper.prototype.basic = function(sql, db_callback){
+ 'use strict';
+ var db = new sqlite3.Database(config.db_name);
+ db.all(sql, function (err, rows) {
+ SQLiteHelper.prototype.errorHandler(err);
+ db.close();
+ db_callback(JSON.stringify(rows));
+ });
+
+};
+
+SQLiteHelper.prototype.postData = function (block, callback) {
+ 'use strict';
+ var str = this.parseData(config.keys);
+ var string = this.parseData(block);
+
+ var sql_command = "insert or replace into " + config.table_name + " (" + str + ") VALUES (" + string + ");";
+ SQLiteHelper.prototype.basic(sql_command, callback);
+};
+
+SQLiteHelper.prototype.deleteData = function (url, callback) {
+ 'use strict';
+ var sql_command = "DELETE FROM " + config.table_name + " where " + URLHandler.getKeyFromURL(url) + "=" + URLHandler.getValueFromURL(url);
+ SQLiteHelper.prototype.basic(sql_command, callback);
+};
+
+SQLiteHelper.prototype.getData = function (url, callback) {
+ 'use strict';
+ var sql_command = "SELECT * FROM " + config.table_name + " where " + URLHandler.getKeyFromURL(url) + "=" + URLHandler.getValueFromURL(url);
+ SQLiteHelper.prototype.basic(sql_command, callback);
+};重构完后的代码比原来还长,这似乎是个问题~~
或许你应该知道了,重构是怎样的,你也知道重构能带来什么。在我刚开始学重构和设计模式的时候,我需要去找一些好的示例,以便于我更好的学习。有时候不得不创造一些更好的场景,来实现这些功能。
+有一天,我发现当我需要我一次又一次地重复讲述某些内容,于是我就计划着把这些应该掌握的技能放到Github上,也就有了Artisan Stack 计划。
+每个程序员都不可避免地是一个Coder,一个没有掌握好技能的Coder,算不上是手工艺人,但是是手工人。
+艺,需要有创造性的方法。
+++为了更好的代码。
+
在经历了一年多的工作之后,我平时的主要工作就是修Bug。刚开始的时候觉得无聊,后来才发现修Bug需要更好的技术。有时候你可能要面对着一坨一坨的代码,有时候你可能要花几天的时间去阅读代码。而,你重写那几十代码可能只会花上你不到一天的时间。但是如果你没办法理解当时为什么这么做,你的修改只会带来更多的bug。修Bug,更多的是维护代码。还是前人总结的那句话对:
+++写代码容易,读代码难。
+
假设我们写这些代码只要半天,而别人读起来要一天。为什么不试着用一天的时候去写这些代码,让别人花半天或者更少的时间来理解。
+如果你的代码已经上线,虽然是一坨坨的。但是不要轻易尝试,没有测试的重构。
从前端开始的原因在于,写得一坨坨且最不容易测试的代码都在前端。
+让我们来看看我们的第一个训练,相当有挑战性。
+代码及setup请见github: js-refactor
+uMarkdown是一个用于将Markdown转化为HTML的库。代码看上去就像一个很典型的过程代码:
/* code */
+while ((stra = micromarkdown.regexobject.code.exec(str)) !== null) {
+ str = str.replace(stra[0], '<code>\n' + micromarkdown.htmlEncode(stra[1]).replace(/\n/gm, '<br/>').replace(/\ /gm, ' ') + '</code>\n');
+}
+
+/* headlines */
+while ((stra = micromarkdown.regexobject.headline.exec(str)) !== null) {
+ count = stra[1].length;
+ str = str.replace(stra[0], '<h' + count + '>' + stra[2] + '</h' + count + '>' + '\n');
+}
+
+/* mail */
+while ((stra = micromarkdown.regexobject.mail.exec(str)) !== null) {
+ str = str.replace(stra[0], '<a href="mailto:' + stra[1] + '">' + stra[1] + '</a>');
+}选这个做重构的开始,不仅仅是因为之前在写EchoesWorks的时候进行了很多的重构。而且它更适合于,重构到设计模式的理论。让我们在重构完之后,给作者进行pull request吧。
Markdown的解析过程,有点类似于Pipe and Filters模式(架构模式)。
Filter即我们在代码中看到的正规表达式集:
+regexobject: {
+ headline: /^(\#{1,6})([^\#\n]+)$/m,
+ code: /\s\`\`\`\n?([^`]+)\`\`\`/g他会匹配对应的Markdown类型,随后进行替换和处理。而``str```,就是管理口的输入和输出。
+接着,我们就可以对其进行简单的重构。
+(ps: 推荐用WebStrom来做重构,自带重构功能)
+作为一个示例,我们先提出codeHandler方法,即将上面的
+/* code */
+while ((stra = micromarkdown.regexobject.code.exec(str)) !== null) {
+ str = str.replace(stra[0], '<code>\n' + micromarkdown.htmlEncode(stra[1]).replace(/\n/gm, '<br/>').replace(/\ /gm, ' ') + '</code>\n');
+}提取方法成
+codeFilter: function (str, stra) {
+ return str.replace(stra[0], '<code>\n' + micromarkdown.htmlEncode(stra[1]).replace(/\n/gm, '<br/>').replace(/\ /gm, ' ') + '</code>\n');
+ }, while语句就成了
+while ((stra = regexobject.code.exec(str)) !== null) {
+ str = this.codeFilter(str, stra);
+}然后,运行所有的测试。
+grunt test
+同理我们就可以mail、headline等方法进行重构。接着就会变成类似于下面的代码,
/* code */
+while ((execStr = regExpObject.code.exec(str)) !== null) {
+str = codeHandler(str, execStr);
+}
+
+/* headlines */
+while ((execStr = regExpObject.headline.exec(str)) !== null) {
+str = headlineHandler(str, execStr);
+}
+
+/* lists */
+while ((execStr = regExpObject.lists.exec(str)) !== null) {
+str = listHandler(str, execStr);
+}
+
+/* tables */
+while ((execStr = regExpObject.tables.exec(str)) !== null) {
+str = tableHandler(str, execStr, strict);
+}然后你也看到了,上面有一堆重复的代码,接着让我们用JavaScript的奇技浮巧,即apply方法,把上面的重复代码变成。
['code', 'headline', 'lists', 'tables', 'links', 'mail', 'url', 'smlinks', 'hr'].forEach(function (type) {
+ while ((stra = regexobject[type].exec(str)) !== null) {
+ str = that[(type + 'Handler')].apply(that, [stra, str, strict]);
+ }
+});进行测试,blabla,都是过的。
+ Markdown
+ ✓ should parse h1~h3
+ ✓ should parse link
+ ✓ should special link
+ ✓ should parse font style
+ ✓ should parse code
+ ✓ should parse ul list
+ ✓ should parse ul table
+ ✓ should return correctly class name快来试试吧, https://github.com/artisanstack/js-refactor
我也是蛮拼的,虽然我想的只是在Github上连击100~200天,然而到了今天也算不错。
@@ -1308,7 +1819,7 @@ pipe.execute()虽说算法很重要,但是编码才是基础能力。算法与编程在某种程度上是不同的领域,算法编程是在编程上面的一级。算法写得再好,如果别人很难直接拿来复用,在别人眼里就是shit。想出能work的代码一件简单的事,学会对其重构,使之变得更易读就是一件有意义的事。
于是,在某一时刻在Github上创建了一个组织,叫Artisan Stack。当时想的是在Github寻找一些JavaScript项目,对其代码进行重构。但是到底是影响力不够哈,参与的人数比较少。
-如果你懂得如何写出高可读的代码,那么我想你是不需要这个的,但是这意味着你花了更多的时候在思考上了。当谈论重构的时候,让我想起了TDD(测试驱动开发)。即使不是TDD,那么如果你写着测试,那也是可以重构的。(之前写过一些利用Intellij IDEA重构的文章:提炼函数、以查询取代临时变量、重构与Intellij Idea初探、内联函数)
在各种各样的文章里,我们看到过一些相关的内容,最好的参考莫过于《重构》一书。最基础不过的原则便是函数名,取名字很难,取别人能读懂的名字更难。其他的便有诸如长函数、过大的类、重复代码等等。在我有限的面试别人的经历里,这些问题都是最常见的。