赞
踩
方法/函数,是编程中最常见的概念。我们几乎对其已经熟悉到不能再熟悉了。但是,由于承载了大部分的业务逻辑,大多数的bug都出现在的方法体/函数体的定义中。
我们先看如下实例代码:
- // 根据权限过滤相册,并返回具有权限的相册列表
- public List<Album> filterByPermission(String loginPersonId, List<Album> albums) {
- // 如果相册列表的第一个所有者是当前用户,那么直接返回整个列表
- if (loginPersonId == albums.get(0).getPersonId()) {
- return albums;
- }
-
- List<Album> newAlbums = new ArrayList<Album>();
- ...
- ...
- return newAlbums;
- }
上面这段代码,从方法名上可以看到是根据某个用户的ID来过滤要访问的相册列表。就像你访问他人的QQ相册一样。如果目标对象有10个相册,如果当前用户是作者,自然可以访问所有相册;如果你是对方好友,可能只能访问其中的8个,如果只是游客,可能只能访问公开的1个相册。该方法就是用来完成过滤功能的。
第一句,personId == albums[0],那么,登录用户就是相册的作者。相册作者当然拥有所有相册的权限,所以返回原始的相册。这里就有一个假设,所有相册的personId完全相同。这就属于产品场景代入感太强,而忽略了方法本身所含有的意义。这样写,至少会带来以下方面的隐患:
1. 重用隐患。其他人调用这个方法,会出现错误,因为从方法名,看不出你的业务意义会有隐含的意义。换句话说,这个方法使用了很通用的方法名,却有不少隐含的业务限制在里面。暴露出去就是很大的问题。
2.安全隐患。有人把自己拥有的相册ID追加到最前面,而将很多其他的相册ID追加到后面。那么会产生什么样的效果呢?利用第一个相册(自己的)进行判断,结果为true,直接返回所有,相当于拥有了所有相册的权限。唯一还能正常工作的场景,可能就是调用者自己增加了这方面的校验和限制。
这属于典型的业务场景带入太深——假定该方法被调用到时,一定是进入单个人的相册空间。对于列表中的每个相册均进行判断才是正确的写法,修改后的代码可以写作:
- public List<Album> filterByPermission(String loginPersonId, Album[] albums) {
- List<Album> newAlbums = new ArrayList<Album>();
-
- for album in albums {
- if album.getPersonId() == loginPersonId || hasPermission(loginPersonId, album) {
- newAlbums.add(album);
- }
-
- }
- return newAlbums;
- }
修复完bug并不是目的。我们真正要探究的是,思想深处为什么会产生这样的想法。为何会将业务场景过多带入到代码实现中?如果程序员在编写代码时,只将albums参数当做相册列表来看待,而不做过多业务假设,便不会写出如此脆弱的代码。
思索良久,我得出了“纯粹”两个字。参数是纯粹的,参数中所蕴含的意义,仅仅与其数据结构/数据类型相关。一旦脱离“纯粹”二字,便容易让我们有各种假设,导致只有在特定场景下才正确的代码。
我们再来看一段代码:
- public boolean checkArticleDeletedStatusByTitle(String title) {
- List<Article> articles = this.queryByTile(title)
- if articles.size() == 0 {
- return true;
- }
- if (articles[0].getStatus() == DELETED) {
- return true;
- }
- ... 其他逻辑
- }
该方法首先根据标题,查找文章列表。然后,获得文章列表的第一篇文章,并判断第一篇文章的状态是否为删除。
当问到为什么会有这样的逻辑时,同事告诉我,因为queryByTitile()方法,已经在数据库中按照【状态】字段进行了倒序排序。而状态值——DELETED,如果存在,一定是最大值。
这其实假定了以后不会新增新的状态值,但万一新增状态,即使没有改动到该代码逻辑,但是该代码却引发了bug。
我们可以得出结论,这是针对this.queryByTile()方法的返回值,带入了特定场景。而并未将其当做单纯的List<Artticle>来看待。这是针对返回值,为按照“纯粹”的规则进行看待。
问题很简单,修复问题也很简单。但这完全是意识问题。我想,如果用“纯粹”来抽象方法的“参数”和“返回值”应该是比较恰当的。当我们进行逻辑编写时,无论是参数,还是调用其他方法的返回值,从“纯粹”的角度来看待它们,不带入任何业务场景。看待问题的角度和思考问题的思路,是否会有不同呢?
欢迎评论区讨论!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。