本來是原創文章,發表與論壇,增加修改了一些東西。
我說MFC設計得不好, MFC最重要的一個class應該是CWnd,對吧?
一個CWnd多少個位元組? 我記得好像是64(60?)個位元組。按照你的了解,可能4個位元組的HWND以及最多虛拟函數需要使用的4個位元組加起來8個位元組就夠了(ATL的CWindow隻有4個位元組,虛函數都沒有,消息處理通過多層繼承實作正交分解)。但是CWnd為什麼有64個位元組? 因為CWnd裡面什麼都有,甚至包括為了COM支援的一個m_xDispatch對象,IUnkonwn指針,派生類對話框需要的傳回值等等。總是隻要可能需要的全部都在此,完全違背了一個良好的OO設計中高聚集,低偶合的原則,更勿論什麼正交分解。我大部分時候使用CWnd的時候不需要和Dispatch什麼的打交道,對吧?這個設計好像叫God class,什 麼都有,好像一個垃圾桶,一切盡在其中。
其他的我想你也可以找到,再舉個例子,MFC的CSocket是不能跨線程使用的,因為他内部使用了一個CWnd對象。OK,這個是封裝細節,我無須考慮,但是由于CWnd不能跨線程使用,造成我的CSocket不能跨線程,這就變成一個需要開發者了解的實作細節了。你不看源代碼你根本不知道什麼原因。可以了解這個是基于Win3.0不支援多線程的遺留代碼,但是現在一直沒有改進,無論如何作為一個在網絡環境中使用的類,竟然不能跨線程使用,不可能是一個好的設計。
又例如雖然有CSocket類,但是沒有一個對應的CSocketAddress類似的類,OK,你可以使用CSocket::open("localhost", 80)進行網絡連接配接而無須處理sockaddr結構,但是你如果想将localhost, 80轉化成一個sockaddr結構就沒門,它隻能在CSocket中使用,而且必須就是連接配接open的這個函數,除了最原始的拷貝張貼,你實在無法重用什麼東西。提取不夠,一個類權限過多。
還有好像我記得它的SplitWindow,一個分割視窗的實作,非要綁定到Doc-View上面,結果我的一個簡單的對話框程式就無法使用。不是人人都需要Doc-View的。對吧?但是需要split windows的地方很多,結果MFC這個設計非将我綁定到Doc-View上面。跟Delphi中的anchor,dock比起來落後一個世紀。
還有很多,例如CString,CString的reference count是線程安全的,但是代價是使用Interlock等函數,雖然代價很少,但是我如果總是在單線程中使用,這個代價我都不應該付出,但是無論如何,這個代碼你使用CString都得有。你有什麼辦法?
而且CString使用宏來确定是否wchar還是char也很不合理。例如我的程式編譯成unicode版本,但是需要使用ansi string,那麼CString好像也無法使用了。
MFC以現在的C++觀點來看,對C++語言的使用停留在Script級别,對OO的封裝了解停留在Visual Basic的級别。我知道有WTL之父,有STL之父的interview,當然C++之父的就不用說了,好像還沒有聽說MFC之父吧?誰也不願意當冤大頭,丢不起這個人啊。對吧?
當然有曆史原因。但是你不能因為它當時還湊合能用就認為它永垂不朽吧?或者,認為他就是一個很好的C++類庫設計典範吧?
總之,某些局部地方,MFC可能有可取之處,特别是它Bug相對來說很少。整體設計,如果不是MS的官方産品,我估計早就在垃圾箱了,在很多反面違反了,即使是作為OO對象對象理論來說的一些基本的設計準則。