2019独角兽企业重金招聘Python工程师标准>>>

例子: (本文所探讨内容皆使用该例子)

场景:编写一个文章详情页。 该页面需要从服务器加载文章内容和评论,然后显示在页面上。

一 time decoupling

1. callback hell

加载文章内容的回调函数中加载评论。

// 获取文章信息
function getPost(i, callback) {setTimeout(()=> callback({ id: i, title: 'Love them tasks' }), 500)return { id: i, title: 'Love them tasks' };
}
// 获取对应评论列表
function getComments(post) {setTimeout((post.id)=> callback(["This book should be illegal","Monads are like space burritos"]), 500)});return ["This book should be illegal","Monads are like space burritos"];
}var makeComments = _.reduce(function(acc, c){ return acc+"<li>"+c+"</li>" }, "");var render = _.curry(function(p, cs) { return "<div>"+p.title+"</div>"+makeComments(cs); });// 调用
const post = getPost(i, getComments);
render(post, 'how can i get the returned value of getComments?');
// 所以必须使用基于事件监听的方式,就像
this.trigger(comment: ["This book should be illegal","Monads are like space burritos"]);
// 也就是说返回值为undefined

上面这种方法返回值为undefined,实际上我们目前打单和短信中存在大量这样的代码。   这样存在以下几个问题

①不利于测试

如果说函数没有返回值,那么他到底执行了没有呢?

没有返回值的函数,怎么去判断他运行了没有呢?  判断会变得非常别扭: 比如我写了一个函数叫

setStyle(selector, key, value) {

$(selector).css(key, value);

}

就是给dom设置样式。并且没有返回值,那么就必须要检测dom节点的style 是否符合预期,这种测试是别扭的。 现在我们来改写一个:

setStyle(selector, key, value) {

$(selector).css(key, value);

return value;

}

这样就可以容易测试了。

② 不能够组合

如果对linux熟悉的话,应该知道pipe。 代码组合就类似于此,将你的task 分成若干task执行,每一个task有明确的返回值,这样才能保证task sequence 按照预期执行

上面的例子可以这么写

 Task.of(render).ap(getPost(2)).ap(getComments(2));  

不理解没有关系后边会详细介绍

2. normal promise || generator(es6)

上面的callback 如果在嵌套层数更多的时候,将会更加糟糕。下面介绍一个promise写法。 promise 写法看起来是可以在返回值没有的时候就去操作他了。

上面的代码promise重写:

// 获取文章信息
function getPost(i) {return new Promise(function (resolve, reject) {setTimeout(function () {resolve({ id: i, title: 'Love them tasks' });}, 500);});
}
// 获取对应评论列表
function getComments(post) {return new Promise(function (resolve, reject) {setTimeout(function () {resolve(["This book should be illegal","Monads are like space burritos"]);}, 500);});
}var makeComments = _.reduce(function(acc, c){ return acc+"<li>"+c+"</li>" }, "");var render = _.curry(function(p, cs) { return "<div>"+p.title+"</div>"+makeComments(cs); });// 调用
const post = getPost(i).then((post)=>{render(post);getComments(post).then((comment)=>{render(comment);});
})
// PS: 如果只关注后边的返回值的话就更简单了
var comments = R.pipeP(getPost, getComments)(i);
// pipeP 是管道工具方法,该方法作用是参数从左到右依次执行, 上一个函数返回值是下一个函数的参数

目前npm 很多第三方都没有普及promise,所以使用难免会有问题。这里推荐一个库promisify

promisify 就是把带有 callback 函数,变成重新用 promise 来实现的一种技术方案,它能一劳永逸的解决

看起来清爽多了

3.OUCH

这才是我要讲的重点

代码:

// 获取文章信息
function getPost(i) {return new Task(function (rej, res) {setTimeout(function () { res({ id: i, title: 'Love them futures' }); }, 300);});
}
// 获取评论
function getComments(i) {return new Task(function (rej, res) {setTimeout(function () {res(["This book should be illegal", "Monads are like space burritos"]);}, 300);});
}var makeComments = _.reduce(function(acc, c){ return acc+"<li>"+c+"</li>" }, "");var render = _.curry(function(p, cs) { return "<div>"+p.title+"</div>"+makeComments(cs); });
// 调用var renderedPage = Task.of(render).ap(getPost(2)).ap(getComments(2));
// ap是函数式编程的一个概念,简单的说就是能够把一个 functor 的函数值应用到另一个 functor 的值上
// Task是函数式编程中的容器概念的实现  我使用的实现库是task.io

附:单元测试

renderedPage.fork(console.log, function (html) {assert.equal("<div>Love them tasks</div><li>This book should be illegal</li><li>Monads are like space burritos</li>", html);done();
});
// 你当然可以针对数据写测试

二 exception handling

之前写了一篇文章关于如何优雅的书写代码中提到纯函数,

参见:http://my.oschina.net/wanjubang/blog/675953

http://my.oschina.net/wanjubang/blog/703907

1.impure handling

之前我写的一个文章介绍了错误上传,参见:http://my.oschina.net/wanjubang/blog/701843#OSC_h2_1

为了简单起见,写的就是不纯的错误处理。那么有什么关系呢?

① 函数报错会影响后面依赖的函数执行。

②  函数不够纯粹的话,就无法把程序的不确定性掌控在一个范围。 其结果就是你写的程序到处都可能出错,而你不知情。 就像:

靠巧合编程

2.pure handling

针对上面提到的两点。 这里设计一个容器。任何数据都放到容器中,任何数据的操作都是对容器的操作。对monad熟悉的话,可能对这个概念不陌生,那么现在就来将这个思想应用在我们的代码上。

如果对monad不熟悉的,可以看下这个科普文章:http://www.ruanyifeng.com/blog/2015/07/monad.html

上面的例子中我们将函数用Task封装。然后给Task添加了ap方法 和 of 方法。 这样我们就可以把任意一个函数应用在另一个函数上。

举一个简单的例子: 我要实现一个方法,这个方法的功能是将两个数相加,并将相加的结果返回。

var add = R.curry((x, y)=> x + y);

var maybeAdd = function(x, y) {
  return Maybe.of(add).ap(Maybe.of(x)).ap(Maybe.of(y));
};

这里maybe就是一个容器,从名字可以看出不确定性。 也就是说这个方法可能返回一个正确的数字Maybe(3)或者返回一个Maybe(null).

有了往容器存值的方法,当然也要有一个统一操作容器数据的方法,这个方法不会因容器里数据类型不同而不同。

Maybe 完整定义如下:

/ Maybe
Maybe = function(x) {this.__value = x;
};Maybe.of = function(x) {return new Maybe(x);
};Maybe.prototype.isNothing = function(f) {return (this.__value === null || this.__value === undefined);
};Maybe.prototype.map = function(f) {return this.isNothing() ? Maybe.of(null) : Maybe.of(f(this.__value));
};Maybe.prototype.chain = function(f) {return this.map(f).join();
};Maybe.prototype.ap = function(other) {return this.isNothing() ? Maybe.of(null) : other.map(this.__value);
};Maybe.prototype.join = function() {return this.isNothing() ? Maybe.of(null) : this.__value;
}Maybe.prototype.inspect = function() {return 'Maybe('+inspect(this.__value)+')';
}

maybeAdd(1)(2)  // maybe(3)

maybeAdd(3,5) // maybe(8)

maybeAdd(3,null) // maybe(null)

并不会影响后面程序运行,直接直接终止程序。

这似乎看起来还不够, 我们似乎无法看出来错误信息了。

我们继续在maybe基础上扩展一下Either:

// Either
Either = function() {};
Either.of = function(x) {return new Right(x);
}Left = function(x) {this.__value = x;
}// TODO: remove this nonsense
Left.of = function(x) {return new Left(x);
}Left.prototype.map = function(f) { return this; }
Left.prototype.ap = function(other) { return this; }
Left.prototype.join = function() { return this; }
Left.prototype.chain = function() { return this; }
Left.prototype.inspect = function() {return 'Left('+inspect(this.__value)+')';
}Right = function(x) {this.__value = x;
}// TODO: remove in favor of Either.of
Right.of = function(x) {return new Right(x);
}Right.prototype.map = function(f) {return Right.of(f(this.__value));
}Right.prototype.join = function() {return this.__value;
}Right.prototype.chain = function(f) {return f(this.__value);
}Right.prototype.ap = function(other) {return this.chain(function(f) {return other.map(f);});
}Right.prototype.join = function() {return this.__value;
}Right.prototype.chain = function(f) {return f(this.__value);
}Right.prototype.inspect = function() {return 'Right('+inspect(this.__value)+')';
}

重写一个刚才的add方法

var add = R.curry((x, y)=> x + y);

var eitherAdd = function(x, y) {

return R.is(Number, x) && R.is(Number, y) ?

Right.of(add).ap(Right.of(x)).ap(Right.of(y)):

Left.of("arguments should be number");
};

eitherAdd (1)(2)  // Right(3)

eitherAdd (3,5) // Right(8)

eitherAdd (3,null) // Left("arguments should be number")

报错是不是更友好了? 当然做测试也是异常的简单

拿maybe距离, either 类似不赘述

assert.deepEqual(Maybe.of(8), maybeAdd(3, 5));
assert.deepEqual(Maybe.of(null), maybeAdd(null, 3));

转载于:https://my.oschina.net/wanjubang/blog/704162

代码评审 16.7.1相关推荐

  1. 敏捷研发之代码评审与工具

    代码评审的主要内容 编码规范问题:命名不规范等 代码结构问题:重复代码.巨大大方法和类.分层不当.紧耦合 实现问题:错误验证.异常处理.事务划分.线程.性能.安全.实现过于复杂.代码可读性不佳.扩展性 ...

  2. 对不起,我的代码评审毁了一个程序员!

    技术使人膨胀?! 在过往的 coding 的生活中,你是否有过被技术前辈 diss 得找不着北的经历? 作者 | Philipp Ranzhin 译者 | 弯月 责编 | 屠敏 出品 | CSDN(I ...

  3. 11 个高效的同行代码评审最佳实践

    看到一篇同行评审的文章,感觉不错,特转载. 原文链接: http://www.ibm.com/developerworks/cn/rational/11-proven-practices-for-pe ...

  4. 【原创】项目管理杂谈(1):代码评审这点事,元芳你怎么看

    为什么80%的码农都做不了架构师?>>>    申明:因学识有限,某些见解和观点或有不妥,如有冒犯还请见谅.如需与作者联系,见文章底部个人签名处,乐于交流.Q群:210285832, ...

  5. 视频分享:编码与代码评审-质量与现实的最激烈冲突点(完整版)

    内容提要: 某次进度计划评审,QA发现缺少代码评审的环节,于是PM在所有编码任务之后增加了一个评审代码的任务,于是这个进度计划就通过评审了.代码评审不是走形式,领衔项目工作的你一定深受劣质代码的影响, ...

  6. 15个最佳的代码评审(Code Review)工具

    代码评审可以被看作是计算机源代码的测试,它的目的是查找和修复引入到开发阶段的应用程序的错误,提高软件的整体素质和开发者的技能.代码审查程序以各种形式,如结对编程,代码抽查等.在这个列表中,我们编制了1 ...

  7. 关于代码评审的微博讨论汇集

    编者按: 7月12日,weibo上 @自律自强 发表了一条微博:十几年来的软件项目经历告诉我,评审是最有效也是成本最低的质量保证和提升的手段,设计书和代码100%肉眼全覆盖绝对值得,而且还是迅速提高新 ...

  8. 远程开发 代码提示_VS Code 远程开发和代码评审实践

    很多年前的一天,我在 TypeScript 仓库下创建了一个 issue:微软打算拿 Monaco 来干嘛?接着第二天微软就发布了 VS Code.这个巧合我吹了五年还孜孜不倦. 因为已经用上了 Ty ...

  9. node 16位 转24位_C代码实现16位和32位数据字节序转换

    点击上方公众号名称关注,获得更多内容 ✎ 编 者 悟 语 每天给自己一个存在的意识,这样才会有一颗认真的心. 文 章 导 读 今天给大家用C代码实现下大小端字节序的转换代码,感谢的小伙伴可以拿下来撸一 ...

  10. 代码评审的不可能三角

    Code Review 是保证代码质量的重要手段之一,但许多研发团队中它常常由于各种原因并未得到真正的落地.为什么会这样呢?本文希望用一个非常简单的观点来理解这个现象,并据此给出一点优化的想法. 观点 ...

最新文章

  1. 用 Python 制作酷炫的可视化大屏,特简单!
  2. 最快让你上手ReactiveCocoa之进阶篇
  3. 2020年春季学期信号与系统课程作业参考答案-第十次作业
  4. 中文乱码,也许这个小技巧可以帮到你
  5. C语言中static详细分析
  6. tcp序列号为什么是随机的_每个开发人员都应该掌握的TCP知识
  7. Java猿面试_猿灯塔:关于Java面试,你应该准备这些知识点
  8. Windows 10+Ubuntu 16.04在MBR分区上安装双系统之后没有Windows 10的启动菜单解决方法...
  9. java plus方法_Java.math.BigDecimal.plus()方法实例
  10. 使用PowerShell将字符串拆分为数组
  11. 简单绑定要注意的问题_AX
  12. WinRunner介绍
  13. Opencv图像边缘检测——Roberts算子(手写)、Sobel算子(手写和调包)、Scharr算子、Laplacian算子
  14. dcdc模块降额设计_大功率IGBT模块及驱动技术
  15. Head First Java 目录结构
  16. 1-4 正弦和余弦
  17. Android使用Github Actions持续集成并自动上传apk到蒲公英App内测分发平台(含证书密码脱敏)
  18. 无法给变量添加属性导致出问题
  19. F1巴林揭幕阿隆索拔头筹 雷克南一次进站成就季军
  20. OpenSSL安装使用(二):OpenSSL安装说明

热门文章

  1. linux .net环境变量,Linux编程 系统环境变量位置, 环境变量持久化
  2. php curl 返回cookie_分享新浪图床上传接口PHP源码
  3. 打开pdf文件提示文件过大_如何把pdf文件进行分割?拆分pdf文件的方法分享
  4. split函数python_python有split函数吗
  5. 点赞功能java_jquery点赞功能实现代码 点个赞吧!
  6. 2021谷饶中学高考成绩查询,2020汕头地理生物中考成绩查询入口
  7. 凯撒密码加密,解密的实现,可以在项目上使用
  8. dlib 怎么安装vs2017_VS2017+DLib_19.17详细配置教程
  9. python写出租车计费系统_出租车计费系统设计 Java
  10. CS224N刷题——Assignment2.1_TensorflowSoftmax