Cocoa教學(xué):對比Windows OOP與Cocoa MVC
本文我們將介紹Windows OOP與Cocoa MVC之間的對比。在Windows里,尤其是C# .NET,你可以通過添加一個Form對象來創(chuàng)建新的窗口,而且還可以簡單地在設(shè)計(jì)窗口中添加一些不同的“控件”。這些操作是非常容易的。不過一旦窗口創(chuàng)建好之后,你需要在主窗口的代碼中建立剛才新建的窗口的實(shí)例,然后提供公共變量在兩個窗口之間設(shè)置或者獲取數(shù)據(jù)。窗口類是由Windows Form模板寫好直接交給你使用的,這當(dāng)然可以使代碼看起來清晰干凈,但是它打破了MVC的慣例,所以大部分Windows的程序員會花費(fèi)不少時間提升他們思考的方式也就不足為奇了。
我用VS2008做了一個demo程序,截圖大概是這樣的:
看一下主窗口的代碼:
- public partial class Form1 : Form
- {
- Panel p;
- ?
- public Form1()
- {
- InitializeComponent();
- p = new Panel();
- p.Show();
- }
- ?
- private void btnChangeText_Click(object sender, EventArgs e)
- {
- p.OutputText = this.tbInputText.Text;
- }
- }
注意看我聲明了一個Panel的對象,這是我們需要在上面設(shè)置文字的第二個窗口(view)。下面是Panel類的代碼:
- public partial class Panel : Form
- {
- public Panel()
- {
- InitializeComponent();
- }
- ?
- public string OutputText
- {
- set
- {
- this.tbOutputText.Text = value;
- }
- }
- }
好,代碼很容易理解,但是從這里就可以看出我的觀點(diǎn):MVC模型已經(jīng)被破壞了。雖然這個例子里面并沒有任何編程邏輯,不過很清楚的是這樣的設(shè)計(jì)導(dǎo)致你只需要把代碼放到按鈕的事件處理里面就可以了,而不是去將邏輯抽象到controller對象中。
你也許會問,我在C#中如何做MVC呢?呃……這是一個關(guān)于Objective-C、Cocoa編程的網(wǎng)站,對于讀者們來說這是個作業(yè)了……不過坦白講,我可不知道。我知道那是一件可能的事情,不過C#語言的內(nèi)部就沒有把開發(fā)者向這個方向去引導(dǎo)。我也看過一些講這方面事情的文章,不過那些也都是基本上困難到?jīng)]法實(shí)踐的。有幾篇號稱是MVC不過根本不算,所以如果你真想在C#上面實(shí)現(xiàn)MVC,自己想辦法弄吧……^o^ 我想說的其實(shí)就是要想在C#上面實(shí)現(xiàn)MVC,那算你狠。
Objective-C/Cocoa的方式
在Objective-C里,你必須明確地創(chuàng)建一個controller用來處理model和view之間的變化。其實(shí)MVC應(yīng)該被稱做MCV,因?yàn)閏ontroller是在model和view之間的一個協(xié)調(diào)員。如果你的model發(fā)生了改變,你的controller會通知view。如果用戶在view中做出了某種改變,controller就會通知model。所以我建議初學(xué)者可以叫它MCV,會更加形象一點(diǎn)。有點(diǎn)跑題了。
在Objective-C/Cocoa的世界里,我們建立的controller通常是指應(yīng)用程序(Application)的托管(Delegate),或者可以簡單稱做app delegate。很多Windows程序員都會在這里迷惑不解的事情是,我們通常學(xué)習(xí)到的面向?qū)ο箝_發(fā)就是你應(yīng)該去做的事情,而并不會關(guān)注為什么你會去做,或者你為什么不去做。我并不是說你別用OOP的思想,而正相反我建議去用。問題是如果把一切都抽象化,那就有點(diǎn)太傻叉了……我們應(yīng)該有很好的理由去寫這些代碼,而不要用諸如“我從大學(xué)里面學(xué)的……”或是“我一直就這么干……”這種理由。
當(dāng)你在Objective-C里面建立一個app delegate的時候,這個delegate可以做為你所有model和view的controller,或者你也可以為不同的model和view分別創(chuàng)建controller。想怎么干就怎么干吧。不過有一個比較重要的事情是要記住的,如果你把所有的代碼都扔到同一個app delegate類里頭,那你就有了一個超大的app delegate文件,很難看清楚。
一些例子程序
為了幫助那個提出問題的朋友,還有另外一些想從Windows開發(fā)轉(zhuǎn)變到Cocoa開發(fā)的朋友們,我也寫了點(diǎn)簡單例子來幫助把這個問題變簡單。如果你想讓兩個View,或者兩個窗口可以互相之間通信,只要在他們之間傳遞消息就可以了。雖然把你的view們封裝到它們自己的類中并不是壞事,不過通常來說真的沒必要。在任何一種語言和任何一種平臺上,都有實(shí)現(xiàn)這個功能的方法,所以就別管我沒提到的事情了,我也沒說這是唯一的方法不是……我說的方法是簡單直接的方法,可以幫你更快的理解。
我同樣建了一個簡單的demo程序來演示上面說的,這里是截圖:
你可以在這里下載例子。
這里是我提到的代碼,只需要在app delegate的頭文件中將你的view聲明為outlet:
- @interface AppDelegate : NSObject {
- IBOutlet NSTextField *inputText;
- IBOutlet NSTextField *outputText;
- }
然后聲明這樣一個方法,在按下按鈕之后會執(zhí)行:
- - (IBAction)updateText:(id)sender;
最好要做的事情就是在IB里面把a(bǔ)ction和outlet連到AppDelegate對象上,任務(wù)完成。就這么簡單。
為什么Windows的方法爛,Mac的方法贊
好吧,這個小標(biāo)題僅僅是個玩笑,Windows專家們千萬表噴俺。不過我的確認(rèn)為C#設(shè)計(jì)用戶界面的方式會把人們的代碼搞得賊亂,而且明顯不是MVC模式。
當(dāng)然,又來了,怎么做還是看你自己,不過.NET的用戶界面設(shè)計(jì)工具非常鼓勵用戶去破壞MVC模式。當(dāng)你在設(shè)計(jì)器里面把一個按鈕拽到窗口里,然后雙擊那個按鈕的時候,它就自動地給你指到按鈕點(diǎn)擊事件代碼里,大部分程序員就自然而然地在那里寫代碼了。當(dāng)你在設(shè)計(jì)過程的時候,倒也沒什么,不過它根本沒有做什么來支持你將邏輯和表現(xiàn)分開。
在Objective-C里,想破壞MVC設(shè)計(jì)模式倒是很困難的事情?;旧夏愣急仨氉裱@個模式。甚至當(dāng)你使用Interface Builder在app delegate和action及outlet中間建立連接的時候,都會帶有一個可視的MVC表現(xiàn)。要連接app delegate類(你的controller)到outlet的時候(比如輸入框),你按住ctrl之后從AppDelegate拖拽一根線放到outlet上。當(dāng)你想告訴AppDelegate執(zhí)行一些動作,你要從觸發(fā)動作的對象中拽到AppDelegate對象上。反過來是不行的。養(yǎng)成這樣的習(xí)慣其實(shí)很好,只不過Windows的鐵桿程序員會相當(dāng)不習(xí)慣。
結(jié)論
從Windows程序員轉(zhuǎn)到Mac程序員是有一點(diǎn)挑戰(zhàn)的,不過你越早拋掉從前的開發(fā)的概念,就越容易接受Mac開發(fā)的概念。想想令狐沖吧……Mac的開發(fā)的確是不太一樣的。要習(xí)慣這種開發(fā)思路,而不要試圖沿用從前的習(xí)慣來進(jìn)行Mac開發(fā)。
承認(rèn)這一點(diǎn)吧兄弟們,工程師們都是很傲慢的,而且當(dāng)學(xué)習(xí)一門新的語言、技術(shù)或是平臺的時候,通常會認(rèn)為他們已經(jīng)很清楚了。最后這句的英文真的很棒,我不知道怎么翻譯才能完美的表達(dá)這句話,和大家共勉:Goto is not inherently evil, you know? Until next time.