当我们谈起代码注释,估计你有以下反应:
1.老子的代码没别人看,不需要注释;
2.这段代码很简单,不需要注释;
3.这段代码很复杂,我多写些注释;
当你发现一段代码有bug,或者看到一坨很恶心的代码像重构,然而没有一点注释,可能会出现以下心理活动:
1.写这段代码的家伙(有可能是你自己)一点注释都不写,这bug怎么修?
2.这段代码做什么?需求是什么?找找需求文档...
3.参数啥意思?我找找接口文档先...
4.需求文档呢?接口文档呢?(好旧的需求)
当你发现一段代码有bug,还有注释:
1.幸好有注释,我知道它干什么;
2.草,注释说的跟代码不符合呀!
今天咱们聊聊,没写注释和写了注释也会出现的尴尬的问题。
请求api的注释
1.如果你公司有接口文档,你应该写注释;
2.如果你公司没接口文档,你更应该写注释。
举个的例子:
public interface Api {
@POST("/challenge/createActivity")
Observable<String> createActivity(@Field("activityStartTime") int activityStartTime, @Field("activityEndTime") int activityEndTime,
@Field("challengePoint") int challengePoint,
@Field("checkInNumRule") int checkInNumRule,
@Field("description") String description,
@Field("distanceRule") int distanceRule,
@Field("speedRule") int speedRule,
@Field("sumDistanceRule") int sumDistanceRule,
@Field("taskType") int taskType);
}
你知道createActivity
方法是做什么的吗?直译的话,可以认为这是“创建一个活动”的接口。你知道每个参数含义吗?distanceRule
checkInNumRule
什么意思?反正没注释笔者是不知道。
这时,有工程师会:
安卓工程师:“找接口文档。”
笔者:“请问接口文档在哪?”
安卓工程师:“公司一般有接口文档管理网站,搜一下就好。”
“搜接口文档”看起来很简单的事,实际上,有那么简单吗?对于新项目,接口文档很好找,如果是老项目,接口文档管理网站可能已经换了,你还得问后端同事,旧网站网址。而且不是每个后端工程师都知道网址,可能得问老员工......
还有,如果每次阅读这段代码,都要查查接口文档,耗费工程师多少时间成本?
以下是笔者认为比较好的注释:
public interface Api {
/**
* 创建个人发起的挑战活动 http://doc.thejoyrun.com/api/challenge/createActivityForPersonal
*
* @param activityStartTime 挑战开始时间
* @param activityEndTime 挑战结束时间
* @param challengePoint 挑战积分
* @param checkInNumRule 打卡次数, 当taskType=2时,必填
* @param description 规则说明
* @param distanceRule 单次打卡距离, 当taskType=2时,必填
* @param speedRule 配速, 当taskType=1时,必填,整型,如配速 6分30秒,应传390
* @param sumDistanceRule 累计距离, 当taskType=1时,必填
* @param taskType 挑战类型, 1是累计距离挑战;2是打卡挑战;必填
*
* @return 创建成功返回msg
*/
@POST("/challenge/createActivity")
Observable<String> createActivity(@Field("activityStartTime") int activityStartTime, @Field("activityEndTime") int activityEndTime,
@Field("challengePoint") int challengePoint,
@Field("checkInNumRule") int checkInNumRule,
@Field("description") String description,
@Field("distanceRule") int distanceRule,
@Field("speedRule") int speedRule,
@Field("sumDistanceRule") int sumDistanceRule,
@Field("taskType") int taskType);
}
这样是不是清晰多了?方法功能、参数意义、接口文档网址都有了。工程师还能用Quick Documentation快速查看代码注释,节省工程师多少时间。
以下给出接口文档 (假的url):http://doc.thejoyrun.com/api/challenge/createActivityForPersonal
接口名称 创建个人发起的挑战活动-(预设)
请求类型 post
请求Url /challenge/createActivity
变量名 | 含义 | 类型 | 备注 |
---|---|---|---|
activityEndTime | 挑战结束时间 | number | 必填 |
activityStartTime | 挑战开始时间 | number | 必填 |
challengePoint | 挑战积分 | number | 必填 |
checkInNumRule | 打卡次数 | number | 当taskType=2时,必填 |
description | 规则说明 | string | 必填 |
distanceRule | 单次打卡距离 | number | 当taskType=2时,必填 |
speedRule | 配速 | number | 当taskType=1时,必填,整型,如配速 6分30秒,应传390 |
sumDistanceRule | 累计距离 | number | 当taskType=1时,必填 |
taskType | 挑战类型 | number | 1是累计距离挑战;2是打卡挑战;必填 |
自动生成代码/注释
一份标准的文档,是有固定格式,笔者公司用RAP文档管理。笔者写了个用java代码根据文档生产代码+注释的小工具,繁琐、重复的工作,交给脚本生成就好。
Java Bean注释
1.当成员变量英文名或意义比较复杂,必写注释;
2.当成员变量数值有范围,或多个值且有特定意义,必写注释。
请看请求接口和返回列表
public interface Api {
/**
* 我的挑战列表 http://doc.thejoyrun.com/api/challenge/myChallenges
*/
@GET("/challenge/myChallenges")
Observable<List<Challenge>> getChallengeList();
}
/**
* 挑战
*/
public class Challenge {
int challengeId;
String title;
String imageCover;
int activityEndTime;
int activityStartTime;
int activityStatus;
int joinTime;
int timeLeft;
int userJoinStatus;
}
Challenge
是挑战列表的元素。直译,猜到activityStatus
、userJoinStatus
是两种状态,但这两种状态分别有什么数值呢?什么数值代表什么意思?
当你看到如下代码:
Challenge challenge = new Challenge();
switch (challenge.getActivityStatus()){
case 0:
// do something A
break;
case 1:
// do something B
break;
......
}
假如,测试工程师跟你说 活动进行中
有bug,而你又不知道activityStatus
哪种情况会执行A
或B
,那你就必须debug,或者查接口文档activityStatus
哪个数值是 活动进行中
。
还有,timeLeft
直译“剩下的时间” ,是到哪个时间节点剩下的时间?
这些工作都会大量耗费工程师时间。如果我们把注释写好一点:
public class Challenge {
/** 挑战活动ID */
int challengeId;
/** 标题 */
String title;
/** 封面地址 */
String imageCover;
/** 挑战结束时间 */
int activityEndTime;
/** 挑战开始时间 */
int activityStartTime;
/**
* 挑战活动状态 0-未开始 1-进行中 2-已结束
*/
int activityStatus;
/** 用户报名挑战时间 */
int joinTime;
/** 挑战未开始的时候为距离开始的剩余时间;当挑战进行中的时候表示还剩余多少时间结束 */
int timeLeft;
/**
* 参与状态 -1失败 0-未参加 1-挑战中 2-挑战成功
*/
int userJoinStatus;
}
这样就清晰多了,直接Quick Documentation就可以查看activityStatus
注释。你可以明确activityStatus==1
时,才出现bug,直接定位到case 1
出bug代码块。
还有,timeLeft
在不同状态下,意义不同。1.挑战未开始
,为距离开始的剩余时间;2.挑战进行中
,表示还剩余多少时间结束。如果不写注释,谁知道这些啊?
Activity注释
如果给你一个Activity
,完全没有注释:
@RouterActivity("page_x")
public class ActivityX extends AppCompatActivity {
......
}
Activity
注解@RouterActivity
表示外部可以通过page_x
路由跳进来。
请问,这个Activity是从哪个界面跳进来的?
好运的话,Find Usages
会找到startActivity(new Intent(context, ActivityX.class))
,或者找到路由跳转page_x
的位置。
给你个场景:ActivityX
从A界面
条进来,而A界面
必须满足一定条件,才出现跳转page_x
按钮。例如,A界面
请求服务器,返回某个字段status=1
满足条件。
再复杂一点,跳转路径:
C -> B -> A -> X
继续复杂点,page_x
是后端返回的跳转路径,app不写死。在代码层面根本找不到入口。
你能一眼看出ActivityX
是干什么的吗?
对于复杂的跳转路径,及满足条件才能跳转,我们非常难知道ActivityX
的功能是什么。
如果我们在头部写上备注:
/**
* 参与过的活动详情。<br>
* 入口:我的活动,右上角“历史” -> 参与过的活动列表,点击item进入
*/
@RouterActivity("page_x")
public class ActivityX extends AppCompatActivity {
......
}
这样是不是清晰明了?
结语
本篇笔者就重点说了请求接口注释、java bean注释。读者应该举一反三:
1.java的方法要写注释,表明用途;
2.方法参数写注释,表明意义;
3.成员变量写注释,表明意义,数值范围,每个值的意义等.
每次笔者要修bug,当代码不是笔者写,而且又没有任何备注,恐怕会立即脱口而出“卧槽”!
写好注释不能让你从中级变高级工程师,但其他工程师阅读你的代码,知道你是一个做事谨慎细心的人,也让阅读的人赏心悦目。
不写注释有个笑话:
我写这段代码时,知道它在做什么的,只有老天和我;
但是现在,只有老天知道这段代码在做什么。
网友评论