2019年8月4日 星期日

Delphi 為何仍然是開發工具最佳選擇

前言– Delphi的興衰與奮起

2019,是Delphi問世之後的第26個年頭,IDREA推出了Delphi/C++ Builder/RAD Studio的第20個版本—Delphi XE 10.3.2,但截至筆者撰寫本文的這當口,全世界仍有很高比例的Delphi開發人員持續在使用Delphi 5-Delphi 7,無關工具本身的好壞,而是關於既有專案能否「無痛升級」。
筆者在Delphi開發人員當中屬於異類的少數,並非筆者的能力,而是開發的應用面向不同。傳統的Delphi使用者,大多集中在與資料庫相關的系統,例如ERP、財會、MES等類別,以文件與資訊交換為主要作業。
Delphi甫一推出,就憑著高效能、與多種資料庫(尤其是Oracle)的介接容易、在Windows作業系統中的高相容性,程式語言的完備度,博得了「VB殺手」的薄倖名。
然而從1993-1996年之間,稱譽資訊學界近20年(含AdaPascal)的Pascal程式語言,漸漸被物件導向概念夾擊,先有挾C語言廣泛性優勢的C++出現,到了1997又有號稱全物件導向、可以透過虛擬機跨越所有平台、以沙盒概念提升安全性的JAVA來襲,整個資訊學界言必稱JAVA,語必稱物件,傳統的Pascal失去了學界的關愛眼神,從1998年開始,全台再沒有學校教授Pascal
然而,Borland1994年推出了Delphi,以Pascal為基礎語言,更為Pascal加入了物價導向的概念,成為Object Pascal,整個Delphi的物件導向概念與視覺化元件不斷優化,到了Delphi 5的時候大致完備,當時是1996-1997年間,然而JAVA當時也還沒完備,除了文件得印上幾大本,當年連編譯器都還沒有出現,遑論虛擬機。
到了2000年,Borland 負責DelphiPascal的靈魂人物Anders Hejlsberg被挖角到微軟,把原本沒能在DelphiObject Pascal語言上實現的設計與理想用在微軟新一代的平台跟語言設計上,於是就誕生了.Net FrameworkC#語言。接著Borland策略錯誤,以為可以做出Delphi 8成為跨.Net與機器語言的工具,沒想到微軟四兩撥千斤,2004年以不授權.Net FrameworkBorland為戰技,一下就讓已經要上市的Delphi 8無法推出,Delphi只好回頭繼續改良原本的工具,但也就此流失了關鍵的3年,直到2005年初才又推出Delphi 2005,但效能不如Delphi 7,介面又大幅度改變,所以絕大多數的Delphi 7使用者都不升級,歷經Delphi 20062007,都一蹶不振,於是Borland把開發工具部門獨立出來,成立了CodeGearBorland則專注於軟體開發流程控管的產品,後來越發寂寥,直到現在已經很多年沒聽過Borland了。
Delphi 2009這個版本中,可以算得上是CodeGear發奮圖強的開始,這個版本是第一個全環境支援Unicode的版本,從元件到程式碼,都完全支援Unicode,換句話說,除了Object Pascal語言的關鍵語法,其他的變數、類別命名,都可以使用Unicode字元,如果你願意,可以把變數用中英文混合命名,但打字會麻煩很多,所以筆者雖在2009年就已知道有此一功能,卻從未使用過,因為對維護來說,這是一個很負面的作法!
Delphi 2009算的上是CodeGear努力的成果,但到了2009年,Windows應用程式已經不是市場主流了,取而代之的主流,是手機應用程式。
筆者也是在2009年開始轉向手機應用程式開發,從Objective-C (iOS)JAVA (Android),連後端的控制平台(PHP),沒有一樣不用重新學習。在筆者已經把Objective-C弄到滾瓜爛熟之後,到了2014年,手機應用程式的製作市場又飽和了,此時Delphi已經又推出了Delphi 2010, Delphi XE, Delphi XE2, Delphi XE3這幾個版本,而從Delphi XE2之後,這個工具已經不再只是原本的開發人員所熟悉的Windows應用程式開發工具,而是跨平台的開發工具了,在XE2的年代,Delphi用來開發MacOSiOSWindows 32bit, Windows 64bit這幾種平台的應用程式已經完全沒有問題了,唯一的問題是,原本的開發人員也已經大多轉向了相同設計的C#,而Delphi成為了歷史名詞,不再能夠在一堆免費的開發工具中受人矚目。
之後從XE5 (Android Ready)XE6 (各平台穩定)XE7 (iOS 64bit)XE 10.0, XE10.1, XE 10.2, XE 10.3,除了更為穩定、速度更快,也完成了MacOSX 64 bitLinux程式的編譯功能。
歷史總是有很多有趣的地方,也有很多令人感傷的地方,但大多數的演進都已經介紹過了,我們開始進入主題,從技術面介紹一下新的Delphi

VCL v.s. FireMonkey

Delphi 1.0開始,視覺元件架構就是VCL (Visual Component Library),這個架構從Delphi 1.0一直到目前的Delphi 10.3都是Delphi的重要核心。VCL提供了絕大多數我們能夠在Windows作業系統中看到的視窗元件,並且與時俱進,從Windows 3.1的版本一直到Windows 10的版本,都能直接以原生碼(Native code)的方式執行,無須另外裝.Net Framework,但有些Delphi的元件在動態連結時會需要使用附在Delphi系統中的BPL檔案,這樣的檔案可以橫跨Windows 3.1, Windows 95, Windows 98….. 一直到Windows 10,相信目前沒有任何其他工具可以做到。
Delphi XE2之後,所有跨平台的應用程式,都需要使用FireMonkey這個新的架構來製作,FireMonkey當中,提供了WindowsMacOSXiOSAndroidLinux五大平台的元件,而尤其值得一提的是『單一專案,可編譯五種平台程式,各種程式都是原生機器碼!』
VCL當中,雖然可以滿足所有Windows平台的視覺元件需求,但VCL還是有其功能上的限制,例如:
1.      VCL要製作元件,所有元件都必須透過畫布重繪來顯示、也都必須由開發人員自行控制當中的互動邏輯(VCL元件中不能內含其他元件)
2.      VCL 元件必須透過元件註冊才能分享給其他程式在設計階段使用。
跨平台的功能就完全別提了。
FireMonkey則有幾個好處:
1.      跨平台,而且自動套用各平台的視覺樣式
2.      元件可以內含其他元件,製作元件時可以省下很多時間
3.      畫布與圖片都支援32Bit Alpha圖片,也就是可以製作半透明的畫面
4.      FireMonkey的畫面與座標會自動依照螢幕解析度進行必要的調整,過去在Windows系統中,傳統Delphi程式會在Windows螢幕字形比例大於100%的時候出現畫面錯置的情況,在FireMonkey裡面不會發生。
5.      MDI的程式問題,在FireMonkey不再發生。
理論條列講的差不多了,讓我們舉幾個實例來作深入一點的說明。
以往在VCL的程式中,很難把所有的畫面都集中在同一個表單檔案裡面,但在手機App的概念中,並沒有多視窗的設計,因此FireMonkey也才採取了『所有元件都可以扮演Container角色』的設計,讓程式畫面可以集中在同一個畫面中。
這個設計從XEXE5不斷的被驗證與優化,在XE5之後,FireMonkey提供了TFrame這個元件,讓我們可以把一整個畫面的程式碼跟表單(其實是Frame, 不是Form)獨立為單獨的檔案,這樣一來,所有的畫面雖然保留在同一個Form上面,但不同的畫面呈現可以被儲存在不同的Frame裡面。
很多公司把單一檔案的Size做了限定,希望程式碼可以不要集中在某幾個檔案裡面,這固然可以降低單一檔案的Size,使得Windows 32bitDelphi 7的限制不被挑戰,但有些時候,設計上是無法避免一個Pas檔案上3萬或5萬行的。(筆者在2008-2009服務於美商,製作備份系統的時候,單一主畫面就已經到10萬行,也曾聽聞有到15萬行的)
程式設計應該遵守的準則不少,但也需要看專案的性質跟是否能夠被實現,盡信書不如無書,這句話在程式設計上更應該被重視,也是所有程式人員應該時時刻刻謹記在心的。
透過適當的物件設計,避免獨立、不屬於任何物件的Function與變數,能夠避免一些問題,這雖然是物件導向程式設計的基本,但筆者在替許多專案程式碼進行優化與修改的時候,發現到雖然現在已經是2010年代,即將進入2020年代,但很多程式人員的物件導向概念還停留在1980年代。常見的問題很多,我列出幾個很嚴重的問題,期望大家盡量不要明知故犯:
1.   盡量完全使用物件設計,避免全域變數、全域函式或Procedure,避免多個Unit裡面宣告了相同名稱的變數,卻沒有被發現,在編譯上雖然不會出錯,但執行時卻會發生完全無法預估的錯誤,而且也很難除錯。
2.   在畫面上要使用的資料,務必獨立由該單元檔或表單檔獨立控制,如果其他單元檔案需要處理不同單元檔的資料,一定要透過參數、函式進行傳遞與修改,不要直接抓畫面變數、資料,這會造成未來多個單元檔互相羈絆,無法獨立修改內容,甚至升級,系統寫到這個程度的話,只能說是病入膏肓,除了重新設計,很難有救活的機會,越大的系統難度越高。
3.   承第2點,畫面的設計、資料處理、儲存的方式,最好可以獨立分開設計與處理,不要把這三個主要的部分綁的死死的。例如畫面上的各個欄位可以有獨立的變數名稱,儲存時用內部物件來儲存,要計算的時候透過這個內部物件來處理,需要存檔或讀檔的時候,也透過內部物件來暫存,這樣一來,任何一部分作修改,都不用考量連鎖反應,因為不會有連鎖反應,程式的修改與未來的維護才會簡化人力,也就是簡化成本。這個作法,在設計模式上面,被稱為MVC Pattern.(MVC模式)
4.   處理同一種計算或作業的程式碼,最好可以用同一個Method來處理,未來有任何需要修改的時候,修改一個Method,在系統中所有的地方都一起修改好了。在設計模式上面,這個作法被稱為Factory Pattern.(工廠模式)
5.   在處理可能會被大量呼叫的Method時,務必採用效率最好的演算法與最適合的物件,在這種Method當中使用越多的迴圈,程式的效能就會越低落。
6.   盡量使用Thread以及Task來處理可以同時被處理的作業,這樣可以在多核心的機器上獲得最大的效能。
傳統的VCL有很多難以避免的問題,也不一定可以守的住上述的幾種規範,因此,如果舊系統已經如我所述的『病入膏肓』,重寫在所難免時,請先考量FireMonkey

Big5UTF8

在我所看過的,歷史在10年以上的Delphi程式,很少有當時就已經能夠相容於Unicode的程式,即使是我所提到的這些少數的程式碼,只要不是使用Delphi 2009以後的系統升級或處理過的,也只能透過TntWare系列的元件提供Unicode相容的功能。
因此,筆者這十年來被問最多次的問題,就是『要怎麼樣才能無痛把舊版Delphi程式升級到支援Unicode』,然而,這並不是一個單純的問題,我們最少需要考量三個層面:介面、程式資料處理、資料庫或網路通訊。
這三個層面說的輕巧,但是能夠精通的人並不多。而且我說的是『最少』,最嚴重的問題不只是這三個層面,還在於程式中是否使用了『第三方元件』,而這些『第三方元件』是否有支援Unicode的新版本?或者這些『第三方元件』是否還有人在維護?是否擁有這些元件的原始碼?
我們就這些環節一個一個來剖析:
第三方元件通常是最棘手的。很多案例中,使用了大量的第三方元件,可能從網路通訊、視窗外觀客製化、繪圖、加密、資料庫、條碼產生、報表繪製/預覽/列印、到晶片卡讀卡/簽章等功能,除了晶片卡之外,其他我提到的元件,在原廠FireMonkey都可以有原生方式可以解決。
介面:舊版的Delphi製作的介面是傳統的VCL元件,只要該元件還存在,沒有隨著Delphi的版本更迭而被停用,就可以用新版的Delphi開啟該表單檔案,基本上可以無痛升級,但如果有些自行安裝或撰寫的視覺元件是舊版的,就必須要把這些元件全數升級、安裝到新版Delphi,才能正確開啟該表單檔案。
程式資料處理:這部份相對麻煩一點,可以分成兩個部分來處理,第一部分是寫在程式碼裡面的文字訊息,因為檔案本身是ANSI編碼,中文資訊自然就是Big5編碼。此時,我們可以用新版Delphi開啟程式碼,如果該檔案是表單,也可以按F12切換畫面跟程式碼。切換到程式碼之後,在程式碼的任何一個地方點滑鼠右鍵,選擇File Format,將之設定為UTF8,就『大致』完成了。
這只是『大致』而已,因為如果在程式碼當中使用了檔案、網路介面傳遞資料,傳統的Delphi程式人員習慣會把String拿來當成Byte陣列指標,然後用陣列元素來存取當中的資料,如果有這樣的情形,請記得把這些資料的處理改用TRawByteStringTBytes來處理。
Delphi 2009之後,String型別預設就是WideString。而在Delphi 1Delphi 2007當中,String型別預設則是AnsiString,長度是不同的,所以用陣列元素來取資料,會發生全部錯誤的狀況。
資料庫元件:通常傳統的程式中,使用的資料庫連線可能是DBExpressBDE、也可能是ADODBODBC,但到了Delphi XE 10之後,全數資料庫元件都更新為FireDAC元件系列,表單畫面上一定會找不到這些元件,這部份的修改是最費時的。
接著如果要改寫成FireDAC,需要修改的應該只有Connection元件、連線設定、Table/Query/Transaction元件的更換,至於SQL指令則不太需要更改。
還是那句話,這些分析都是輕巧的幾句話,但真的在裡面修改、更換元件,可能會是嘔心瀝血的過程,遇到問題的話,大家可以分享、交流一下經驗。
許多公司仍舊堅持著使用Delphi,可能是因為過去的Code Base很大,無法很快換掉,也可能是因為發現了Delphi原來的優勢、未來的優點。不可諱言的,Delphi的程式人員不像過去那麼多了,但這也是好事,表示濫竽充數的人也少了,我們不用花費太多的成本去抓出這些人來Fire掉。人才很多,帶人帶心,如何找到適合的人才,讓他願意為公司服務、在公司成長的路上一起成長,是每個公司的課題。

這年頭,實習、培訓、找外包,方法多的是,如果不用Delphi的原因只是因為怕人不好找,那麼,這家公司的文化也應該會讓很多資深的人敬而遠之了,許多資深的人才並不怕苦,怕的是苦完了還要被精神剝削,那麼,誰也不願意留下來了……

1 則留言: