Effective Dart 文档注释在Flutter项目中的实践
前言
什么是注释?
在编程语言中,注释就是对代码的解释和说明,其目的是让人们能够更加轻松地理解代码。
也有一句话是这样说的:程序员都讨厌两件事,1.别人不写注释 2.自己写注释
在开发者社区里,我不止一次的看到吐槽离任的前同事不写注释的例子,其实不光是他人的代码,即便是自己写的代码,一段时间以后再去看,你也会发现:这写的什么呀?
作为开发者,我们大多都知道编写注释的重要性,但是却往往抱着”能实现功能即可以了“这样的心态去写代码,完全随心所欲的去实现,结果当然就是留下一堆烂摊子,形成前文所述的恶性循环,所以在编写代码的同时,请牢记代码首先是写给人看的。
编写精炼的、精确的注释 只要要几秒钟,但是以后可能节省其余人几个小时 的时间来读懂您的代码。
写好注释
写注释简单吗?简单。那么写好注释,简单吗?
答案就跟写文章一样,有的文章是记流水账,有的则是发人深省,回味无穷。
注释也是同样,在《代码精进之路》里总结了编写注释的三项准则:
- 精确,错误的注释比没有注释更糟糕。
- 必要,多余的注释白费阅读者的时间。
- 清晰,混乱的注释会把代码搞得更乱。
比方,当我们说编程语言时,肯定不要省略“编程”这两个字。否则,即可能被误会为大家日常说话用的自然语言。这就是精确性的要求。
bad:
String language = "Java"; // the languagebetter:
String language = "Java"; // the programming language假如代码已经能够清晰、简单地表达自己的语义和逻辑,这时候重复代码语义的注释就是多余的注释。注释的维护是耗费时间和精力的,所以,不要保留多余的、不必要的注释。还有一句很精辟的话:Code tells you How, Comments tell you Why.
bad:
// the programming languageString programmingLanguage = "Java";better:
String programmingLanguage = "Java";假如注释和代码不能从视觉上清晰地分割,注释就会破坏代码的可读性。
bad:
/* dump debug informationif (hasDebug) { System.out.println("Programming language: Jave");} */String programmingLanguage = "Java";better:
// dump debug information//// if (hasDebug) {// System.out.println("Programming language: Jave");// }String programmingLanguage = "Java";《Effective Dart》中关于如何写注释,也推荐要清晰和精确,同时还有简洁。
在运用Dart语言编写Flutter应用的过程中,因为视图层都是纯Dart代码,而且夹杂着许多的if..else..,需要根据条件显示不同的widget,因而在做好widget拆分的同时,编写良好的注释势在必行。
Effective Dart 文档注释
在学习并使用Flutter框架开发App的过程中,有些开发者并没有怎样关注Dart语言,草草看了几下语法之后就开始了Flutter之旅,还是按照以前的Java、Swift这样的语法风格进行开发,这在以后可能会带来不必要的麻烦,比方团队成员里各个都不同的命名方式、注释也各不相同等等。
因而推荐先看一下 《Effective Dart》,主要内容包含代码风格、文档注释、最佳实践、设计指南,能从中获益良多。我们主要关心文档注释这一章节,这里我总结了以下两点:
1. 使用///放弃/** ... */
在《Effective Dart》解释了相应起因
因为历史起因,dartdoc 支持两种格式的文档注释:
///(“C# 格式”) 和/** ... */(“JavaDoc 格式”)。我们推荐使用///是由于其更加简洁。/**和*/在多行注释中间增加了开头和结尾的两行多余 内容。///在少量情况下也更加易于阅读,例如 当注释文档中包含有使用*标记的列表内容的时候。假如你现在还在使用 JavaDoc 风格格式,请考虑 使用新的格式。
与此同时,要使用 /// 文档注释来注释成员和类型。
bad:
// The number of characters in this chunk when unsplit.int get length => ...better:
/// The number of characters in this chunk when unsplit.int get length => ..而//则主要用于方法体内的注释
greet(name) { // Assume we have a valid name. print('Hi, $name!');}2.把第一个语句定义为一个段落并使用散文的方式来形容
注释文档中的第一个段落应该是简洁的、面向客户的注释。例如下面的示例, 通常不是一个完成的语句。
bad:
/// Starts a new block as a child of the current chunk. Nested blocks are/// handled using their own independent [LineWriter].ChunkBuilder startBlock() { ... }better:
/// Defines a flag.////// Throws an [ArgumentError] if there is already an option named [name] or/// there is already an option using abbreviation [abbr]. Returns the new flag.Flag addFlag(String name, String abbr) { ... }这就跟日常写博客一样,会先写一个段落大意,而后围绕着这点进行详细形容,这样更容易让读者了解核心思想,也更加节省时间。
另外推荐使用散文的方式来形容参数、返回值以及异常信息。
在其余语言中,比方JavaDoc使用各种标签和额外的注释来形容参数和 返回值。
bad:
/// Defines a flag with the given name and abbreviation.////// @param name The name of the flag./// @param abbr The abbreviation for the flag./// @returns The new flag./// @throws ArgumentError If there is already an option with/// the given name or abbreviation.Flag addFlag(String name, String abbr) { ... }而 Dart 把参数、返回值等形容放到文档注释中,并使用方括号来引用 以及高亮这些参数和返回值。
better:
/// Defines a flag.////// Throws an [ArgumentError] if there is already an option named [name] or/// there is already an option using abbreviation [abbr]. Returns the new flag.Flag addFlag(String name, String abbr) { ... }我们主要注意这两点,其它规范可以查看《文档注释》。
在mvvm_flutter项目中的实践
mvvm_flutter是我在Flutter中运用MVVM架构的一个示例。
mvvm_flutter : ditclear/mvvm_flutter
项目完成后只简单的在几个关键方法上增加了几个JavaDoc格式的注释,比方:
/** * call the model layer 's method to login * doOnData : handle response when success * doOnError : handle error when failure * doOnListen : show loading when listen start * doOnDone : hide loading when complete */ Observable login() => _repo .login(username, password) .doOnData((r) => response = r.toString()) .doOnError((e, stacktrace) { if (e is DioError) { response = e.response.data.toString(); } }) .doOnListen(() => loading = true) .doOnDone(() => loading = false);现在我们开始将其转换为Dart语言推荐的注释风格。如下所示:
/// 登录 /// /// 调用 model层[GithubRepo] 的 login 方法进行登录 /// 传入 [username] 和 [password] /// 成功:显示返回的信息 /// 失败:解决错误,显示错误信息 /// 订阅开始:loading = true /// 订阅结束:loading = false /// 返回 [Observable] 给 View 层 Observable login() => _repo .login(username, password) .doOnData((r) => response = r.toString()) .doOnError((e, stacktrace) { if (e is DioError) { response = e.response.data.toString(); } }) .doOnListen(() => loading = true) .doOnDone(() => loading = false);相比之前的代码,清晰干净了蛮多,我们在注释的第一行中形容了这个方法的功能,随后以散文的方式对方法的调用、参数以及返回值进行了形容。
这是ViewModel层的注释,接下来我们来进行Model层的注释。
class GithubRepo { /// ... Observable login(String username, String password) { _sp.putString(KEY_TOKEN, "basic " + base64Encode(utf8.encode('$username:$password'))); return _remote.login(); }}我们对login方法进行注释,结果如下:
/// 仓库层class GithubRepo { /// 登录 /// /// 将ViewModel层 传递过来的[username] 和 [password] 解决为 token 并用[_sp]进行缓存 /// 调用 [_remote] 的 [login] 方法进行网络访问 /// 返回 [Observable] 给ViewModel层 Observable login(String username, String password) { _sp.putString(KEY_TOKEN, "basic " + base64Encode(utf8.encode('$username:$password'))); return _remote.login(); }}这里方法比较简单,但对于复杂的仓库层的方法,可能包含着网络、数据库、MethodChannel、SharedPreferences等等交互,运用Dart推荐的注释方式,可以更好的形容你的代码逻辑。
/// 获取文章介绍 /// /// 1.先通过 数据库[_local] 查看本地能否有 id 为 [articleId]的文章 /// 2.有缓存则到第4步,没有缓存则到第3步 /// 3.通过网络层[_remote] 获取服务端数据,成功后再进行缓存 ,到第4步 /// 4.返回 [Observable] 给ViewModel层 Observable getArticleDetail(int articleId) { return _local .getArticleById(articleId) .onErrorResumeNext( _remote.getArticleById(articleId) .doOnData((article) => _local.insertArticle(article))); }这样,我们轻松的拆解了代码逻辑,也能更容易的编写出相应的测试用例,方便进行单元测试。
而View层相比Model层和ViewModel层,需要额外注意的是其夹杂着逻辑判断,需要根据条件显示不同的Widget,我们在将复杂部分提取成方法的时候,也需要对其进行详细的形容。
/// 登录按钮内部的widget////// 当请求进行时 [value.loading] 为 true 时,显示 [CircularProgressIndicator]/// 否则显示普通的登录文本Widget buildLoginChild(HomeProvide value) { if (value.loading) { return const CircularProgressIndicator(); } else { return const FittedBox( fit: BoxFit.scaleDown, child: const Text( 'Login With Github Account', maxLines: 1, textAlign: TextAlign.center, overflow: TextOverflow.fade, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16.0, color: Colors.white), ), ); }}经过这番改进,当其余开发者阅读您的代码时,也能减少很多不必要的烦恼。
写在最后
在运用《Effective Dart》中的注释技巧进行文档注释时,会有一种在写博客的感觉,由于写博客的好处之一便是备忘,将来再复习的时候能够更加快速的了解知识点,注释也是这样。
我认为即便是再优秀的开发者,假如不写注释,时间长了,也会不记得,而且有幸见识过一个Java文件包含了30000+行代码,还没有注释,到现在都没有人愿意去接手这样的项目,大家都是程序员,程序员何苦为难程序员呢。
参考资料:
Code Complete-自说明代码
代码精进之路-写好注释,真的是小菜一碟?
Effective Dart – 文档注释
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » Effective Dart 文档注释在Flutter项目中的实践