搜尋部落格文章

2012年9月20日 星期四

[轉] Flash 務實主義 - Flash中的 MVC

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是有幫助的。
【Flash <wbr>务实主义】Flash中的 <wbr>MVC
這玩意的確……基本算是MVC,只差一點而已。 MVC是架構模式,至少結構上要分開,即使不分文件至少要讓能看得出來誰是誰(原文可沒有紅字),所以只需要把這些代碼分成三個文件,那就可以稱得上是最簡的MVC了。
這是我在他的下面補充的代碼。
【Flash <wbr>务实主义】Flash中的 <wbr>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的教程可以看這裡:
但我得提醒大家,雖然我覺得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應該是沒有任何監聽邏輯的。

沒有留言:

張貼留言