【讨论】从吉日的一段话说起+寻找WinForm架构的最佳实践
這兩天園子里最火的莫過于吉日的白話反射,導致包子的批判,然后引來了老趙的兩篇文章,然后又有若干人等一堆反射技術文章出世??芍^百花齊放,百家爭鳴啊。喜歡這種氛圍,呵呵。
今天我不談反射,但和反射有關
不談吉日,但話題是從這里開始
吉日的《白話反射》里說到:
"我們在開發大型軟件項目時經常會遇到,系統很龐大了有幾百M的代碼了,主程序啟動時,總不能把這些都引用了吧?全部加載在內存里?那程序的啟動速度,不知道會不會慢如老牛推車了?這時候也會用一些反射技術等,用到哪個窗體,就動態加載哪個那個窗體,總感覺比較清爽一些。"
表面一看,確實。主模塊引用這么多模塊,啟動的時候那不是很慢,那必須用反射來“制造”一個延遲加載的機制。
實際上,CLR的加載過程是什么樣子的呢?(不說的那么詳細)
很多書里都介紹了,JIT的編譯單元是方法。當執行一個方法之前,會先JIT(即時編譯)這個方法,然后發現,這個方法里引用了在別的程序集里定義的類型,那么CLR Loader就會加載這個程序集(更詳細的過程可以參見《.Net本質論》或我之前的兩篇文章CLR Loader和Assembly Loader)。那這么看來,吉日說的就是錯誤的,幾百M的代碼不錯,難道這幾百M的代碼在啟動的時候就會全部執行么?如果不執行也就不會JIT,不會JIT也就不會加載這些模塊。但是有一個例外:你這幾百M代碼,全部放在一個模塊里(我覺得吉日肯定不會那么干)。
根據上面的討論,所以吉日說的這種場景不需要用反射,不會影響主程序的啟動速度,只需要合理的劃分子功能模塊就可以了,不要弄得鐵板一塊。
真的是這樣的么?如果光說教我會很快的陷入太虛之中,所以我準備了一個小實驗:
這個一個典型的WinForm應用的解決方案。Main是主啟動項目,而FunctionModule1-FunctionModule4是具體的子功能模塊(實際的項目中,肯定不止四個)。
我們看到這里Main項目中引用了四個子功能模塊。然后下面是主界面:
Function主菜單下有四個子菜單,每個子菜單調用一個子功能模塊,菜單的事件代碼如下:
1: private void function1ToolStripMenuItem_Click(object sender, EventArgs e) 2: { 3: Function1 function = new Function1(); 4: MessageBox.Show(function.ToString()); 5: } 6: private void function2ToolStripMenuItem_Click(object sender, EventArgs e) 7: { 8: Function2 function = new Function2(); 9: MessageBox.Show(function.ToString()); 10: } 11: private void function3ToolStripMenuItem_Click(object sender, EventArgs e) 12: { 13: Function3 function = new Function3(); 14: MessageBox.Show(function.ToString()); 15: } 16: private void function4ToolStripMenuItem_Click(object sender, EventArgs e) 17: { 18: Function4 function = new Function4(); 19: MessageBox.Show(function.ToString()); 20: }現在問:這里的Main項目啟動時,會主動加載這四個子功能模塊么?
我們使用Visual Studio調試時輸出窗口的功能看看:
窗口中,前部分輸出的是加載的框架的模塊,最后一個是加載Main程序集。但是FunctionModule1-FunctionModule4呢?我仔仔細細尋找了好幾遍沒發現。那么說明,Main項目啟動時,不會主動加載子功能模塊的,所以吉日的說法是錯誤的,除非他把所有子功能模塊全部寫在主功能模塊中。那什么時候加載這些子功能模塊?來,我們嘗試點一下菜單:
子菜單的功能彈出來了,從輸出窗口里也發現這個時候加載了FunctionModule1.dll。
以下是對WinForm應用架構設計的討論
-----------------------------------------------------------------------------------------------------------
下面,我們來討論一下WinForm應用的架構問題。
我不知道園子里有多少人在做.Net WinForm應用,只是看到園子里大部分人都是在搞Web或WPF??鄲赖奈疫€在搞WinForm,不過也其樂無窮。
上面的實例實際上也給出了大部分WinFrom項目的一個雛形,很多的菜單,每個菜單對應一個功能,打開一個功能的時候要么是Mdi的模式,彈出一個窗口,要么就是一個TabPage。大的工程肯定是一個人完成不了的,需要多個人來做這個事情。但是又需要統一規劃。
所以必定是這樣的:由架構師來規劃好底層支撐的框架,比如這里的Shell,還有一些引擎級別的服務(比如LanguageService“多語言”,PropertyService“持久化軟件里某些設置”等)。這個框架必須穩定,項目組其他人員,每個人負責一個或多個功能,每個人只需要關注自己的業務就可以了,把功能做好,然后調用框架里提供的一些服務,把自己給掛接到主框架中。
那么如果按照上面示例的這種設計,我們的程序主框架就必須引用所有的子功能模塊,項目開始的時候,可能只有一兩個模塊,隨著項目的前進,引用也在不斷的增加,而主界面上的菜單也在不斷的增加,這可以說主界面是穩定的么?
那我們必須尋找一種機制。子功能對自己負責,子功能負責自己將自己注冊到系統中去。這個時候我們可能采用這樣的方式:
一個Modules文件夾,在這個文件夾下又放著很多子文件夾,每個子文件夾里放著一個功能,當系統啟動的時候,由框架搜尋Modules子文件夾,在里面查找一個后綴名為addin(或者其他方式)的xml文件,文件里面的內容可能如下:
<MenuItem Site=”File” Text=”Edit” Icon=”Edit.png” CommandType=”MultiLibrariesApp.EditCommand” />
主模塊讀取這個之后就會生成一個菜單項,當點擊這個菜單項的時候,根據CommandType,利用反射實例化一個EditCommand類型,所有的Command可能都實現一個ICommand接口,而這個接口里有一個Run方法:
1: public interface ICommand 2: { 3: void Run(); 4: } 5:? 6: public class EditCommand : ICommand 7: { 8: public void Run() 9: { 10: //code here... 11: } 12: } 13: public class MenuItem 14: { 15: public string Text{get;set;} 16: //主框架的代碼里 17: ICommand command = null; 18: private void MenuItem_Click(object sender, EventArgs e) 19: { 20: //延遲加載 21: if(command == null) 22: command = //通過反射實例化具體的Command類型,這里就是EditCommand 23: command.Run(); 24: } 25: public ToolStripMenuItem CreateMenu() 26: { 27: ToolStripMenuItem menuItem = new ToolStripMenuItem(); 28: menuItem.Text = this.Text; 29: menuItem.Click += MenuItem_Click; 30: } 31: }這樣,就能保證主框架是穩定的了,子功能負責自己的菜單管理,只需要寫一個文件就可以了。
當然,實際的框架實現過程中,肯定會碰到各種各樣的問題,這只是一個思路。對于WinForm程序而言,你還可以參考一下開源項目:
SharpDevelop 一個開源的IDE
MonoDevelop 從SharpDevelop發展而來,但是現在大變樣了
Mono.AddIn由MonoDevelop的插件機制發展出來的一個小插件系統
Composite Application Block 微軟模式&實踐小組的
還有很多其他,我就沒有研究過了。
以上只是我的一個思路,由于WinForm開發的看到不多,我在網上搜WinForm Best Practice也沒找到多少資料,所以希望能夠在這方面有所討論。也許能碰撞出一些火花出來。
后話
剛才出去了一趟,在路上又思考了一些問題。
通過上面對模塊加載和WinForm架構設計的討論,總結一下:吉日文中說的這種應用反射的情況并不是因為基于啟動效率的問題,而是設計的考量。這個地方跟性能一點關系都沒有(在這里對我在老趙博客里開始錯誤的評論表示道歉),用不用反射啟動效率都是這樣。
再看看吉日另外一個應用反射的場景:兩個類循環引用。我不知道為什么有這樣一個設計,如果是遺留代碼那你首先應該考慮重構一下,如果實在不能重構,就必須這樣,那只有用反射了。所以這個問題也是設計上的問題,跟反射也沒啥關系。
還有吉日說的,配置多數據庫的場景。老趙說了,這里推崇ORM。即使你不用ORM,我也覺得這是沒有必要的。我不知道有多少情況一個正在運行的系統要突然更換不同類型的數據庫?即使有這種情況,那么這也屬于重大變更,對于這種變更,你完全可以修改代碼。還有,針對這種情況,微軟已經給出了Best Practice:提供者模式。雖然提供者模式最終還是反射。
總結
以上是生活随笔為你收集整理的【讨论】从吉日的一段话说起+寻找WinForm架构的最佳实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PHP学习——定界符格式引起的错误
- 下一篇: 配置WINDOWS群集