- 积分
- 2
- 阅读权限
- 100
- 鲜花
- 0
- 猫粮
- 66
- 在线时间
- 29 小时
- 精华
- 0
- 注册时间
- 2018-2-27
- 性别
- 保密
- 最后登录
- 2022-6-23
按键电脑&手机班学员
- 鲜花
- 0
- 猫粮
- 66
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
本帖最后由 tiybu 于 2018-4-29 11:04 编辑
好的代码和糟糕的代码非常明显,好的代码不需要过多思考就一下子能够看明白,而不是认为自己能看懂,大家都能看懂。好的代码是经过很多客观因素考虑后写出来的,而不是按自己的个人偏好去写的(但习惯了写好代码,就是自己的个人偏好);好的代码即使过了很长时间再去翻阅,也会一眼看懂。一眼就看得懂的代码不需要注释,代码才是最好的注释。
题主在写这篇帖子之前,考虑了 刚学函数却对此模糊不清 和 早就懂得使用函数却想学习些技巧 的朋友。
函数就像一个榨汁机,把一条萝卜放进去,按一下启动榨汁的按钮,就可以出来一杯新鲜的萝卜汁。如果放苹果,最后出来的就是苹果汁。组装榨汁机的人,不必考虑各组件是怎么实现的;使用榨汁机的人,不必考虑其机是怎么组装的。
函数的参数和返回值,都可以用外部(或全局)变量来代替。什么情况下给函数定义参数更合适? 什么情况下给函数返回值更合适?
参数的意义
现假设有一个自动登录的脚本。如果要我们去写,就可以把登录的功能划分出来,作为一个函数。先来考虑这种写法(为了方便演示,下面的一些注释假设为实现代码):
Dim userId,pwd
//.....
//设置登录账号
userId = "123456"
//设置登录密码
pwd = "qwe520"
//登录游戏
Login()
//....
Function Login()
//.......
End Function
上面的写法并不直观,原因是每次调用Login函数之前都要设置userId和pwd变量,假如忘记了userId变量名的拼写怎么办呢? 那么又要去花时间找出来了,不仅如此,脚本写完后放在一边,时间长了再去翻阅原先写的这些代码,可能就忘记了这个变量名的作用,此时又要花时间去理解这个变量名。你们一定会想到,可以用参数指定账号和密码。登录论坛,登录企鹅,亦或登录其它的一些App、网站等等,都是先输入账号,后输入密码,这个操作顺序永远被我们记得。 所以,上述代码中的函数定义可以改写成:
Function Login(userId,userPwd)
//.......
End Function
这样不仅一目了然,减少了记忆了负担,还大大降低了出错的风险。
参数定义原则
如果一个外部(或全局)变量作为某种状态以可改变两个以上的函数的执行,并且这种改变并不频繁,如果有这样的需求,就应该保持它为一个外部(或全局)变量。看下面的代码就会明白(为了方便演示,下面的注释假设为实现代码)。
Function IndexString(inputStr,findStr, ignoreCase)
Dim retVal
//......
IndexString = retVal
End Function
Function ReplaceString(inputStr,oldStr,newStr, ignoreCase)
Dim retVal
//......
IndexString = retVal
End Function
IndexString函数作用是得到字符串索引,类似VBS函数InStr;
ReplaceString函数作用是替换字符串,类似VBS函数Replace。
这两个函数的设计并不是最好的方案,原因是它们都有一个共同的参数ignoreCase,它指明是否需要区分大小写。字符串相关的操作函数,一般情况下很少需要指明是否区分大小写,如果在每次调用,都指明它,就显得比较麻烦。最好的办法是,把ignoreCase作为一个外部变量。上述代码更换成下叙代码为最优解:
dim ignoreCase
ignoreCase = true //设置默认状态
Function IndexString(inputStr,findStr)
Dim retVal
//......
IndexString = retVal
End Function
Function ReplaceString(inputStr,oldStr,newStr)
Dim retVal
//......
IndexString = retVal
End Function
这样,如有需要,每次调用它们之前,就可以先设置ignoreCase。像VBS中的InStr函数就有可选参数,但按键精灵中的函数定义并不支持这样做。上述代码不仅做到了“可选参数”的功能,在某些情况下,也非常有用,比如像下面的情况:
dm.SetPath("c:\")
用过大漠插件的人都知道这个函数的作用。假设大漠的图色函数支持传入一个全局路径的字符串并支持可选,就不合适了——引起混乱。 这个SetPath函数的设置就相当于上述代码中的ignoreCase的设置。
返回值的意义
大部分情况下,把外部变量代替函数的返回值并不合适。一目了然、减少记忆负担、降低出错风险,给函数添加返回值更是增强了这三点。一个函数就像一个任务待命执行者,需要的时候可以命令它,任务完成了还可以给我们反馈完成的情况。厨师的职责是单一的,本职工作就是炒菜烹饪,不能干相关以外的事情。厨师做完菜会给顾客上菜吗? 函数的职责也应该是单一的,这是编写函数功能的一个原则。拿上面的Login函数作为例子,请先看下面的代码(为了方便演示,下面的注释假设为实现代码):
Login("123456","qwe123")
Function Login(userId,userName)
//.....
//检测是否登录成功
If (isLogined) Then
//选择角色....
//......
Else
//.....
End If
//....
End Function
其实这样的写法并不明智,反而混乱了。函数的职责应该是单一的,只做一件事情,这里不仅给你做了检测是否登录成功,成功了还帮你选择角色。脚本写完后放在一边,时间长了再去翻阅原先写的这些代码,通过看函数的名字Login,知道了是登录的意思,但也不确定登录完了还干些什么事情,登录失败了又干些什么事情。
我们需要的是,通过一个名字,就能断定它主要是干什么的,而不用过多的怀疑。
所以必须树立一个原则——单一职责。
为了减少以后看代码的痛苦,树立这个原则是很明智的。
把上述代码更换成下述代码为最优解:
Dim flag
flag = Login("123456","qwe123")
If (flag) Then
//选择角色
Else
//........
End If
Function Login(userId,userName)
//.....
//检测是否登录成功
If (isLogined) Then
Login = true
Else
Login = false
End If
//....
End Function
这样就更一目了然了,做了该做的事情。做完了还给我反馈信息——返回值。
函数的设计原则
1.单一职责原则
一个功能可能有很多个子功能,一个子功能可能又有很多个子子功能。应该把哪些功能写成函数是要斟酌的。但不可否定的是,函数始终保持单一的功能。一个函数通常来说只有20~30行代码,也会有意外。假如写的代码有足足50行以上,就要考虑是否要进行分解了。
2.金字塔原则
函数分为具体函数和抽象函数。具体依赖于抽象,抽象不能依赖于具体。它们是层级关系。大漠插件中有LeftClick、FindPic函数,它们是抽象的,Login函数可以依赖于它们。LeftClick依赖了Windows系统底层的API函数,那么API函数比LeftClick函数要抽象。
假设现在有一个需求:自动登录游戏过任务。
那么,我们划分成的函数可以是这样的:
Main()
//主程序
Function Pro()
OpenGame("C:\XX游戏.exe")
Login("123456","qwe123")
PassTask()
End Function
//打开游戏
Function OpenGame(path)
//.....
End Function
//登录游戏
Function Login(userId,userPwd)
//...
End Function
//通过任务
Function PassTask()
//....
End Function
他们的层级关系是这样的(图):
虽然上面的图包含了大漠函数、API函数的层级关系,但平时我们自己写的函数以这种方式去关联是明智之举。
|
|