共计 6221 个字符,预计需要花费 16 分钟才能阅读完成。
对于命名长度,在可能表白含意的额状况下,命名当然是越短越好。在大多数的状况下,短的命名不如长的命名更能表白含意,很多书籍是不举荐应用缩写的。
只管长的命名能够蕴含更多的信息,更能精确直观地表白用意,然而,如果函数、变量的命名很长,那由它们组成的语句就会很长。在代码列长度有限度的状况下,就会经常出现一条语句被宰割成两行的状况,这其实会影响代码可读性。
所以有时候咱们是能够适量的应用缩写的短命名
在什么场景下适合应用短命名
1、对于一些默认,大家都熟知的倒是能够应用缩写的命名,比方,sec 示意 second、str 示意 string、num 示意 number、doc 示意 document 等等
2、对于作用域比拟小的变量,咱们能够应用绝对短的命名,比方一些函数内的长期变量,绝对应的对于作用于比拟大的,更举荐应用长命名
2、利用上下文简化命名
来看个栗子
type User struct {
UserName string
UserAge string
UserAvatarUrl string
}
复制代码
比方这个 struct,咱们曾经晓得这是一个 User 信息的 struct。外面用户的 name ,age,就没有必要加上 user 的前缀了
修稿后的
type User struct {
Name string
Age string
AvatarUrl string
}
复制代码
当然这个在数据库的设计中也是同样有用
3、命名要可读、可搜寻
“可读”,指的是不要用一些特地生僻、难发音的英文单词来命名。
咱们在 IDE 中编写代码的时候,常常会用“关键词联想”的办法来主动补全和搜寻。比方,键入某个对象“.get”,心愿 IDE 返回这个对象的所有 get 结尾的办法。再比方,通过在 IDE 搜寻框中输出“Array”,搜寻 JDK 中数组相干的函数和办法。所以,咱们在命名的时候,最好能合乎整个我的项目的命名习惯。大家都用“selectXXX”示意查问,你就不要用“queryXXX”;大家都用“insertXXX”示意插入一条数据,你就要不必“addXXX”,对立规约是很重要的,能缩小很多不必要的麻烦。
4、如何命名接口
对于接口的命名,个别有两种比拟常见的形式。一种是加前缀“I”,示意一个 Interface。比方 IUserService,对应的实现命名为 UserService。另一种是不加前缀,比方 UserService,对应的实现加后缀“Impl”,比方 UserServiceImpl。
正文
咱们承受一个我的项目的时候,常常会吐槽老我的项目正文不好,文档不全,那么如果正文都让咱们去写,怎么的正文才是好的正文
有时候咱们会在书籍或一些博客中看到,如果好的命名是不须要正文的,也就是代码即正文,如果须要正文了,就是代码的命名不好了,须要在命名中下功夫。
这种是有点极其了,命名再好,毕竟有长度限度,不可能足够详尽,而这个时候,正文就是一个很好的补充。
1、正文到底该写什么
咱们写数正文的目标是让代码更易懂,正文个别包含三个方面,做什么、为什么、怎么做。
这是 golang 中 sync.map 中的正文,也是别离从做什么、为什么、怎么做 来进行正文
// Map is like a Go map[interface{}]interface{} but is safe for concurrent use
// by multiple goroutines without additional locking or coordination.
// Loads, stores, and deletes run in amortized constant time.
//
// The Map type is specialized. Most code should use a plain Go map instead,
// with separate locking or coordination, for better type safety and to make it
// easier to maintain other invariants along with the map content.
//
// The Map type is optimized for two common use cases: (1) when the entry for a given
// key is only ever written once but read many times, as in caches that only grow,
// or (2) when multiple goroutines read, write, and overwrite entries for disjoint
// sets of keys. In these two cases, use of a Map may significantly reduce lock
// contention compared to a Go map paired with a separate Mutex or RWMutex.
//
// The zero Map is empty and ready for use. A Map must not be copied after first use.
type Map struct {
mu Mutex
read atomic.Value // readOnly
dirty map[interface{}]*entry
misses int
}
复制代码
有些人认为,正文是要提供一些代码没有的额定信息,所以不要写“做什么、怎么做”,这两方面在代码中都能够体现进去,只须要写分明“为什么”,表明代码的设计用意即可。
不过写了正文可能有以下几个长处
1、正文比代码承载的信息更多
函数和变量如果命名得好,的确能够不必再在正文中解释它是做什么的。然而,对构造体来说,蕴含的信息比拟多,一个简略的命名就不够全面详尽了。这个时候,在正文中写明“做什么”就荒诞不经了。
2、正文起到总结性作用、文档的作用
在正文中,对于具体的代码实现思路,咱们能够写一些总结性的阐明、非凡状况的阐明。这样可能让浏览代码的人通过正文就能大略理解代码的实现思路,浏览起来就会更加容易。
3、一些总结性正文能让代码构造更清晰
对于逻辑比较复杂的代码或者比拟长的函数,如果不好提炼、不好拆分成小的函数调用,那咱们能够借助总结性的正文来让代码构造更清晰、更有条理。
2、正文是不是越多越好
正文自身有肯定的保护老本,所以并非越多越好。构造体和函数肯定要写正文,而且要写得尽可能全面、具体,而函数外部的正文要绝对少一些,个别都是靠好的命名、提炼函数、解释性变量、总结性正文来进步代码可读性。
代码格调
1、函数多大才适合
函数的代码太多和太少,都是不太好的
太多了:
一个办法上千行,一个函数几百行,逻辑过于繁冗,浏览代码的时候,很容易就会看了前面忘了后面
太少了:
在代码总量雷同的状况下,被宰割成的函数就会相应增多,调用关系就会变得更简单,浏览某个代码逻辑的时候,须要频繁地在 n 多办法或者 n 多函数之间跳来跳去,浏览体验也不好。
多少最合适的呢?
不过很难给出具体的值,有的中央会讲,那就是不要超过一个显示屏的垂直高度。比方,在我的电脑上,如果要让一个函数的代码残缺地显示在 IDE 中,那最大代码行数不能超过 50。
2、一行代码多长最合适
这个也没有一个齐全的准侧,毕竟语言不同要求也是不同的
当然有个通用的准则:一行代码最长不能超过 IDE 显示的宽度。
太长了就不不便代码的浏览了
3、善用空行宰割单元块
也就是垂直留白,不太倡议咱们的代码写下来,一个函数或办法中一行空格也没余,通常会依据不同的语义,一个小模块的内容完了,通过空白空格进行宰割。
// Store sets the value for a key.
func (m *Map) Store(key, value interface{}) {
read, _ := m.read.Load().(readOnly)
if e, ok := read.m[key]; ok && e.tryStore(&value) {return}
m.mu.Lock()
// ...
m.mu.Unlock()
}
复制代码
这里上锁的代码就和上文进行了空格
当然有的中央会讲首行不空格,这也是对的,函数头部的空行是没有任何用的。
编程技巧
https://zhuanlan.zhihu.com/p/…
https://zhuanlan.zhihu.com/p/…
https://zhuanlan.zhihu.com/p/…
https://zhuanlan.zhihu.com/p/…
https://zhuanlan.zhihu.com/p/…
https://zhuanlan.zhihu.com/p/…
https://zhuanlan.zhihu.com/p/…
https://zhuanlan.zhihu.com/p/…
https://zhuanlan.zhihu.com/p/…
https://zhuanlan.zhihu.com/p/…
1、把代码宰割成更小的单元块
长于将代码中的模块进行形象,可能不便咱们的浏览
所以,咱们要有模块化和抽象思维,长于将大块的简单逻辑提炼成小的办法或函数,屏蔽掉细节,让浏览代码的人不至于迷失在细节中,这样能极大地提高代码的可读性。不过,只有代码逻辑比较复杂的时候,咱们其实才倡议把对应的逻辑提炼进去。
2、防止函数或办法参数过多
函数蕴含 3、4 个参数的时候还是能承受的,大于等于 5 个的时候,咱们就感觉参数有点过多了,会影响到代码的可读性,应用起来也不不便。
针对这种状况有两种解决办法
1、思考函数是否职责繁多,是否能通过拆分成多个函数的形式来缩小参数。
2、将函数的参数封装成对象。
栗子
func updateBookshelf(userId, deviceId string, platform, channel, step int) {
// ...
}
// 批改后
type UpdateBookshelfInput struct {
UserId string
DeviceId string
Step int
Platform int
Channel int
}
func updateBookshelf(input *UpdateBookshelfInput) {
// ...
}
复制代码
3、勿用函数参数来管制逻辑
不要在函数中应用布尔类型的标识参数来管制外部逻辑,true 的时候走这块逻辑,false 的时候走另一块逻辑。这显著违反了繁多职责准则和接口隔离准则。
能够拆分成两个函数别离调用
栗子
func sendVip(userId string, isNewUser bool) {
// 是新用户
if isNewUser {// ...} else {// ...}
}
// 批改后
func sendVip(userId string) {
// ...
}
func sendNewUserVip(userId string) {
// ...
}
复制代码
不过,如果函数是 private 公有函数,影响范畴无限,或者拆分之后的两个函数常常同时被调用,咱们能够酌情思考不必拆分。
4、函数设计要职责繁多
对于函数的设计咱们也要尽量职责繁多,防止设计一个大而全的函数,能够依据不同的性能点,对函数进行拆分。
举个栗子:咱们来校验下咱们的额一些用户属性,当然这个校验就省略成判断是否为空了
func validate(name, phone, email string) error {
if name == "" {return errors.New("name is empty")
}
if phone == "" {return errors.New("phone is empty")
}
if email == "" {return errors.New("name is empty")
}
return nil
}
复制代码
批改过就是
func validateName(name string) error {
if name == "" {return errors.New("name is empty")
}
return nil
}
func validatePhone(phone string) error {
if phone == "" {return errors.New("phone is empty")
}
return nil
}
func validateEmail(name, phone, email string) error {
if email == "" {return errors.New("name is empty")
}
return nil
}
复制代码
5、移除过深的嵌套档次
代码嵌套档次过深往往是因为 if-else、switch-case、for 循环适度嵌套导致的。过深的嵌套,代码除了不好了解外,嵌套过深很容易因为代码屡次缩进,导致嵌套外部的语句超过一行的长度而折成两行,影响代码的整洁。
对于嵌套代码的批改,大略有四个方向能够思考
举个栗子:
这段代码中,有些中央是不太适合的,咱们从上面的四个方向来剖析
func sum(sil []*User, age int) int {
count := 0
if len(sil) == 0 || age == 0 {return count} else {
for _, item := range sil {
if item.Age > age {count++}
}
}
return count
}
复制代码
1、去掉多余的 if 或 else 语句
批改为
func sum(sil []*User, age int) int {
count := 0
if len(sil) != 0 && age == 0 {
for _, item := range sil {
if item.Age > age {count++}
}
}
return count
}
复制代码
2、应用编程语言提供的 continue、break、return 关键字,提前退出嵌套
func sum(sil []*User, age int) int {
count := 0
if len(sil) != 0 && age == 0 {
for _, item := range sil {
if item.Age <= age {continue}
count++
}
}
return count
}
复制代码
3、调整执行程序来缩小嵌套
func sum(sil []*User, age int) int {
count := 0
if len(sil) == 0 || age == 0 {return count}
for _, item := range sil {
if item.Age <= age {continue}
count++
}
return count
}
复制代码
4、将局部嵌套逻辑封装成函数调用,以此来缩小嵌套
6、学会应用解释性变量
罕用的用解释性变量来进步代码的可读性的状况有上面 2 种
1、常量取代魔法数字
func CalculateCircularArea(radius float64) float64 {
return 3.1415 * radius * radius
}
// 批改后
const PI = 3.1415
func CalculateCircularArea(radius float64) float64 {
return PI * radius * radius
}
复制代码
2、应用解释性变量来解释简单表达式
if appOnlineTime.Before(userId.Timestamp()) {
appOnlineTime = userId.Timestamp()
}
// 批改后
isBeforeRegisterTime := appOnlineTime.Before(userId.Timestamp())
if isBeforeRegisterTime {
appOnlineTime = userId.Timestamp()
}