《 嵌入式系统设计与实践》一一2.3 一个可以实践的沙盒

2.3 一个可以实践的沙盒
前面部分很好地覆盖了底层和中间层的模块,但还有一些算法模块需要考虑。好的架构设计的目标之一就是尽可能地让算法保持独立。常用的模型-视图-控制器(MVC)模式就是解决这个问题很好的方法。这个模式的目的是将应用程序的业务逻辑从用户界面中分离出来,这样可以独立地对它们进行开发和测试。
在这个模式中,视图是提供给用户的界面,包括了输入和输出。在我们的设备中,用户可能不是一个人,它可以是一个硬件传感器(输入)和一个屏幕(输出)。事实上,如果系统没有屏幕,但是它通过网络传送数据,这时候视图没有一个可见的外观,但它作为输入和输出,仍然是系统的一部分。模型是与特定领域相关的数据和逻辑。它从输入部分获取原始数据,然后使用那些让你的产品与众不同的算法,创建出一些有用信息。控制器将模型和视图胶合在一起:它负责处理如何从输入获取数据并送给模型处理,以及如何将来自模型的数据显示或者与外部通信。关于这三个元素如何交互,有一些标准的方法。但是现在我们只需要理解各个模块的功能划分就足够了。
“模型-视图-控制器”的不同方面
“模型-视图-控制器”好的一面就是几乎所有人都认同它的这三个部分,坏的一面是这可能是所有人都认同的唯一一点。
模型包含了数据、状态和应用逻辑。如果你正在构建一个气象站系统,模型中有温度监视代码和预测模型。对于一个MP3播放器来说,模型由音乐数据库和播放音乐的必要代码组成。
按照传统思维,在有屏幕的情况下,视图代表了显示处理函数。视图表示用户从模型中能看到什么。视图可以是一个太阳的图片,或者从气象服务中读取的详细的统计数据。这都是同一个信息的不同视图。
控制器就好像是在模型和视图之间有些模糊的云层,以便让它们可以一起工作。控制器的目标是让视图和模型相互独立,这样每个部分都可以重用。对于MP3播放器,公司可能希望有一个一致的用户界面,即使音频播放硬件重新设计。或者,对于同样的硬件,市场部希望这个系统能够更适合儿童使用。怎么做才能在代码改动最少的情况下同时满足这些需求呢?控制器可以通过提供服务将模型/视图分离,如将用户按压按钮的事件转换成模型中的一个动作。
图2-8展示了“模型-视图-控制器”的一个基本解释和一些常见变体。除此之外,还有其他相当多的不同表示方法,有些看起来是矛盾的。MVC这个术语有点类似形容词——乐观,可以表示充满杀气,也可以表示盲目乐观。需要借助一定的上下文线索,才能弄清楚到底是哪个意思。但是,任何一种方法都有可能让别人心情不爽。
《 嵌入式系统设计与实践》一一2.3 一个可以实践的沙盒

图2-8:“模型-视图-控制器”模式概览
有另一种方式使用MVC模式,对于嵌入式系统的算法开发和验证来说,这是一个非常有价值的方法:虚拟盒子或者沙盒。算法越复杂,越有必要采用这种方法。
将所有算法模块的输入都设计成一个接口,这样我们就可以将个人计算机上的文件和实际系统之间来回切换。同样的道理,也可以将算法的输出重新定向到一个文件。现在,就可以在个人计算机上测试算法了(调试环境可能比嵌入式系统环境好很多),这样可以一遍又一遍地运行同样的数据直到任何异常都被隔离和解决。做一些回归测试,对小的算法改变进行验证以确保没有产生不可预见的副作用,这是个非常好的做法。
在沙盒环境中,文件以及将读数据的方法是MVC中的视图部分。要测试的算法就是模型,是不应该改变的。控制器是把算法放到个人计算机上时改变的那个部分。考虑MP3播放器(见图2-9),在沙盒下,MVC框图是什么样的呢?注意,视图和控制器都有相对严格的应用程序编程接口(API),因为从虚拟设备迁移到真实设备时会替换这些接口。
《 嵌入式系统设计与实践》一一2.3 一个可以实践的沙盒

图2-9:沙盒中的“模型-视图-控制器”
输入视图文件可能像一个电影剧本,指示沙盒采取一些用户期望的动作。第一个字段是时间,它用来表示第二个字段,即系统里的方法在什么时候会被触发:
Time, Action, Variable arguments // comment
00:00.00, PowerOnClean // do all of the initialization
00:01.00, PlayPressed // no action, no song loaded
00:02.00, Search, "Still Alive" // expect list back based on available matches
00:02.50, PlayPressed // expect song to be played
根据需要,输出文件可以有多种形式。比如,可以有多个输出文件,一个用来描述状态以及从控制器和模型发送到用户界面的变化信息,另一个将模型发送到数模转换器(DAC)的信息输出。第一个输出文件类似这样:
Time, Subsystem, Log message
00:00:01, System, Initilization: sandbox version 1.3.45
00:01:02, Controller, Minor error: no song loaded
00:02:02, Controller, 4 songs found for "Still Alive" search, selecting ID 0x1234
00:02.51, Controller, Loading player class to play ID 0x1234
再次强调,需要定义满足具体需要的格式(通常会变化)。例如,假设有一个缺陷,播放器播放了错误的歌曲,那么就可以让沙盒在每一行都输出歌曲的ID。
“模型-视图-控制器”是一个很高层次的、系统级的设计模式。一旦将系统按这种方式进行了分解,就可以发现它有分形的特点。例如,如果仅仅要求从内存中读取文件并输出到模数转换器该怎么做?文件名和将要输出到模数转换器的位流可以视为视图(由输入/输出组成,而不管它们是否与人交互),将文件进行转换的必要逻辑和数据就是模型,而对文件的处理则是控制器,因为在切换平台时会发生改变。
将分离和沙盒的概念记在心里,再来看看架构图上的一个算法有几个输入。如果有多个输入,那么设计一个单独的接口对象是否可行?有时候,用多个文件来表示多个正在处理的事情可能会更好。进一步研究架构图,将虚拟盒子应用到底层模块是不是比仅仅把它用在产品特征算法上要更有道理?这样做,拓展了设计模型,更重要的是,可以在一个受控的环境下更多地对代码进行测试。
开发虚拟盒子可能比较昂贵,因此也许不需要立即着手去实现它。特别是当虚拟盒子对变量有不同的大小以及编译器可能有不同的行为时,因此除非算法变得复杂或者要等待很长的时间硬件才能可用,否则就不会去实现它。因此我们需要在虚拟盒子的开发时间和它强大的调试和测试能力之间做一个权衡。在设计架构的时候,找出如何从设计中实现虚拟盒子并将其记在心里,这样会在设计出错的时候留下足够的回旋余地。
在资源和时间约束下继续前进
从零开始创建一个架构图可能是令人望而生畏的。如果你刚加入一个团队,有一个就要到达的最终期限,一堆代码,很少的文档而且没有时间去写更多的文档,这时候可能会更难。系统架构图还是必要的,尽管你可以将它画在你的笔记本中并竭尽全力去完善它。你能够看到系统结构会在你开发系统中对你有所帮助,它能够帮助你提供高质量的代码和符合最终期限。
原理图(或者硬件框图,如果有的话)依然是一个比较好的起点。在此之后,可以得到代码文件的列表。如果文件太多,难以计数,可以用目录名或者库。有些人决定这些是模块,或者他们会试着将这些文件看成对象,于是这些文件就成为我们图中的方框。如何放置这些方框,如何组织它们,需要花时间才能找到答案。
你可以考虑描述两个架构:一个为你正在工作的代码服务;另一个从全局的角度描述整个产品,这样你就可以知道你所负责的那部分如何与整个系统配合。当架构日趋变大时,需要一定的深度,特别对于一个成熟的设计来说。如果设计图很复杂,可以将多个方框合为一个,然后在另一个图中将其展开。
你采用什么样的风格画图取决于哪个让你感到比较得心应手。也许在你最终决定采用某个之前,你需要尝试稍许多一点的不同方法。

上一篇:【Cocos2d-X(2.x) 游戏开发系列之三】最新版本cocos2d-2.0-x-2.0.2使用资源加载策略


下一篇:大型网络实战之ospf路由协议