如何写出容易阅读的代码

一. 开篇废话

很多年前,我写过一篇文章叫《关于阅读代码 》。是在2014年写的,那时候刚开始写博客。根据后台统计数据显示,这篇文章这些年来也没什么人看。

其实文章分两种,一种是给自己看的,一种是给别人看的。就像写代码一样,有的代码你自己写的爽就行了,也没打算给别人看;还有一种,写出来的目的就是为了给别人看的。

所以回到《关于阅读代码》这篇文章,当时写的时候更多的是因为随着职业经历的积累自己有一些了感悟,觉得自己有一些绝妙的想法,想记录下来。这篇文章也一样。

作为程序员,工作职责中,可能写代码只占很小的一部分,不过因人而异。如果作为Salesfoce从业者,工作职责中80%以上都是写代码的话,我倒是觉得这其实是一件挺可悲的事情。应该考虑换一个平台或者试试别的角色。

但是,单就开发这部分来说,由于标准功能的存在,代码在Salesforce开发中的比重也不再如传统开发的100%。可以说我们的目标就是要干掉所有代码。

虽然说我们要干掉代码,但是代码却极其重要。毕竟所有标准功能无法实现的功能还是要代码去实现。这时候“对人友善”的代码在Salesforce开发活动中就格外的重要。毕竟在梳理业务流程的时候,谁也不想面对方便面一样的代码皱眉头。或者,被问到头上的时候,只能╮(╯▽╰)╭说:“这个功能好用但我不知道为什么。”

二. 不容易阅读的代码怎么来的

这些年我发现一个现象。习惯疯狂抽象分层的Java程序员,在转Salesforce开发之后往往更容易写出散文诗式流水账代码。然后再进行几次需求变动,代码就变成了方便面。

那么,为什么一个优秀的coder做salesforce之后会写出方便面代码?我觉得原因是接受到的信息的不同。

在Java时代,代码写手在动手之前接受到的信息为具体的功能模块,已经有架构师或同等角色的人将功能完全拆解或者绘制好蓝图。代码写手要做的是实现规划好的功能模块,将自己的模块成功嵌入到大框架内。而Salesforce代码写手接受的信息往往是业务描述——只是说了要做哪些事情,而不是严谨的功能模块划分。

举个例子,就像做意大利面,非Salesforce的厨师被提供了一个精确的食谱和严格的验收标准,要做的是用规定的食材严格按照食谱在指定地点把意大利面做好就行。而Salesforce的厨师只是被告知要做一盘什么味道的意大利面,可能连食材都还没买,到做好上桌之前都要自己搞定。

如果有自由发挥的空间,就像在饭店点蛋炒饭,每个厨师都会做出不同的味道。

继续阅读“如何写出容易阅读的代码”

Javascript版的String.format()

Salesforce的Apex语言作为Java的高度豪华定制版。不仅提供了很多Java原生就有的很好的方法与语法。同时也对Java进行了纷纷总总的简化。
比如Apex的String.format方法。
Apex的format方法提供了一个简单的方式可以对字符串模板进行关键字替换。
下面是官方文档的例子。

String template = '{0} was last updated {1}';
List<Object> parameters = new List<Object> {'Universal Containers', DateTime.newInstance(2018, 11, 15) };
String formatted = String.format(template, parameters);
System.debug ('Newly formatted string is:' + formatted);

如此一来,我们在custom label里做好带{x}字样的字符串模板,就可以随心所欲的动态生成内容了。

但是,随着进入Lightning时代,在Lightning Component前端承担的业务和内容越来越多,随之而来的就是出现了在前端直接获取字符串模板并动态生成内容的需求。
而Aura库和原生Javascript都没有提供类似与Apex的String.format方法。

俗话说,自己动手丰衣足食。那就自己写一个吧。
首先在Helper.js里面添加如下format方法。

    },    //.......... Other Helper Method
    format: function(label, args) {
        for (var k in args) {
            label = label.replace("{" + k + "}", args[k]);
        }
        return label;
    },
  // .......... Other Helper Method

然后在Controller.js中使用的时候,如同Apex版,第一个参数传入模板,第二个参数为按模板参数顺序传入的字符串数组。

let template = "{0} was last updated {1}";
let myLabel = helper.format(template , ["Universal Containers", "2018/11/15"]);

如何在使用e.force:createRecord创建数据之后阻止跳转到数据详细页面

有一天,小明同学和他的同伙们在实现一个需求。

三天前我路过他座位的时候简单的了解了下,感觉功能都实现的差不多了,怎么这会又开始一堆人聚一起叽叽喳喳的乱叫什么”CreateRecord事件”啦,什么“自定义Modal”啦,还让不让人静一静了。

“喂喂喂,怎么了怎么了?”。我实在忍不住上去扒拉开了最外面的几个。
被围在最里面的可怜人回头看到了我,立马趁机脱离开了包围圈。扯了扯衣角,开始介绍起情况来了。“是这样的,这个需求是我几天前给你介绍过的,就是一个Lightning Component上有一个按钮,然后点一下弹出一个新建数据的页面。”

“嗯嗯,然后呢”。我点了点头,心想着果然nothing new under sunshine,还是那个需求。
“然后我决定使用createRecord事件,就是e.force:createRecord。”,说着他扒拉开站在他身后的人,俯身用鼠标高亮了$A.get(“e.force:createRecord”)那行代码。“然后我们发现,居然在EditForm点保存之后,就直接跳到了新建数据的详细页面!”
在我开头之前,他立马切到了e.force:createRecord官方文档的页面,指在上面赶紧补充到,“我看了官方文档,上面也没有可以使用的参数或者选项。所以现在这几个人主张让我自己写一个modal出来,虽然这样一切都可控,但是工程量太大了。所以我们还在争论这件事。”

“主张做modal的先散了吧。”。接下来我做到了座位上,找出了相关的一个Idea 《Allow redirect after creating a new record using force:createRecord》。

小明难以置信的瞪大了眼睛,说:“这上面明明写的是没有这个功能啊。。。”
“上了这么多年的网,还不知道评论才是精华!?”

俗话说,文档上没写的不一定就没有。

——沃·兹几硕德

评论里提到了两个属性,一个是panelOnDestroyCallback,在Summer’19之后单独使用已经无效。另一个是navigationLocation有个固定写法”navigationLocation”:”LOOKUP”。

一起服用才有效果。

var createRecordEvent = $A.get("e.force:createRecord");
createRecordEvent.setParams({
	"entityApiName": "Opportunity",
	"navigationLocation": "LOOKUP",
	"panelOnDestroyCallback": function(event) {
		// Refesh current cmp
		$A.get('e.force:refreshView').fire();
		/* OR direct to anywhere
		let urlDirectEvent = $A.get("e.force:navigateToURL");
		urlDirectEvent.setParams({
			"url": "/lightning/n/XXXX"
		});
		urlDirectEvent.fire();
		*/
	}
});
createRecordEvent .fire();

但是,由于此属性从未出现在官方文档中,所以有可能就像Summer’19失效那次一样,在某一次升级之后又不好用了,使用还需慎重。