• 按键公众号 :
按键精灵电脑版
立即下载

软件版本:2014.06
软件大小:22.9M
更新时间:2021-12-03

按键精灵安卓版
立即下载

软件版本:3.7.2
软件大小:46.2M
更新时间:2023-05-10

按键精灵iOS版
立即下载

软件版本:1.8.0
软件大小:29.2M
更新时间:2023-03-21

按键手机助手
立即下载

软件版本:3.8.0
软件大小:262M
更新时间:2023-05-30

快捷导航

登录 后使用快捷导航
没有帐号? 注册

发新话题 回复该主题

[笨蛋熊] 「架构师」流程精讲:第二节 - 常见的工作流步骤实现方法 [复制链接]

1#
一、假循环写法(替代Goto语句)
不推荐养成使用Goto语句的习惯,因为Goto语句是非结构化的,当然也可以严格写成结构化的,但是,严格的结构化Goto和直接使用Do语句几乎没有什么区别了。

Goto语句有很多缺点,比如多语言翻译障碍(很多语言已经删掉这玩意了),让代码看起来混乱,可能会导致出很多BUG等。

不要辩解说熟练的人写Goto也可以不出BUG,这东西稍微写点代码的人都不难做到,但我没必要把一个坑货属性满满的东西教给新手去用,总结下来就是:你当Goto不存在就好了。

假循环其实就是利用了 Do 语句有两个跳点的机制(Do一个跳点、Loop一个跳点),来控制代码跳来跳去,之所以叫“假循环”,是因为通常这种写法,都会自带一个 Exit Do 做结尾,整个语句不作为循环使用。
  1. // 经典假循环写法
  2. Do
  3. // 这里写你的执行代码
  4. If 1 = 1 The
  5. Exit Do
  6. End If

  7. // 无论如何,最终循环都会被跳出
  8. Exit Do
  9. Loop
复制代码
当然,上面的这个例子没有有效利用到Do的跳点,在某些情况下,Do 跳点也可以很有用,使用 Continue 语句可以跳转到 Do 的位置:
  1. // 经典假循环写法
  2. Do
  3. // 这里写你的执行代码,这里替换为 Continue 了,所以会形成死循环,实际应用时可以产生很多种变化
  4. If 1 = 1 The
  5. Continue
  6. End If

  7. // 无论如何,最终循环都会被跳出
  8. Exit Do
  9. Loop
复制代码
假循环在C语言代码中被广泛使用,例如 7z 源代码,就有许多运用循环语句实现巧妙跳转的写法。

单个假循环应用场景不多,看起来过于花哨似乎也没什么用,可一旦和下面的其他用法或者单纯的死循环配合起来,就可以实现多种变化。


二、带超时的特征等待

就是第一节课最后一个范例给的循环,循环负责实现超时退出功能,实现思路是在循环开始前计时,循环内发现对应的特征达成就会跳出,否则会在超时时间到达后跳出
  1. Dim iFlowRet = 0
  2. Dim ST = Timer()
  3. Do
  4.     If FindElement(xxx) = 1 Then
  5.         // 找到某个界面特征
  6.         Log.Info("记录日志,发现了一个特征,流程将会继续执行")
  7.         iFlowRet = 0
  8.         Exit Do
  9.     End If
  10.     If FindElement(xxx) = 1 Then
  11.         // 找到第二个界面特征
  12.         Log.Info("记录日志,这是出错的情况,需要单独处理错误")
  13.         iFlowRet = 1
  14.         Exit Do
  15.     End If
  16.     If Timer() - ST > 30 Then
  17.         Log.Info("xxx 功能执行超时(30秒),请检查环境或逻辑是否正常")
  18.         iFlowRet = -1
  19.         Exit Do
  20.     End If
  21. Loop
复制代码
这种循环最为基础,在工作流中往往承担着最基本的功能实现结构,日志也大多基于这类循环展开。

这种循环除了实现超时机制外,也可以负责功能实现,例如下面的代码,就是这类循环的完全体写法:
  1. // 伪代码,不能运行,仅展示逻辑
  2. Log.Info("流程点功能的描述,用于在日志里定位执行位置,开始执行 ...")
  3. Dim iFlowRet = 0
  4. Dim ST = Timer()
  5. Do
  6.     If FindElement(xxx) = 1 Then
  7.         // 找到某个界面特征
  8.         Log.Info("记录日志,发现了一个特征(自动点击),流程将会继续执行")
  9.         Mouse.Move(x, y)
  10.         Mouse.Click(BTN_LEFT)
  11.         iFlowRet = 0
  12.         Exit Do
  13.     End If
  14.     If FindElement(xxx) = 1 Then
  15.         // 找到第二个界面特征
  16.         Log.Info("记录日志,这是出错的情况,需要单独处理错误")
  17.         iFlowRet = 1
  18.         Exit Do
  19.     End If
  20.     If Timer() - ST > 30 Then
  21.         Log.Info("xxx 功能执行超时(30秒),请检查环境或逻辑是否正常")
  22.         iFlowRet = -1
  23.         Exit Do
  24.     End If
  25. Loop
复制代码
判断总是可以衔接一个操作,或者需要监控直到某个元素出现的情况,例如打开某个网页页面,需要关闭2个弹窗,点击一下确定,然后等待某个元素出现,则关闭弹窗的特征找到后不必跳出循环,点击确定也不必跳出循环,检测到点击确定后的界面某个元素出现,再跳出循环,则可以把一系列的操作整合在一个代码块里,并且保证结构不会过于混乱。

在这种写法的思维上稍加扩展,我们就可以实现 带优先级的特征识别了。



三、带优先级的特征识别

这类循环其实就是上一种循环的变化型。

上一种循环,我们可以检测多个特征,然后走不同的分支,跳出或者不跳出循环,由于代码执行的过程中,这一切都是不断检测执行的,没有办法确定特征出现时,代码必须执行到某个位置,因此我们可以下一个武断的结论,在这样的循环执行过程中,对于特征的检测和动作是乱序的。

但有时候,我们对操作的执行顺序非常敏感,例如第二步骤需要将鼠标悬浮在第一步骤的元素上,如果我们检测第一步骤元素是否存在,再点击第二步骤出现的元素,则无法确定会不会循环后继续执行的时候,脚本还是执行的第一步,从而破坏了已经执行过的环境变化。

这时候就需要对操作进行一个优先级分配了,比如一共3个敏感的动作,我们称为:第一步、第二步、第三步。

第三步建立在第二步的操作上,第二步又建立在第一步的操作上,这时候我们操作的优先级应该是第三步 大于 第二步 大于 第一步。

循环自带的跳点能力,让优先级实现起来非常容易,直接上一段范例代码,将上述两种类型的循环组合起来,就可以实现这样的功能了:
  1. // 伪代码,不能运行,仅展示逻辑
  2. Dim iFlowRet = 0
  3. Do
  4.     Do
  5.         If FindElement(xxx) = 1 Then
  6.             // 找到某个界面特征,最高优先级
  7.             Log.Info("记录日志,发现了一个特征(自动点击),流程将会继续执行")
  8.             Mouse.Move(x, y)
  9.             Mouse.Click(BTN_LEFT)
  10.             iFlowRet = -1
  11.             Exit Do
  12.         End If
  13.         
  14.         If FindElement(xxx) = 1 Then
  15.             // 找到第二个界面特征,第二优先级
  16.             // 操作代码
  17.             Exit Do
  18.         End If
  19.         
  20.         // 不管什么情况,都会跳出这个循环
  21.         Exit Do
  22.     Loop
  23.     
  24.     // 任务完成后,跳出外层循环
  25.     If iFlowRet = -1 Then
  26.         Exit Do
  27.     End If
  28. Loop
复制代码
这也是一种非常经典的双循环结构,优先级怎么安排呢?假设一个操作是:第一步、第二步、第三步。

那么在写代码的时候,优先级最高的是第三步、其次是第二步,最后是第一步,倒着写。

为什么这样呢?因为第三步建立在第二步的基础上,第二步建立在第一步的基础上,所以执行的顺序就变成了这样:

第一轮循环:第三步(没检测到)、第二步(没检测到)、第一步(检测到了,开始操作)
……
第N次循环:第三步(没检测到)、第二步(检测到了,开始操作)、第一步(优先级较低,代码直接跳出了,所以不会执行到这里)
……
又N次循环:第三步(检测到了,开始操作)、第一步和第二步都不会执行。

但是一旦出错了,因为优先级的缘故,代码又会从第一步开始执行,从而使这种代码具备一定的自我修复能力。



四、一个任务出错重试N次

假循环是如此重要,以至于任何复杂的循环结构,都需要依赖这种技巧

任务重试机制实现起来也很简单,一个假循环负责处理逻辑,提供尾部跳点,然后再增加一个条件判断,来确定执行状态和重试次数就可以啦

和上一种用法一样,这个用法也必须先熟练的掌握假循环的用法
  1. Dim iReCount = 5            // 重试5次
  2. Dim iErrorCount = 0
  3. Dim iFlowRet = 0
  4. Do
  5.     Do
  6.         iFlowRet = 0
  7.         
  8.         // 这里执行各种检测
  9.         If FindElement(...) = False Then
  10.             // 检测到了失败的元素,执行失败了!
  11.             iFlowRet = -1
  12.             Exit Do
  13.         End If
  14.         
  15.         // 工作流执行成功,跳出循环
  16.         iFlowRet = 0
  17.         Exit Do
  18.         
  19.     Loop
  20.     
  21.     // 重试机制实现
  22.     If iFlowRet = 0 Then
  23.         Log.Info(Format("【%s】流程执行成功!", sFlowName))
  24.         Exit Do
  25.     Else
  26.         iErrorCount = iErrorCount + 1
  27.         If iErrorCount > iReCount
  28.             Log.Error(Format("【%s】流程尝试 %d 次全部失败。", sFlowName, iReCount))
  29.             Exit Do
  30.         Else
  31.             Log.Error(Format("【%s】本次操作失败,将会在 10 秒后重新尝试。", sFlowName))
  32.             Delay(5000)
  33.         End If
  34.     End If
  35.     
  36. Loop
复制代码
五、去除非必要的重试步骤

有时候我们需要流程能够记忆执行的位置,以便于通过简单的几个数据,来快速恢复调试现场。

这有点类似游戏的存档功能。

事实上,有过游戏开发经验的人都知道,游戏的逻辑一般是写死的,游戏提供一个全局的状态机(游戏全局数据管理对象),只需要将这个状态机的数据保存起来,就可以实现存档功能了,而独挡就是把数据写回到全局状态机里。

脚本多数情况下没有游戏复杂,实现这样的功能非常简单。

我们先整理一下前面几种写法的精髓:

假循环:通过循环为操作提供跳点,可以自由控制代码是跳到循环头还是循环尾,通过再嵌套一层循环,可以实现优先级控制或者出错重试机制,如果再嵌套两层循环,则两种机制可以兼得。

特征等待:一种阻塞的平坦状态机的实现,阻塞是说代码在执行的时候,除非有明确的状态,否则会在循环里反复进行状态监测,平坦是说,多个这样的语句结构组合在一起,可以吧一个非常复杂的流程,变的平坦,如果我们封装一个函数,然后依次调用这个函数执行一个流程的第一步、第二步、第三步……则整个过程是不需要条件语句和循环语句的,因此这种写法实际上是化复杂为简单。

优先级特征识别:通过双循环结构(工作循环 + 假循环),来让流程带有执行优先级属性,特别适合处理前后相关易出错的步骤,直到最后一个步骤达成,循环退出。

重试机制:通过双循环结构(重试循环 + 假循环),来实现执行出错的情况下重试多次,来提升整体的稳定性。

在组合用法的基础上,我们可以增加 Step 变量,然后将执行的数据代入,当脚本因为异常情况退出时,可以通过这个变量重新断点执行。

我们可以发现,几乎所有的功能实现,都是写在一个假循环里面的,因为假循环用来控制流程执行跳点,它很灵活,最适合用来承载逻辑,所以 Step 变量也必定是写在假循环里的。

这个写法我就不提供例子了,需要根据项目不同,自己组织数据的形式

记住一句话:程序的最终目的,一定是操作数据,各种形态的数据,包括按键的图色脚本,实际上操作的也不过是图色和坐标等数据。

最后编辑笨蛋熊 最后编辑于 2022-04-13 22:04:17
2#

可算编辑完了…… 顶一下帖子,不讲武德,这个写了一个月 - -

3#

反正随手写代码例子, 建议写能运行的, 可以帮助理解

4#

感谢分享。

5#

积分又涨一分了

6#

我怎么又到这个页面了

7#

麻烦大神给点列子做参考也行

发新话题 回复该主题