现在都用什么框架开发软件或网页?

开发框架主要分为三个部分:前端、web和持久性,可以自由组合。我单独谈。

目前,只有一个选择:SpringMVC,其他人都不能玩。无论是玩耍、高格、火花还是Struts2。springmvc在用户数量和生态方面与springmvc不一样。

不管其他框架宣传得多好,都不建议投入大量精力。学习框架的设计思路还可以。请直接在生产环境中使用springmvc。spring的基础设施无法与其他框架相比。弹簧靴可用于快速开发。POM可以依赖于enable注释直接使用。

目前,有两种主流的JPA和mybatis。

如果项目是数据建模,请使用mybatis;如果项目是对象建模,请使用JPA。

春天来了。springboot的spring数据子项目应该希望将两者统一起来。不仅两者,而且其野心更大。它希望统一所有持久性层。DB、redis和elastic search都有相应的项目。它希望将所有持久性操作抽象为存储库操作。如果成功,业务逻辑就不需要关心持久层的选择,交换框架也非常方便。

目前,前端框架有三个选项:react、Ag和Vue。

我不喜欢react….的语法。。。。

似乎Ag在中国的知名度不高,所以我们不太关注。

版权声明:本文内容由互联网用户自发贡献,本站不承担相关法律责任。如有侵权/违法内容,本站将立刻删除。

点击上方“大鱼机器人”,选择“置顶/星标公众号”

福利干货,第一时间送达!

整理 :嵌入式云IOT技术圈,作者:veryarm

Protocol Buffer的编码格式是二进制的,同时也提供可读的文本格式。效率高,体积小,上下兼容。目前支持Java,Python和C++,很快会支持更多的语言。

5.1. 从自动化代码生成更进一步

面向语言编程的通俗定义是:将特定领域的知识融合到一种专用的计算机语言当中,从而提高人与计算机交流的效率。

自动化代码生成其实就是面向语言编程。语言不等于是编程语言,可以是图,也可以是表,任何可以建立人和机器之间交流渠道的都是计算机语言。软件开发历史上的一次生产率的飞跃是高级语言的发明。它让我们以更简洁的方式实现更复杂的功能。但是高级语言也有它的缺点,那就是从问题领域到程序指令的过程很复杂。因为高级语言是为通用目的而设计的,所以离问题领域很远。举例来说,要做一个图形界面,我可以跟另一个工程师说:这里放一个按钮,那边放一个输入框,当按下按钮的时候,就在输入框里显示Hello World。我甚至可以随手给他画出来。

对于我和他直接的交流而言,这已经足够了,5分钟。但是要让转变为计算机能够理解的语言,需要多久?

如果是汇编语言?(告诉计算机如何操作寄存器和内存)

如果是C++? (告诉计算机如何在屏幕上绘图,如果响应鼠标键盘消息)

如果有一个不错的图形界面库?(告诉计算机创建Button,Label对象,管理这些对象,放置这些对象,处理消息)

如果有一个不错的开发框架+IDE? (用WYSIWYG工具绘制,设计类,类的成员变量,编写消息响应函数)

如果有一门专门做图形界面开发的语言?

通用的计算机语言是基于变量,类,分支,循环,链表,消息这些概念的。这些概念离问题本身有着遥远的距离,而且表达能力非常有限。自然语言表达能力很强,但是歧义和冗余太多,无法格式化标准化。传统的思想告诉我们:计算机语言就是一条条的指令,编程就是写下这些指令。而面向语言编程的思想是,用尽量贴近问题,贴近人的思维的办法来描述问题,从而降低从人的思想到计算机软件转换的难度。

举一个游戏开发的例子。现在的网络游戏普遍的采用了C++或者C开发游戏引擎。而具体的游戏内容,则是由一系列二次开发工具和语言完成的。地图编辑器就是一种面向游戏的语言。Lua或者类似的脚本则被嵌入到游戏内部,用来编写武器,技能,任务等等。Lua本身不具备独立开发应用程序的能力,然而游戏引擎的设计者通过给Lua提供一系列的,各种层次上的接口,将领域知识密集的赋予了脚本,从而大大提高了游戏二次开发的效率。网络游戏的鼻祖MUD则是设计了LPC来作为游戏的开发语言。MUD的引擎MudOS和LPC之间的关系如图:

用LPC创建一个NPC的代码类似如下:

LPC培养了一大批业余游戏开发者,甚至成为很多人进入IT行业的起点。原因就是它简单,易理解,100%为游戏开发设计。这就是LOP的魅力。

LOP最重要的优点是将领域知识固化到语言中,从而:

  1. 优化团队结构,降低交流成本,领域专家和程序员可以更好的合作。

其次,由于LOP不是通用语言,所涉及的范围就狭窄很多,所以:

相应的,LOP也有它的劣势:

  1. LOP对领域知识抽象的要求比框架更高。

  2. 开发一门新的语言本身的成本。幸好现在设计一门新的语言不算太难,还有Lua这样的“专用二次开发”语言的支持。

  3. 性能损失。不过相比开发成本的节约,在非性能核心部分使用LOP还是很值得的。

5.3. 在嵌入式系统中的应用

举例,嵌入式设备的Web服务器。很多设备都提供Web服务用于配置,比如路由器,ADSL猫等等。这种设备所提供的web服务的典型用例是用户填写一些参数,提交给Web服务器,Web 服务器将这些参数写入硬件,并将操作结果或者其他信息生成页面返回给浏览器。由于典型的Apache,Mysql,PHP组合体积太大且不容易移植,通常嵌入式系统的Web服务都是用C/C++直接写就的。从socket管理,http协议到具体操作硬件,生成页面,都一体负责。然而对于功能复杂,Web界面要求较高的情况,用C来写页面效率就太低了。

shttpd是一个小巧的web服务器,小巧到只有一个.c文件,4000余行代码。虽然体积很小,却具备了最基本的功能,比如CGI。它既可以独立运行,也可以嵌入到其他的应用程序当中。shttpd在大多数平台上都可以顺利编译、运行。lua是一个小巧的脚本语言,专用于嵌入和扩展。它和C/C++代码有着良好的交互能力。

将Lua引擎嵌入到shttpd中,再使用C编写一个(一些)驱动硬件的扩展,注册成为Lua的函数,形成的系统结构如下图:

这样的应用在嵌入式系统中是有一定代表性的,即,以C实现底层核心功能,而把系统的易变部分以脚本实现。大家可以思考在自己的开发过程中是否可以使用这种技术。这是LOP的一种具体应用模式。(没有创造一种全新的语言,而是使用Lua)

6.1. 可测试性是软件质量的一个度量指标

好的软件是设计出来的,好的软件也一定是便于测试的。一个难于测试的软件的质量是难以得到保障的。在今天软件规模越来越大的趋势下,以下问题是普遍存在的:

  1. 测试只能手工进行,回归测试代价极大,实际只能执行点测,质量无法保证

  2. 各个模块只有集成到一起后才能测试

  3. 代码不经过任何单元测试就集成

这些问题的根源都在于缺乏一个良好的软件设计。一个好的软件设计应该使得单元测试,模块测试和回归测试都变得容易,从而保证测试的广度和深度,最终产生高质量的软件。除了功能,非功能性需求也必须是可测试的。所以,可测试性是软件设计中一个重要的指标,是系统架构师需要认真考虑的问题。

6.2. 测试驱动的软件架构

这里谈的是测试驱动的软件架构,而不是测试驱动的开发。TDD(Test Driven Development) 是一种开发方式,是一种编码实践。而测试驱动的架构强调的是,从提高可测试性的角度进行架构设计。软件的测试分为多个层次:

系统测试是指由测试人员执行的,验证软件是否完整正确的实现了需求的测试。这种测试中,测试人员作为用户的角色,通过程序界面进行测试。在大部分情况下这些工作是手工完成的。在规范的流程中,这个过程通常要占到整个软件开发时间的1/3以上。而当有新版本发布的时候,尽管只涉及了软件的一部分,测试部门依然需要完整的测试整个软件。这是由代码“副作用”特点决定的。有时候修改一个bug可以引发更多的bug,破坏原来工作正常的代码。这在测试中叫回归测试(Regression test)。对于规模较大的软件,回归测试需要很长的时间,在版本新增功能和错误修正不多的情况下,回归测试可以占到整个软件开发过程了一半以上,严重影响了软件的交付,也使软件测试部门成为软件开发流程中的瓶颈。测试过程自动化,是部分解决这个问题的办法。

作为架构师,有必要考虑如何实现软件的可自动化测试性。

在没有图形化界面以前,字符方式的界面是比较容易进行自动化测试的。一个编写良好的脚本就可以实现输入和对输出的检查。但是对于图形化的界面,人的参与似乎变得不可缺少。有一些界面自动化的测试工具,如WinRunner, 这些工具可以记录下测试人员的操作成为脚本,然后通过回放这些脚本,就可以实现操作的自动化。针对嵌入式设备,有Test Quest可以使用,通过在设备中运行一个类似远程桌面的Agent,PC端的测试工具可以用图像识别的方法识别出不同的组件,并发送相应用户的输入。此类工具的基本工作原理如图:

但是这个过程在实际中存在三个问题:

  1. 可靠性差,经常中断运行。要写一个可靠的脚本甚至比开发软件还要困难。比如,按下一个按钮,有时候一个对话框立刻就出现,有时候可能要好几秒,有时候甚至不出现,操作录制工具不能自动实现这些判断,而需要手动修改。

  2. 对操作结果的判断很困难,尤其是非标准的控件。

  3. 当界面修改后,原有代码很容易失效

要应用基于图形界面的自动化测试工具,架构师在架构的时候应该考虑:

  1. 界面风格如何保持一致。应当由架构,而非程序员决定架构的风格。包括布局,控件大小,相对位置,文字,对操作的响应方式,超时时长,等等。

  2. 如何在最合适测试工具的界面和用户喜欢的界面之中折中。比如,Test Quest是基于图像识别的,那么黑白两色的界面是最有利的,而用户喜欢的渐进色就非常不利。也许让界面具备自动的切换能力最好。

对于已经完成的产品,如果架构没有为自动化测试做过考虑,所能应用的范围就非常有限,不过还是有一些思路可以供参考:

  1. 实现小规模的自动化脚本。针对一个具体的操作流程进行测试,而不是试图用一个脚本测试整个软件。一系列的小测试脚本组成了一个集合,覆盖系统的一部分功能。这些测试脚本可以都以软件启动时的状态作为基准,所以在状态处理上会比较简单

  2. ”猴子测试”有一定的价值。所谓猴子测试,就是随机操作鼠标和键盘。这种测试完全不理解软件的功能,可以发现一些正常测试无法发现的错误。据微软内部的资料,微软的一些产品15%的错误是由“猴子测试”发现的。

总的来讲,基于界面的自动化测试是不成熟的。对架构师而言一定要避免功能只能通过界面才能访问。要让界面仅仅是界面,而软件大部分的功能是独立于界面并可以通过其他方式访问的。上面框架的例子中的设计就体现了这一点。

思考:如何让界面具有自我测试功能?

6.3.2. 基于消息的自动化测试

如果软件对外提供基于消息的接口,自动化测试就会变得简单的多。上面已经提到了固件的TL1接口。对于界面部分,则应该在设计的时候,将纯粹的“界面”独立出来,让它尽可能的薄,而其他部分依然可以基于消息提供服务。

在消息的基础上,用脚本语言包装成函数的形式,可以很容易的调用,并覆盖消息的各种参数组合,从而提高测试的覆盖率。关于如何将消息包装为脚本,可以参考SOAP的实现。如果使用的不是XML,也可以自己实现类似的自动代码生成。

这些测试脚本应该由开发人员撰写,每当实现了一个新的接口(也就是一条新的消息),就应该撰写相应的测试脚本,并作为项目的一部分保存在代码库中。当需要执行回归测试的时候,只要运行一遍测试脚本即可,大大提高了回归测试的效率。

所以,为了实现软件的自动化测试,提供基于消息的接口是一个很好的办法,这让我们可以在软件之外独立的编写测试脚本。在设计的时候可以考虑这个因素,适当的增加软件消息的支持。当然,TL1只是一个例子,根据项目的需要,可以选择任何适合的协议,如SOAP。

在编写自动化测试脚本的时候,有很多的工作是重复的,比如建立socket连接,日志,错误处理,报表生成等。同时,对于测试人员来说,这些工作可能是比较困难的。因此,设计一个框架,实现并隐藏这些重复和复杂的技术,让测试脚本的编写者将注意力集中在具体的测试逻辑上。

这样一个框架应该实现以下功能:

  1. 完成连接的初始化等基础工作。

  2. 捕获所有的错误,保证Test Case中的错误不会打断后续的Test Case执行。

  3. 自动检测和执行Test Case。新增的Test Case是独立的脚本文件,无须修改框架的代码或者配置。

  4. 消息编解码,并以函数的方式提供给Test Case编写者调用。

  5. 方便的工具,如报表,日志等。

  6. 自动统计Test Case的运行结果并生成报告。

自动化测试框架的思路和一般的软件框架是一致的,就是避免重复劳动,降低开发难度。

下图是一个自动化测试框架的结构图:

每个Test Case都必须定义一个规定的Run函数,框架将依次调用,并提供相应的库函数供Test Case用来发送命令和获得结果。这样,测试用例的编写者就只需要将注意力集中在测试本身。举例:

测试用例的编写者拥有的知识是“必须先打开激光器然后才能向线路上插入错误”。而架构师能提供的是消息收发,编解码,错误处理,报表生成等,并将这些为测试用例编写者隔离。

问题:如何进一步实现知识的解耦?能否有更方便的语言来编写TestCase?

有了自动化的测试脚本和框架,回归测试就变得很简单了。每当有新版本发布时,只需运行一遍现有的Test Case,分析测试报告,如果有测试失败的Case则回归测试失败,需要重新修改,直到所有的Case完全通过。完整的回归测试是软件质量的重要保证。

集成测试要验证的是系统各个组成模块的接口是否工作正常。这是比系统测试更低层的测试,通常由开发人员和测试人员共同完成。

例如在一个典型的嵌入式系统中,FPGA,固件和界面是常见的三个模块。模块本身还可以划分为更小的模块,从而降低复杂度。嵌入式软件模块测试的常见问题是硬件没有固件则无法工作,固件没有界面就无法驱动;反过来,界面没有固件不能完整运行,固件没有硬件甚至无法运行。于是没有经过测试的模块直到集成的时候才能完整运行,发现问题后需要考虑所有模块的问题,定位和解决的代价都很大。假设有模块A和B,各有十个bug。如果都没有经过模块测试直接集成,可以认为排错的工作量相当于10*10等于100。

所以,在设计一个模块的时候,首先要考虑,这个模块如何单独测试?比如,如果界面和固件之间是通过SOCKET通信的,那么就可以开发一个模拟固件,在同样的端口上提供服务。这个模拟固件不执行实际的操作,但是会响应界面的请求并返回模拟的结果。并且返回的结果可以覆盖到各种典型的情况,包括错误的情况。使用这样的技术,界面部分几乎可以得到100%的验证,在集成阶段遇到错误的大大减少。

对固件而言,因为处于系统的中间,所以问题复杂一些。一方面,要让固件可以通过GUI以外的途径被调用;另一方面则要模拟硬件的功能。对于第一点,在设计的时候,要让接口和实现分离。接口可以随意的更换,比如和GUI的接口也许是JSON,同时还可以提供telnet的TL1接口,但是实现是完全一样的。这样,在和GUI集成之前,就可以通过TL1进行完全的测试固件。对于第二点,则应该在设计的时候提取出硬件抽象层,让固件的主要实现和寄存器,内存地址等因素隔离开来。在没有硬件或者硬件设计未定的时候实现一个硬件模拟层,来保证固件可以完整运行并测试。

单元测试是软件测试的最基本单位,是由开发人员执行以保证其所开发代码正确的过程。开发人员应该提交经过测试的代码。未经单元测试的代码在进入软件后,不仅发现问题后很难定位,而且通过系统测试是很难做到对代码分支的完全覆盖的。TDD就是基于这个层次的开发模式。

单元测试的粒度一般是函数或者类,例如下面这个常用函数:

这是一个功能非常单一的函数,所以单元测试对它非常有效。可以通过单元测试验证下列情况:

  1. 一般正常调用,如”9”,”1000”,”-1”等

  2. 超过一个的-号和位置错误的-号,”—1”,”-1-“,”-1-2”

如果atoi通过了以上测试,我们就可以放心的将它集成到软件中去了。由它再引发问题的概率就很小了(不是完全没有,因为我们不能遍历所有可能,只是挑选有代表性的异常情况进行测试)。

以上的例子可以说是单元测试的典范,但实际中却常常不是这么回事。我们常常发现写好的函数很难做单元测试,不仅工作量很大,效果也不见得好。其根本的原因是,函数没有遵循好一些原则:

反观atoi的例子,功能单一明确,和其他函数几乎没有任何耦合(我上面并没有写atoi的代码实现,大家可以自己实现,希望是0耦合)。

下面我举一个实际中的例子。

这是一个简单的TL1命令发送和解析软件,功能需求描述如下:

ü 发送TL1命令给TL1服务器

ü 解析TL1服务器的响应

TL1是通讯行业广泛使用的一种协议,为了给不熟悉TL1的朋友简化问题,我定义了一个简化的格式:

CMD - 命令的名字,可以是任意字母开头,由字母和下划线组成的字符串 CTAG - 一个数字,用于标志命令的序号 相应的,TL1服务器的回应也有格式: CTAG – 一个数字,和TL1 命令所携带的CTAG一样 COMPLD – 表明命令执行成功 PAYLOAD - 返回的结果,可以是任何格式的内容

软件的最上层可能是这样的:

以GET_IP_CONFIG为例,这个函数应该完成的功能包括:

ü 建立telnet连接,如果连接尚未建立

ü 构造TL1命令字符串

ü 解析反馈,并给IP_CONF结构复制

我们当然不希望每个这样的函数都重复实现这些功能,所以我们定义了几个模块:

这里我们来分析TL1结果解析,假设设计为一个函数,函数的原型如下:

这个函数的功能是接受一个字符串,如果它是一个合法且已知的TL1回应,则将其中的结果提取出来,放入一个字典对象中。

这本来会是一个很便于进行单元测试的例子:输入各种字符串,检查返回结果是否正确即可。但是在这个软件中,有一个很特殊的问题:

TL1Parse在解析一个字符串时,它必须要知道当前要处理的是哪条命令的回应。但是请注意,在TL1的回应中,是不包括命令的名字的。唯一的办法是使用CTAG,这个命令和回应一一对应的数字。Tl1Parse首先提取出CTAG来,然后查找使用这个CTAG的是什么命令。这里产生了一个对外调用,也就是耦合。

有一个对象维护了一个CTAG和命令名字对应关系的表,通过CTAG,可以查询到对应的命令名,从而知道如何解析这个TL1 response.

如此一来,TL1Parse就无法进行单元测试了,至少不能轻易的进行。通常的桩函数的办法都不好用了。

将TL1Parse拆分为两个函数:

这两个函数都可以单独进行完整的单元测试。而这两个函数的代码基本就是TL1Parse切分了一下,但是其可测试性得到了很大的提高,得到一个可靠的解析器的可能性自然也大大提升了。

这个例子演示了如何通过设计来提高代码的可测试性—这里是单元测试。一个随意设计,随意实现的软件要进行单元测试将会是一场噩梦,只有在设计的时候就考虑到单元测试的需要,才能真正的进行单元测试。

模块的复杂度直接影响了单元测试的覆盖率。最著名的度量代码复杂度的方法是圈复杂度测量。

计算公式:V(F)=e-n+2。其中e是流程图中的边的数量,n是节点数量。简单的算法是统计如 if、while、do和switch 中的 case 语句数加1。适合于单元测试的代码的复杂度一般认为不应该超过10。

扇入是指一个模块被其他模块所引用。扇出是指一个模块引用其他模块。我们都知道好的设计应该是高内聚低耦合的,也就是高扇入低扇出。一个扇出超过7的模块一般认为是设计欠佳的。扇出过大的模块进行单元测试不论从桩设置还是覆盖率上都是困难的。将系统的传出耦合和传入耦合的数量结合起来,形成另一个度量:不稳定性。

这个值的范围从0到1。值越接近1,它就越不稳定。在设计和实现架构时,应当尽量依赖稳定的包,因为这些包不太可能更改。相反的,依赖一个不稳定的包,发生更改时间接受到伤害的可能性就更大。

6.5.3. 框架对单元测试的意义

框架的应用在很大程度上可以帮助进行单元测试。因为二次开发者被限定实现特定的接口,而这些接口势必都是功能明确,简单,低耦合的。之前的框架示例代码也演示了这一点。这再次说明了,由高水平的工程师设计出的框架,可以强制初级工程师产生高质量的代码。

7. 维护架构的一致性

在实际的开发中,代码偏离精心设计的架构是很常见的事情,比如下图示例了一个嵌入式设备中设计的MVC模式:

View依赖于Controller和Model, Controller依赖于Model,Model作为底层服务提供者,不依赖View或者Controller. 这是一个适用的架构,可以在相当程度上分离业务,数据和界面。但是,某个程序员在实现时,使用了一个从Model到View的调用,破坏了架构。

这种现象通常发生在产品的维护阶段,有时也发生在架构的实现阶段。为了增加一个功能或者修正一个错误,程序员由于不理解原有架构的思路,或者只是单纯的偷懒,走了“捷径”。如果这样的实现不能及时发现并纠正,设计良好的架构就会被渐渐破坏,也就是我们常说的“架构”腐烂了。通常一个有一定年龄的软件产品的架构都有这个问题。如何监视并防止这种问题,有技术上的和管理上的手段。

技术上,借助工具,可以对系统组件的依赖进行分析,架构的外在表现最重要的就是各个部分的耦合关系。有一些工具可以统计软件组件的扇入和扇出。可以用这种工具编写测试代码,对组件的扇出进行检测,一旦发现测试失败,就说明架构遭到了破坏。这种检查可以集成在一些IDE中, 在编译时同步进行,或者在check in的时候进行。更高级的工具可以对代码进行反向工程生成UML,可以提供更进一步的信息。但通常对扇入扇出做检查就可以了。

通过设置代码检视的开发流程,对程序员check in的代码进行评审,也可以防止此类问题。代码检视是开发中非常重要的一环,它属于开发后期阶段用来防止坏的代码进入系统的重要手段。代码检视通常要关注以下问题:

  1. 是否正确完整的完成了需求

代码检视通常以会议的形式进行,时间点设置在项目阶段性完成,需要check in代码时。对于迭代式开发,则可以在一个迭代周期结束前组织。参与人员包括架构师,项目经理,项目成员,其他项目的资深工程师等。一般时间不要太长,以不超过2个小时为宜。会议前2天左右发出会议通知和相关文档代码,与会者必须先了解会议内容,进行准备。会议中,由代码的作者首先讲解代码需要实现的功能,自己的实现思路。然后展示代码。与会者根据自己的经验提出各种问题和改进意见。这种会议最忌讳的是让作者感到被指责或者轻视,所以,会议组织者要首先定义会议的基调:会议成功与否的标准不是作者的代码质量如何,而是与会者是否提供了有益的建议。会后由作者给与会者打分,而不是反之。

8. 一个实际嵌入式系统架构的演化

上世纪九十年代,互联网的极速发展让通讯测试设备也得到了极大的发展。那个年代,能够实现某种测量的硬件是竞争的核心,软件的目的仅仅是驱动硬件运行起来,再提供一个简单的界面。所以,最初的产品的软件结构非常简单,类似前面的城铁门禁系统。

优点:程序简单明了的实现了用户的需求,一个程序员就可以全部搞定。

缺点:完全没有划分模块,底层上层耦合严重。

用户要求能将测量结果保存下来,并可以重新打开。数据存储模块和界面被独立出来。

依然保持上面的主逻辑,但是界面部分不仅可以显示实时的数据,也可以从ResultManager中读取数据来显示。

优点:数据和界面分离的雏形初步显现

缺点:ResultManager只是作为一个工具存在,负责保存和装载历史数据。界面和数据的来源依然耦合的很紧。不同的界面需要的不同数据都是通过硬编码判断的。

随着功能不断复杂,界面窗口越来越多,原来靠一个类来绘制各种界面的方式已经不能承受。于是窗口的概念被引入。每个界面都被视为一个窗口,窗口中的元素为控件。窗口的打开,关闭,隐藏则由窗口管理器负责。

优点:界面功能以窗口的单位分离,不再是一个超大的集合。

缺点:虽然有了窗口管理器,但是界面依然是直接和底层耦合的,依然是大循环结构。

随着规模进一步扩大,最初的大循环结构终于无法满足日益复杂的需求了。标准的MVC模式被引入,经历了一次大的重构。

数据中心作为Model被独立出来,保存着当前最新的数据。View被放在了独立的任务中执行,定期从DataCenter轮询数据。用户的操作通过View发送给Controller,进一步调用硬件驱动执行。硬件执行的结果从驱动到Controller更新到DataCenter中。界面,数据,命令三者基本解耦。ResultManager成为DataCenter的一个组件,View不再直接与其通讯。

MVC模式的引入,第一次让这个产品了有真正意义上职责明晰,功能独立的架构。

8.4. 大量类似模块,低效的复用

到上一步,作为一个单独的嵌入式设备,其架构基本可以满足需求。但是随着市场的扩展,越来越多的设备被设计出来。这些设备虽然执行的具体测量任务不同,但是他们都有着同样的操作方式,类似的界面,更主要的是,它们面临的问题领域是相同的。长期以来,复制和粘贴是唯一的复用方式,甚至类名变量名都来不及改。一个错误在一个设备上被修正,同样一段代码的错误在其他设备上却来不及修改。而随着团队规模的扩大,甚至MVC的基本架构在一些新设备上都没能遵守。

最终框架被引入了这个系列的产品。框架确定了如下内容:

  1. 窗口管理器和组件布局算法

  2. 多国语言方案(字符串管理器)

  3. 内存分配器和内存泄露检测

客户希望将设备固定安放在网络的某个位置,作为“探针”使用,在办公室通过远程控制来访问这个设备。这对于原本是作为纯手持设备设计的系统又是一个挑战。幸运的是,MVC架构具有相当的弹性,早期的投入获得了回报。

由于TL1命令相当多,而TL1又往往不是客户的第一需求,很多设备的TL1命令开始不完整。究其原因,还是手写TL1命令的解释器太累。后来通过引入Bison和Flex,这个问题有所改善,但还是不足。自动化代码生成在这个阶段被引入。通过以如下的格式定义TL1,工具可以自动生成TL1的编码和解码器代码。

经过数十年的积累,产品已经成为一个系列,几十种设备。大部分设备进入了维护期,经常有客户提一些小的改进,或者要求修正一下缺陷。繁重的手工回归测试成为了噩梦。

基于TL1的自动化测试极大的解放了测试人员。通过在PC上运行的测试脚本,回归测试变得简单而可靠。唯一不足的是界面部分无法验证。

基于Test Quest的自动化工具需要在设备运行的pSOS系统上开发一个类似远程桌面的软件,而这在pSOS上并非易事。不过好消息是,由于框架固定了界面的风格和布局算法,基于Test Quest的自动化工具会有很高的识别效率。

从这个实际的嵌入式产品重构的历程可以看出,第三步引入MVC模式和第四步的框架化是非常关键的。成熟的MVC模式保证了后续一系列的可扩充性,而框架则保证了这个架构的在所有产品中的准确重用。

本文是针对嵌入式软件开发的特点,讨论架构设计的思路和方法。试图给大家提供一种思想,启发大家的思维。框架,自动化代码生成和测试驱动的架构是核心内容,其中框架又是贯穿始终的要素。有人问我,什么是架构师,怎么样才能成为架构师?我回答说:编码,编码,再编码;改错,改错,再改错。当你觉得厌烦的时候,停下来想想,怎么才能更快更好的完成这些工作?架构师就是在实践中产生的,架构师来自于那些勤于思考,懒于重复的人。




若觉得文章不错,转发分享,也是我们继续更新的动力。

在公众号内回复「更多资源」,即可免费获取,期待你的关注~

长按识别图中二维码关注

C/S系统开发框架旗舰版为软件团队提供强大的技术支撑以及快速开发能力,开发框架集成大量的通用开发包与工具实用类,提供丰富的例子,借助技术文档、网站资源、演示源码以及在线技术指导,用户能快速投入研发自己的项目。自2007年首发开发框架以来,我们成功积累了2000多位用户,其中包括数百家软件公司或大型集团企业。经过十年的产品迭代升级,最新旗舰版技术更成熟,性能更稳定,产出效率更高!

    配套的代码生成器工具能自动生成界面(FORM)、业务层(BLL)、数据层(DAL)、模型(Model)、报表(Report)以及业务模块(Class Library DLL)的源代码,极大地提高开发效率、节约开发时间、降低项目成本。据多年项目实践统计,开发框架基本能完成项目大概60%以上的工作量。

我要回帖

更多关于 前端开发常用框架 的文章

 

随机推荐