两个人关于Flex MVC的争执

来源:互联网 发布:北京美工集团玉印 编辑:程序博客网 时间:2024/04/28 22:51

 

正方出场:

 

许多人的flex程序就像写php一样,先是一通导入(import),然后定义变量(var),然后是一堆function,再下来就是一堆 html(在flex里是MXML,差不多).构建页面根本不是这样的,更不可以用这种方法写整个程序了!但很多人就是这样做的,太乱了!

我想让大家知道下面的"最差写法"就是我最近碰到的一段代码,我只是略微做了一点简化,原来的更乱.
你可能要反驳我说用MVC太耗时间,要写更多代码,代码也不易读,或者其他的理由.但其实MVC更容易,简洁,清爽,面向对象,是"最佳写法".

如果你的代码看起来也是这样,说明你也做错了!
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
  3. xmlns:s="library://ns.adobe.com/flex/spark"
  4. xmlns:mx="library://ns.adobe.com/flex/halo"
  5. creationComplete="application1_creationCompleteHandler(event)">
  6. <fx:Script>
  7. <![CDATA[
  8. import mx.controls.Alert;
  9. import mx.rpc.events.FaultEvent;
  10. import mx.rpc.events.ResultEvent;
  11. import mx.collections.ArrayCollection;
  12. import mx.events.FlexEvent;
  13. [Bindable]
  14. private var searchList:ArrayCollection = new ArrayCollection();
  15. protected function application1_creationCompleteHandler(event:FlexEvent):void
  16. {
  17. runSearch();
  18. }
  19. private function runSearch(event:MouseEvent=null):void
  20. {
  21. httpService.send({searchTerm:searchInput.text});
  22. }
  23. protected function httpService_resultHandler(event:ResultEvent):void
  24. {
  25. searchList = new ArrayCollection( event.result as Array);
  26. }
  27. protected function httpService_faultHandler(event:FaultEvent):void
  28. {
  29. searchList = new ArrayCollection();
  30. Alert.show("fault");
  31. }
  32. ]]>
  33. </fx:Script>
  34. <fx:Declarations>
  35. <mx:HTTPService id="httpService"
  36. result="httpService_resultHandler(event)"
  37. fault="httpService_faultHandler(event)"
  38. resultFormat="e4x"
  39. url="http://www.someUrl.com/yourMethod"/>
  40. </fx:Declarations>
  41. <s:layout>
  42. <s:VerticalLayout gap="20" paddingBottom="20" paddingLeft="20" paddingRight="20" paddingTop="20"/>
  43. </s:layout>
  44. <s:HGroup width="100%">
  45. <mx:Label text="Search Term"/>
  46. <s:TextInput width="100%" id="searchInput"/>
  47. <s:Button label="Do It!" click="runSearch(event)"/>
  48. </s:HGroup>
  49. <s:List width="100%" height="100%"/>
  50. </s:Application>
复制代码
这是一个非常简单的例子,如果你的程序只是需要这些功能,那这样写也未尝不可.不过如果你的程序要比这个复杂,如果这样写下去,你的程序很快就会"四分五裂",惨不忍睹.
初识MVC
MVC其实是个简单而实用的概念,主要用于简化程序的开发.
主要概念就是把你应用分为数据层(Model),表现层(view)和控制层(conreoller).

了解数据层(Model)
Model就是你整个应用的数据中心.在代码里,你一定要在model中使用getter和setter方法!为啥?因为只有这样,model才能在数据发生改变的时候抛出相应的事件.

最佳实践:model的类都继承自Eventispatcher以便抛出事件,所有的数据要有setter 方法.从下面的代码里你可以明白我说的意思.你可以使用自定义事件,但下面的代码里我只使用了flash最基本的Event.在抛出事件之前,要确认数据是否真的发生了改变.数字和字符串比较容易,复杂对象可能费点劲.

用Model重构代码
下面例子中的代码极其简单,只有一个属性,但你也能看出他是如何简化和分离整个程序的.并且可以在多个控制器里重用.
  1. public class SampleModel extends EventDispatcher
  2. {
  3. public static const SEARCH_LIST_CHANGED:String = "searchListChanged";
  4. public function SampleModel(target:IEventDispatcher=null)
  5. {
  6. super(target);
  7. }
  8. private var _searchList:Array;
  9. public function get searchList():Array
  10. {
  11. return _searchList;
  12. }
  13. public function set searchList(v:Array):void
  14. {
  15. _searchList = v;
  16. dispatchEvent(new Event(SEARCH_LIST_CHANGED));
  17. }
  18. }
  19. }
复制代码
了解控制器(conroller)
说白了控制器的功能就是从model中读写数据,响应view的的操作.我之前也谈到过这个,那时是说要把类分解,使用接口,子类,用unit test单独来测试.基本上,你使用的这些技巧越多,你的程序会越健壮.

最佳实践:controller包含了view所需的一切方法,并且这些方法都是共有的.其他的方法和属性都是私有的.所有的时间处理函数都应该在controller中,并由controller来操作数据.再重复一遍,我用的事件都是flash自带的 Event,不过你可以使用自定义时间,尽管有些多余.特别要注意的是,model对view或其他组件来说是不可见的,如果你非要使用一些model中没有的特别的数据,可以在controller中价格getter方法(不过不提倡这样做).

用controller重构代码
构建controller非常简单,先建一个空的类.
  1. package com.unitedmindset.controllers
  2. {
  3. public class SampleController
  4. {
  5. public function SampleController()
  6. {
  7. }
  8. }
  9. }
复制代码
现在只需要把view需要用的的方法放进去,注意controller有一个model的实例并且监听model的更改以便在需要是更新view.
  1. package com.unitedmindset.controllers
  2. {
  3. import com.unitedmindset.models.SampleModel;
  4. import flash.events.Event;
  5. import flash.events.MouseEvent;
  6. import mx.collections.ArrayCollection;
  7. import mx.controls.Alert;
  8. import mx.events.FlexEvent;
  9. import mx.rpc.events.FaultEvent;
  10. import mx.rpc.events.ResultEvent;
  11. import mx.rpc.http.HTTPService;
  12. public class SampleController
  13. {
  14. [Bindable]
  15. public var view:UMBlogMVC;
  16. private var _model:SampleModel;
  17. public function SampleController()
  18. {
  19. _model = new SampleModel();
  20. _model.addEventListener(SampleModel.SEARCH_LIST_CHANGED,_onSearchListChanged);
  21. }
  22. public function application1_creationCompleteHandler(event:FlexEvent):void
  23. {
  24. runSearch();
  25. }
  26. private function _onSearchListChanged(event:Event):void
  27. {
  28. view.searchList.dataProvider = new ArrayCollection( _model.searchList );
  29. }
  30. public function runSearch(event:MouseEvent=null):void
  31. {
  32. var service:HTTPService = new HTTPService();
  33. service.addEventListener(ResultEvent.RESULT,_httpService_resultHandler);
  34. service.addEventListener(FaultEvent.FAULT,_httpService_faultHandler);
  35. service.resultFormat = "e4x";
  36. service.url = "http://www.someUrl.com/yourMethod";
  37. service.send({searchTerm:view.searchInput.text});
  38. }
  39. private function _httpService_resultHandler(event:ResultEvent):void
  40. {
  41. var service:HTTPService = event.target as HTTPService;
  42. service.removeEventListener(ResultEvent.RESULT,_httpService_resultHandler);
  43. service.removeEventListener(FaultEvent.FAULT,_httpService_faultHandler);
  44. _model.searchList = event.result as Array;
  45. }
  46. private function _httpService_faultHandler(event:FaultEvent):void
  47. {
  48. var service:HTTPService = event.target as HTTPService;
  49. service.removeEventListener(ResultEvent.RESULT,_httpService_resultHandler);
  50. service.removeEventListener(FaultEvent.FAULT,_httpService_faultHandler);
  51. _model.searchList = new Array();
  52. Alert.show("fault");
  53. }
  54. }
  55. }
复制代码
有人认为httpservice的那部分应该在model里面,这也是许多开发者争论的焦点.我认为应该再controller里面,因为httpservice包含的是从服务器获取数据的逻辑,而不是数据本身.
简洁的view
最后是重构显示的代码,并且创建一个controller的实例.
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
  3. xmlns:s="library://ns.adobe.com/flex/spark"
  4. xmlns:mx="library://ns.adobe.com/flex/halo"
  5. xmlns:controllers="com.unitedmindset.controllers.*"
  6. creationComplete="controller.application1_creationCompleteHandler(event)">
  7. <fx:Declarations>
  8. <controllers:SampleController id="controller" view="{this}"/>
  9. </fx:Declarations>
  10. <s:layout>
  11. <s:VerticalLayout gap="20" paddingBottom="20" paddingLeft="20" paddingRight="20" paddingTop="20"/>
  12. </s:layout>
  13. <s:HGroup width="100%">
  14. <mx:Label text="Search Term"/>
  15. <s:TextInput width="100%" id="searchInput"/>
  16. <s:Button label="Do It!" click="controller.runSearch(event)"/>
  17. </s:HGroup>
  18. <s:List width="100%" height="100%" id="searchList"/>
  19. </s:Application>
复制代码
仔细看看上面的代码,你会发现没有任何的逻辑在里面.你的view的目的只有一个:好看!由controller来接受view发出的事件并对数据进行对应操作.

小结
但愿这篇文章对你的代码重构起到一些帮助作用.这种编码风格的好处还在于你可以使用任何你喜欢的框架.如果你再写程序是疑惑:"这段代码应该放在哪?", 你只需想一想,这是属于界面,还是数据,还是商业逻辑.我相信你一定会发现界面与逻辑剥离的好处,并且你可以在不同的view中重用你的 controller.拥有了这样的能力,你的代码水平一定能更上层楼.

编码愉快!
 
反方出场:
我对这篇文章,意见很大。
不知道作者是谁。建议看这篇文并赞同他的观点的人好好看看我下面说的话。自然,有反对意见也欢迎讨论。我实在不希望大家就这样被误导了。

……
这篇做到的只是最低限度的MVC,关键在于,它完全没有做到任何程度的解耦。这个controller直接引用了View和Model(一般的MVC架构都只会引用其中一方),因此,他只能用于这个View和这个Model的通信,且必须保证两者都存在才可以编译成功,只要有一方变化这个controller都必须重写。在我看来,这种程度的MVC,相当于什么都没做。
他的C必须依附V(因为他引用了V,只能用于它),而M也必须依附C(因为是C内部创建的,其他C根本访问不到)。所以,实际上M也在依附V,这就叫什么都没做。

他只做到了形式上的分离,这分为两点:
1. controller得以拆解成不同的文件,这样一个View可以对应多个controller,不同功能的controller会被分开,代码会比较清晰。这还是有一点作用的。可是,因为他的M是C内部创建的,所以其他C是根本访问不到M的,我看这家伙就是打算把所有逻辑都放在同一个C里。是这样的话,这点优势也都没了。
2.MVC形式上的分离。形式上的分离意义根本就不大。尤其是在FLEX中,View是用MXML标签表示的,Controller是用AS代码表示的,而Module也就是几个变量集合。这样的东西即使放在一个文件里依然很容易分辨。现在有SVN,代码合并再简单不过,只要代码之间不相互交叠,一个文件多个文件没什么本质区别。

MVC最关键的地方是解耦啊老大。不要老满足于一些表面现象,代码不是用来看的,关键看实效。费了半天劲,结果和原来说的“难看”的代码一点变化都没有。到底在干嘛呢。


我的意见:
要不就老老实实地按照那几个标准MVC框架来走。他们肯定是对的。
要不,就不要用。
这种就是似懂非懂,以为自己MVC了,代码清晰好看就陶醉其中,不过是幻觉罢了,还不如不要。

"有人认为httpservice的那部分应该在model里面,这也是许多开发者争论的焦点.我认为应该再controller里面,因为httpservice包含的是从服务器获取数据的逻辑,而不是数据本身."
但这家伙的这句话我是同意的。但基本上也就这句话。


老外说的话也不一定是对的。我还是建议翻译的时候把作者介绍稍微说下,这样能根据他的经历来判断可信程度。半桶水很容易误导人的。

原创粉丝点击