软件设计模式是针对常见问题的可重用解决方案。它不是一个完成的解决方案,而是一个如何在各种不同的环境中解决问题的模板。使用设计模式可以节省大量时间,有助于更好地理解、分析和组织代码,从而获得最佳解决方案。
在这篇文章中,我们建议使用设计模式的语言来描述EV3软件中一些最常见的编程结构,并概述以下设计模式:
•简单的设计模式(顺序编程)
•通用设计模式(循环编程)
•并行循环设计模式
•主从设计模式
•状态机设计模式
简单的设计模式
我们给EV3机器人编程的一个最简单的方法就是把不同的动作块按我们想要的顺序或顺序放到序列梁上。
例如,要让一个标准的两轮机器人进行一系列的动作,我们可以把所需要的动作块从左到右拉出来,按照我们的意愿来排列。每个块一次执行一个,一直执行到程序结束。我们称这种纯粹连续的方法为简单设计模式。
一个使用顺序设计模式的例子
请注意,这些块通过一根称为序列束的数据线彼此连接,而序列束通常是隐藏的。虽然我们通常认为这些块是从左到右执行的,但实际上是序列束决定了执行的顺序。要使序列波束可见,单击任意块右侧的连接器,下面的块将向右移动。
使序列束可见
按顺序开始设计程序是很自然的。然而,大多数时候,我们需要机器人处理的事件并不是按照预先知道的顺序发生的。例如,如果需要重复某些操作,该怎么办?如果在不同的具体情况下需要采取不同的行动怎么办?如果采取行动的顺序取决于当前的情况,该怎么办?如果我们需要在特殊情况下停止程序,该怎么办?
为了实现他的这种行为,我们需要一个更复杂的设计模式。
通用设计模式
当我们不得不重复一系列动作时,我们需要一个比顺序设计模式更复杂的结构。在一般设计模式中,结构由一些初始操作、一个包含主程序的循环和一些关闭操作组成。循环的主程序可能只是一个连续的程序,但是如果需要的话,它可能要复杂得多。
ev3通用设计模式示例中的通用设计模式
例如,让我们想象一个带颜色传感器的轮式机器人,它向前行驶,每次驶过一条绿线时发出蜂鸣声,然后停在一条红线上。我们不知道机器人在红线之前会遇到多少条绿线,甚至不知道它在到达红线之前会看到多少条绿线。那么我们如何使用严格的顺序模式来为机器人编程呢?除非我们确切地知道挑战有多少条线,以及它们的排列顺序,否则我们无法做到。如果一行被更改或删除,程序将不再工作。相反,我们可以使用通用的设计模式。
当然,我们可以在循环的每个迭代中依次执行许多操作,但是我们也可以使用嵌套的开关块来建立各种操作的优先级顺序,而不必知道它们在程序运行时以什么顺序出现。
例如,如果我们想让机器人不仅在穿过每条线时发出哔哔声,而且在遇到障碍物时改变方向,该怎么办?我们可以将颜色传感器开关嵌套在距离传感器开关中,反之亦然,这取决于应该首先检查哪个条件。
带有嵌套开关的通用设计模式示例
如果我们需要在同一时间以不同的速率重复运行不同的操作,该怎么办?在主循环中分割序列束是不够的,因为在最长的分割序列完成之前主循环不会重复!解决方案是并行运行不同的循环。
并行环路设计模式
并行循环设计模式是一种允许同时控制不同独立任务的基本编程策略。
并行循环设计模式
在前一个例子的第一个版本的基础上,让我们假设,除了向前移动并在穿过每条绿线时发出蜂鸣声外,我们还希望机器人在发现路径上的障碍时激活它的抓手。显然,爪需要独立运行,并在不同的速度比主程序。实现这一点的唯一方法是同时运行两个并行循环,一个用于主程序,另一个用于抓取器。
并行循环设计模式的例子
注意:必须注意避免竞态条件,当多个循环(或多个分支)中的程序块试图同时控制相同的资源时,可能会出现竞态条件。例如,如果并联回路中的电机块试图控制同一电机,则电机的行为将取决于回路的定时。竞态条件常常导致不可预测和不希望的结果。
但是现在的问题是:我们如何在并行循环设计模式的不同循环之间进行控制、协调和通信?我们可以使用数据线吗?答案是否定的,因为数据线不允许循环并行运行。事实上,连接到数据线路的块将不会执行,直到连接到线路左侧的块被执行。这就是为什么我们使用序列束作为一个非常特殊的数据线路,以确保程序块的顺序执行。答案是:我们需要使用变量。
主从设计模式
当您有两个或多个进程同时运行,但速度不同,需要某种协调时,主设计模式和从设计模式非常有用。
一个循环充当主人,其他循环充当奴隶。主循环控制所有从循环,并使用变量与它们通信。
在EV3中的aster/slave设计模式架构
举个例子,假设我们想要建立一个近距离传感器,当目标出现时它会发出哔哔声。此外,我们如何实现这一点,以这样一种方式,即物体越近,机器人发出的哔哔声越快?显然,哔哔声的重复速度与障碍物的接近程度成正比。此外,寻呼机的接近功能必须与机器人正在做的任何其他事情并行运行,并以自己的速度运行。如果距离已经在主程序循环(主循环)中读取,一种解决方案是将这个值存储在一个变量中,然后在一个单独的循环(从循环)中读取存储的值,该循环处理哔哔声。
主/从设计模式示例
如果我们分析这两个循环,我们能对它们的执行速度说些什么呢?
虽然读取传感器需要一些时间,比如在本例的主循环中,但是播放声音并等待重复声音(比如在从循环中)可能需要更长的时间来执行。因为从属循环比主循环运行得慢,所以我们可以预期,由主循环设置的一些距离值将会被从属循环遗漏。尽管如此,哔哔声似乎与障碍距离成正比。在这种情况下,从循环的运行速度是否比主循环慢并不重要。
状态机设计模式
在简单设计模式中,块按顺序执行。序列束建立程序块的执行顺序,从左到右。虽然顺序编程是最常见的编程风格之一,但从长远来看,它并不总是最有效、最灵活或最容易理解的方法。
例如:
•如果我们必须改变一系列动作的执行顺序,该怎么办?例如,如果触发不同动作的传感器事件被更改。
•如果我们不得不多次重复一个或多个动作,该怎么办?例如,重复几次转向一侧,或向前越过几条不同的颜色线。
•如果我们只在满足特定条件时才执行代码序列,那该怎么办?例如,如果检测到障碍物,绕过它,否则就沿着路线行驶。
•如果我们想要在满足特定条件时停止程序的执行,而不是等到序列的其余部分完成后再停止呢?
我们如何改进顺序编程,使其更实用、更紧凑、更容易理解、更容易调试?答案是:通过使用状态机设计模式。
状态机是由有限数量的状态和规则组成的系统,这些状态和规则决定了系统如何从一种状态过渡到另一种状态。在EV3软件中,这可以通过使用一个开关块来实现,该开关块包含每个状态的一种情况。每个案例都包含功能代码,功能代码由要在当前状态下执行的代码和设置下一个状态的转换函数组成。
EV3中状态机设计模式的基本元素
经常项目将有一个初始化状态,紧随其后的是一个默认的状态——例如,读取传感器做出决定,并呼吁其他国家或采取不同的行动,最后,它可以关闭状态结束之前执行一些操作程序。