BT

如何利用碎片时间提升技术认知与能力? 点击获取答案

Flash务实主义(四)——Flash中的MVC

| 作者 flashyiyi 关注 0 他的粉丝 发布于 2011年4月15日. 估计阅读时间: 18 分钟 | QCon上海2018 关注大数据平台技术选型、搭建、系统迁移和优化的经验。

FLASH与传统环境的不同点

MVC最早在1979年的时候第一次被人提出。不过,当时还不存在网络应用的概念。之后当万维网诞生之后,又过了很长时间……

它并不是自诞生就开始流行的,而改变的原因很简单——因为两个极其流行的开发框架包含了这种模式,它们就是:Struts 和 Ruby on Rails。之后,模仿者蜂拥而至。所以,在人们眼里看来,实际上是先有的Struts,然后才有的MVC,也无怪乎MVC的概念会始终沾染着Web概念,乃至和一些框架附加内容牵涉不清。

因为Struts很好用,别的不说,至少让HTML显得干净了很多。所以很多人都在用Struts,这未必是因为需要MVC模式,而是因为他们需要Struts。因此,当环境变化后,我们不使用Struts而是在使用一些其他的框架的时候,是否还应该像以前那样使用MVC框架就成为了一个问题。因为环境不同,即使在其他语言中使用MVC框架很普遍,也不代表在新环境里同样应该是如此。

AS3与传统语言的不同点:

  • AS3是单一语言环境,多层代码混在一起问题没那么严重。
  • AS3正常情况都是一次性编译全部代码,即使用了MVC框架还是需要一起编译。单独编译一个模块减少编译时间有别的办法,不需要依赖MVC。
  • AS3本身的事件和动态特性和一些框架的功能重复。
  • AS3目前的框架还很不成熟,没有提供比较醒目的功能。

结果是,至少,目前AS3的MVC框架比起传统语言并没有那么突出的作用,就算用了,也不会像Struts那样有质的变化。而且,至少在我看来,AS3的框架使用成本却不见得比Struts低。两者相减,结果就很麻烦了。

而且,AS3在不使用框架的时候有它自己的优势,使用框架会毁掉这些优点:

  • 有一个相对还可以的调试器,使用了框架会调试上产生麻烦,主要体现在单步调试步骤变多的问题上。
  • 阻碍使用IDE的功能。以Flex Builder为例,你可以通过Ctrl+单击(F4)跳转到指定方法的具体实现,通过搜索引用面板从方法的实现跳转到调用方法的位置。使用框架后,这些功能都会失效。
  • Flex framework相关功能会难以使用,诸如绑定。而且,Flex Builder支持拖拽式的将数据接口绑定到视图的功能,可以部分实现零代码编程,框架也会阻碍这个过程。

此外,企业应用和网站还好说,游戏还有另一种情况。游戏的结构并不同于原来的专门用于呈现数据的结构,可能也就是其中的用户界面(User Interface)部分和以前的结构比较类似,其他的诸如地图,诸如人物,无论怎么想也无法套用MVC框架,首先从效率上就说不过去。举个例子,一个项目有3个客户端人员在开发,一个在做地图,一个在做战斗,一个在做UI。前两者都和MVC没什么关系,结果只有一个人在用MVC框架开发界面……而且,开发前两者的时候,开发以及协作难度其实是比开发界面要高的,既然他们都搞定了,为什么开发界面的人还必须靠框架辅助才能解决这个问题?

这使得FLASH比起一般的情况,会更加不适合使用MVC框架。

不使用现有框架并非无法实现MVC

既然我在说框架不好用。那么不用框架,我们又该怎么做呢?

实际上,如果你只是想实现单纯的模型—视图—控制器(Model View Controller)分工职守,它只是一个架构模式而已。将模型和视图的代码分开,并提出控制器的代码,然后互相调用各自方法就算完事了。Model的全部引用放在固定的位置,View的引用使用静态属性储存或者用管理类管理,Command可以作为函数或者类直接初始化并执行,亦可以通过反射。这并不需要专门的工具类来辅助,附加成本也比较小,自然就可以适用于任何规模的项目。

当然,你可以实现一个简单的通信框架,提供必要的功能,如果你需要的话。这和使用一些专门的MVC框架需要的成本是完全不同的。

然而,我的意见则是——MVC是非常好的架构模式,不管什么样的项目都建议尝试使用,但是用框架的话,请务必谨慎。

关于最简的MVC,最近看到一个让人很囧的例子。不过这个例子对大家理解MVC是有帮助的。

这玩意的确……基本算是MVC,只差一点而已。MVC是架构模式,至少结构上要分开,即使不分文件至少要让能看得出来谁是谁(原文可没有红字),所以只需要把这些代码分成三个文件,那就可以称得上是最简的MVC了。

这是我在他的下面补充的代码。

结果是,View只关心与自己相关的Model和Command,Command只关心与自己相关的View和Model,Model谁都不关心,这和一般情况需要的解耦目标是一致的。虽然这样并不算完全解耦,但是至少在思路和逻辑分离上是做到了,仅仅是协作方面存在问题,比如无法实现自由的并行开发,而这个加入简单的反射也可以解决。

所以,单纯的MVC并不困难,没什么要不要放弃一说。还有就是上面只是极端例子,但就算是这种东西,比起完全不实现MVC,也至少实现了50%以上的内容。

是否使用框架应当理性对待

程序员都是理科生,应当用理科生的思考方式(当然我并没有让你们都去模仿Sheldon)。在使用MVC框架的过程中,不管是觉得好,还是差,都要考虑清楚问题的源头在哪。

觉得MVC框架不好用,降低效率,是否曾经有过平行对比的例子,你能否确认不用它效率就确实能提高?效率低有没有可能是框架之外的原因?

觉得MVC框架好用,提高效率,是否有平行对比的例子?你怎么就知道是使用了框架的功劳,而不是规范了代码结构,制定了新的协作流程,甚至是开发人员水平提高的功劳?怎么知道MVC框架并没有起了反效果?

使用了框架,看到了结果,然后根据结果的好话直接判定框架的好坏,这太武断了,作为一个理科生,我们绝对不能这样做。至于那类连比较都没有,而是以“我用了框架,项目依然完成了,没有因为用了框架而失败”这种理由来支持使用某个框架的人,我无言以对。

推荐MVC框架

我并没有完全反对使用MVC框架。这要看你的项目类型,规模,人数。满足条件的时候当然可以使用。尤其是在企业应用里,如果你有幸出现六个客户端的话,没MVC框架可能还真是会出问题。

pureMVC和Cairngorm是两个较早出现的框架,目前我不建议再使用它们。pureMVC的问题在于过于强调分离而缺乏实际功能,提供的便利很难抵消它本身的消耗,性价比较低。Cairngorm的问题则在于过于强调模型更新视图的流程,限制太多,灵活程度不够。

后出的几个框架就好多了,Mate使用了一个全局事件定义,配合FLEX写法非常简略。Swiz则是用控制反转+依赖注入,也就是Spring的做法,而且元标签注入的方式很有趣,感兴趣的可自行查阅资料。

我这里要说的是Robotlegs。这是一个和Swiz非常相似的框架,但也有一些自己的特点。首先它是基于pureMVC的,你依然可以像pureMVC这样来使用它,对于相信pureMVC的团队它是很容易接受的代替品。他让pureMVC也同样拥有了控制反转和依赖注入,包装了大部分功能,配置代码大大减少,而且不管用不用FLEX framework都可以很自然地使用它。

Robotlegs的教程可以看这里:

http://wenku.baidu.com/view/42a08b235901020207409c60.html

但我得提醒大家,虽然我觉得Robotlegs很便利以及有趣,但是并没有在项目里使用它,因为我的项目规模不大,而且是游戏。实际上,我甚至自己实现了一个依赖注入框架,可以很简单的加入到现在的项目中,成本几乎为零,却依然没有去用。使用一个东西要看是否需要去用,而不是可以用就用,更不是“因为用了没有遇到问题所以就用”。用一个东西必须有收益才可以,尤其是在明明看到有损失的时候。仅仅是用“看起来更正规”这类自我满足的理由来决定自己的行为,太愚蠢了。

当然,如果你需要它,那就应该毫不犹豫的使用。不要受到抱怨框架的人影响,他们大部分都有自己的问题,提出的理由也未必是正确的,你并不一定会赴他们后尘——前提是你真的需要它,而且,要将使用框架需要的条件全部补齐。

就算是使用MVC框架也不需要完全解耦

解耦是一个扩展性要求,但扩展性要求并不是越多越好的。

这是一个普遍的误区。诸如使用pureMVC的人,很多都纠结于完全的解耦,以至于用了Command,在Command中改变View的时候还是必须要发一遍Notification。

Command这种类,一般都是在相关的View,Model完成后才开始编写的。比如普通的StartupCommand,OpenWindowCommand,没有对象又如何编写?它在编写顺序上应该是,就算不是也是可以放在View和Model之后的。那么在协作关系上,他就可以直接访问所有相关类,不需要为了这种原因而解耦。

虽然pureMVC将消息全局化了,但是消息实际上是分局部和全局的。比如一个Proxy发生变化要求所有监听某个消息的View更新,那么当然应该发一个叫做I_AM_CHANGED的Notification,并由不同的View来监听这个消息并更新,这个就应该是全局消息。但有些消息就是局部的,是一对一的,比如一个叫做SEND_DATA_TO_WINDOW1的消息,按它的字面意思就应该是刷新WINDOW1,那么由它来触发的Command,就应该直接耦合 WINDOW1这个View来设置值,而不是再发个类似REFRESH_WINDOW1的消息,因为SEND_DATA_TO_WINDOW1的名字已经确定是针对这个View了,如果最终它却没有操作这个View,那才是有问题的吧?

解耦归解藕,但是对于已经有了意义上的联系的模块,结果却不耦合,在任何时候都是没有意义的。即使需求变化,逻辑变化了,使得SEND_DATA_TO_WINDOW1最终不是改变WINDOW1的数据,而是WINDOW2的数据,那么这个Command连带相关Notification的名字就必须修改,也就是说,意义上的紧密联系,在实际操作上和耦合了是一回事。既然已经是这样了,再做成不耦合,给自己制造麻烦的又有什么意义呢?

除了上面的情况,我们也要考虑,真的有必要将项目拆得那么细致么,有没有必要为了1%以下的可能性来解耦两个相关性很强的部分?比如一个叫做ShopPanel的View和一个叫做ShopModel的Model,到底在什么情况下,ShopPanel会不去调用ShopModel,而是别的东西?而且Panel上可能还有各种文字,使得自己意义上只能调用商店的数据。真是要调别的东西,应该重新制作一个新的View吧?而且别忘了pureMVC是可以多个Mediator套用一个View的。这种情况下,我们直接在Mediator中耦合ShopModel,有什么不可以的?当然,反过来,ShopModel被多个View调用的情况很普遍,所以我们不能让它来耦合ShopPanel。这些规则实际上是很明确的,是完全可以预知的。就算预知错误,也是很容易修正的。

解耦是手段,而不是目的。盲目的最求扩展性,只会让自己的程序变成一盘散沙。正是适量的耦合,程序才能拥有一个确定的形态,才不会让人感到茫然。

普遍误解

其实现在使用框架的人群里,真的能够发挥框架长处的确实比较少,尤其是在水平层次较低的Action Script开发人员之中。一方面,这污染了框架的名声,同时也是不建议使用框架的理由之一,因为人员水平限制也是实际项目中不可回避的现实问题。

如果你决定使用MVC框架,就必须提高自己的认识。我拣几个最常见的问题来说吧。

  • 并不是用了消息通信就算用了MVC
  • 消息通信只是一个手段,只使用框架的通信功能在View之间发送消息的话,而将其他功能全部抛弃的话,直接使用事件更好,那还套一个MVC框架就是在没事找事了。

    MVC关键还是在于代码逻辑的分配,通信只是个附赠品而已。要了附赠品而扔了原来的商品——咱们买的不是小浣熊干脆面,对吧。

    但是如果你的目的就是赠品,其实也没什么。比如你就是想用的这个通信框架来发发消息,就不打算用它的MVC,又或者MVC部分是自己实现的。那么我还是建议你把消息部分干脆也自己实现了,别人的始终没有自己的好。

  • 既然用了MVC框架,就不要图省事
  • 要清楚,松散耦合不仅仅是一个形式,目的在于减少模块间的联系。因此,如果你一方面在解除两个模块之间的耦合,一方面自己又没头没脑的将其他模块的内容耦合进来,就会使得你的行为变得没有意义。

    现在一些人一方面在硬套框架,一方面又图省事而随意引入其他类,就属于这样的行为。那些类是可以引入,但你这样做,框架本身的意义就没有了。要不你就不用框架,要不就别这样干,这里只能二选一。

  • 是Mediator知道View的一切,View完全不知道Mediator,而不是相反
  • 对于使用pureMVC的同僚们,我真是不明白你们到底是怎么把这个反过来理解成“View知道Mediator的一切,而Mediator完全不知道View”的,因为官方实例上写的很明白。估计是把mediator当成通信专用的模块类了吧。但是如果你放弃了mediator分离View代码的特性,只是用来通信的话,至少要保留原来的通信功能,就是让Mediator依然可以直接访问View。否则既然Mediator是用来通信的,它却不能操作View,结果还得设法和View再通信一次……

    pureMVC要求“View完全不知道Mediator”是为了能够在不修改View的情况下更换Mediator,但这种需求并不多(多的是在Mediator不变的情况更换View,这个需要用接口或者条件判断解决),所以可以放宽点让他们互相引用,这样两者通信都能畅通。

    pureMVC实现“View完全不知道Mediator”的方法是用Mediator直接去监听View的某个组件的鼠标事件。这只需要监听一次,也不需要传递消息。Mediator存在的期间,按pureMVC的标准View应该是没有任何监听逻辑的。

扩展阅读

http://baike.baidu.com/view/31.htm

http://www.360doc.com/content/09/0804/08/163747_4655702.shtml

http://hi.baidu.com/5%B1%CF%D2%B5%D2%D4%BA%F3/blog/item/2a018366a08e54cde6113af7.html

http://hi.baidu.com/lwcandwo/blog/item/8fb4b3036d01eb8ad43f7cf4.html

http://developer.51cto.com/art/200904/120649.htm

评价本文

专业度
风格

您好,朋友!

您需要 注册一个InfoQ账号 或者 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

告诉我们您的想法

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

某些观点是否需要进一步探讨! by She Walter

作者结合自己的经验和理解做出了一些结论,难能可贵,但是对于文章的第一部分 "FLASH与传统环境的不同点" 中的某些观点我还是觉得是否需要进一步探讨。在此没有给作者挑刺的意思,只是我觉得某些观点可能会误导一些人从而导致副作用。

“AS3是单一语言环境,多层代码混在一起问题没那么严重。"
我认为分层还是有必要的,分层其实是为了解耦和重用业务逻辑,这个在稍微大一点的项目中是必须做的,而不应该妥协或者凑合而不做。解耦从表面看起来会使事情变的臃肿,但它能更好的应对需求变化。

”AS3正常情况都是一次性编译全部代码,即使用了MVC框架还是需要一起编译。“
这个观点就有些错误了,如果做好了分层和模块化,其实每个模块都可以作为库工程来开发,可以单独编译和部署的。另外MVC框架和一次编译之间好像没什么关系吧。

”AS3本身的事件和动态特性和一些框架的功能重复。“
框架包含了很多东西而不止这些

做地图和做战斗其实都和数据有关,视图只是数据的重现,地图上的任何物体其实只是把地图数据呈现了出来,战斗开始的战斗数据和战斗过程中的负伤等其实还是由底层数据驱动的。

Re: 某些观点是否需要进一步探讨! by 唐 翎

相对的啦。。你要看比较。。。
1.要比较。。你懂的。。
2.要比较。。所有都这样和有的这样是不一样的。。
3.我只是说重复啊。。要知道有些语言连反射用起来都很恶心的。。而且没事件。。

还有就是。。这篇文章,都被删减的都不成样子了。。基本表达不出原来的意思:
看这个吧
uh.9ria.com/space.php?uid=12147&do=blog&...

不务实 by jianf zhu

手头的大型项目,引入过几个mvc框架,到后期全删了,专注于管理控件、事件。
另外想做一件事,抽死那些只写文章不写代码的货色。

Re: 某些观点是否需要进一步探讨! by 魏 俊杰

赞同,解耦作为一种手段,在大型的开发项目里是必须的,这点是语言无关的。如果你认为你的项目足够小,可以不考虑框架,但是说AS3不需要框架,那只是因为你一直做小而且不需要很多人协作的项目,Flex framework的出现,本身就是为了Enterprise级别的企业开发使用的,flash小游戏可以不需要,大型游戏、企业级项目不可能不需要,但是你可以选择,使用现成的成熟的框架,或者自己的实现

忘记MVC,记住务实和模块化等设计原则即可 by 陆 仕桑

不错,很务实的文章。忘记MVC,记住务实和模块化等设计原则即可。我们用框架不是为了MVC,而是方便并行开发等其它目的。MVC可以泛化为观察者模式等。社交游戏里,服务端本身就是一个Model了,而Flash组件里可能也有数据模型,所以部分程序实际上可能会是MMCMCV等。所以忘掉死板分层的MVC吧,根据需要分模块分层。

这类主题每个人都能从自己的立场出发提出许多意见 by 许 晓磊

首先简单的需求用框架总是不怎么实用的。但是,现在的问题是多数人只知框架,但几乎不用(因为多数项目一开始的需求总是简单的)。导致碰上复杂点的项目了,往往无法入手,或者经验缺乏导致用的一团糟。这就形成了个矛盾。所以我建议反而是在条件允许的情况该用则用,每次代码都想想当前获得的好处,和碰到那些“困难”应该怎么解决,在下一次开工前就从设计上避免点。另外就是尽量别对任何事,任何做法下什么定论,不要将在一次项目中使用框架的方法就完全当成了定式。另外就是好的框架无所谓引入任何其他类,Robotlegs作者在这方面还是有相当骄傲的自豪说明的。引起问题了更多还是应该考虑这框架值得你改进的地方在哪,或者那个所谓的“其他类”需要改进了。

关于自己的理解· by Terry Mao

我觉得框架之所以出现的原因,是为了让你更好的去理解语言!而不是去批判它的好坏!就像设计模式一样,虽然你可能用不好。
但每一个项目里面,其实处处都是设计模式,我记得一个朋友曾这样说过,所谓写游戏框架的最高境界,就是”无招胜有招“。
想想,我觉得也是,脱离框架之外,争脱这些理念,想怎么写就怎么写!

我举一个例:
var txt:TextField = new TextField();//View
var myName:String = "terry";//Model
var txt.text = myName;//Controller

MVC,模型,视图,控制器,我想这是我理解的最简单的MVC。
还有就是,千万不要把个人的见解强加成许多人,,这样会误导许多人··

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

7 讨论

登陆InfoQ,与你最关心的话题互动。


找回密码....

Follow

关注你最喜爱的话题和作者

快速浏览网站内你所感兴趣话题的精选内容。

Like

内容自由定制

选择想要阅读的主题和喜爱的作者定制自己的新闻源。

Notifications

获取更新

设置通知机制以获取内容更新对您而言是否重要

BT