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的原因只是因為怕人不好找,那麼,這家公司的文化也應該會讓很多資深的人敬而遠之了,許多資深的人才並不怕苦,怕的是苦完了還要被精神剝削,那麼,誰也不願意留下來了……

10 則留言:

  1. 寫的真好,我也是由 Delphi 1 開始用的

    回覆刪除
  2. 回覆
    1. 我在 2005 年的時候也曾一度對 Larazus 抱持很大的信心, 但它在安裝過程與能否安裝順利、第三方元件的支援經過了這麼多年仍然沒有改進之後, 我對它就停留在『極小眾開發工具』的觀點了.

      Lazarus 跟 FPC 的繁體中文 Wiki 還是我翻譯的, 從這一點可以看出我當年有多想推廣它, 當然, 當年的 Delphi 是 2005, 正是最慘的時候, 跟今天的 Delphi 完全不能比.

      現在我只會在很特定的時候才會用 Lazarus, 例如 Raspberry Pi 需要開發有 GUI 的程式時, 其他的情境就不會了.

      Windows 開發, 我一定用 Delphi.
      Web 開發, 小型的我用 php, 中大型的我用 Laravel, 客戶要求的話我可以用 C#.
      IoT, 裝置端沒有介面我會用 Python, 或者配一點 php, 必要時可以用 Anndroid thing.
      手機 App: 跨平台功能都不特殊的話我會用 Delphi 做, 特定平台要求特定功能的話, 我的 objective-c 也沒放下過, Java 也能隨時拿出來.

      開發什麼專案, 要視其特性選擇最適合的工具, 不能一條路走到黑, Pascal 我很喜歡, 但不是什麼都要用 Pascal的, 能隨專案選擇, 才是好的開發人員.

      刪除
    2. pascal的語言不是很好寫
      有點冗長...
      像..try except 就不能在後面跟java一樣多個finally嗎....
      還要再包一層 = =||
      要是沒有第3方那個cnPack 外掛.
      pascal真的是超難用!!

      刪除
    3. Lazarus & Free Pascal Compiler雖然進度緩慢,但是也一步一腳印地走,現在 FPC 3.2.0 RC1 也已發布了,想必正式版本不遠矣。

      小弟非軟工相關科系背景,工作也是比較扯不上邊的機械業,接觸 Object Pascal(Delphi/FPC) 差不多十一、二年,是從 VB6 轉來的,想當初還跟張兄請益過……

      身為非CS科班出身的自學者,個人認為接觸的(第一個)程式語言主要著重在

      1.學習資源多:書店的入門書、網路上廣大的範例對菜鳥來說不可或缺,程式語言不夠熱門可能對學習上會造成不少痛苦。

      2.易讀:與 C-like 語言(C/C++/C#/Obj-C/Java)相比之下,VB、Pascal這些語言沒那麼高度的符號化,看得懂英文大概就看得懂程式在幹啥,不用在那邊記 &&、||、++、-- ……等一堆符號是什麼意思,也許英文要打得字數多,但是在 IDE 有提供 Code Completion(例如Ctrl+J) 的幫助下,這倒不成什麼問題;況且,可能因為寫得不夠專業的關係,我大部分的精神都著重在「用什麼演算法解決工程問題、怎麼寫效率(維護效率 & 執行效率)會更好」,好像不差那多打幾個字的時間。

      3.快速開發:在2010前,支援 RAD 的 IDE 屈指可數,大概就 Visual Studio、C++ Builder、Delphi 這些,圖形介面的應用程式拉一拉、點一點、設一下屬性、寫寫程式碼,很快一個堪用的雛形就有了,不用從最原始的 Text Mode Console 開始真的降低很多距離感;這一點不得不說 VB6 佔盡優勢,因為 Excel VBA 連介面都不用設計,Function寫好就能用。

      4.執行檔依賴性低:辛辛苦苦寫好的程式,拿到別台電腦上跑結果跳出一堆卻這缺那的,又要安裝一堆哩哩叩叩的 Runtime Redistributable、Framework、VM Environment,說真的會讓初學者(甚至程式成品的End User)光看到就軟了;起碼,跟 C/VB6 一樣在作業系統裡內建吧…

      接下來比較跟個人有關的需求

      5.協作性:讓我放棄 VB6 的原因除了MS不再維護以外,主要是沒辦法製作傳統的dll(只能做要額外註冊的ActiveX dll),缺乏與其他語言充分的協作管道;Function寫好要給別人調用常常出現障礙,又不想放 Source Code 出去時真的很兩難…

      6.支援夠低階:指標這東西雖然很多新生代的語言沒有,但是熟悉以後還真好用,VB、Java甚至.NET初期在這塊就比較弱。(當然我知道有變通作法,可是沒有原生支援我覺得還是多少有差)

      7.執行效率:在還JIT之類的優化技術還沒成熟之前,Managed Code的速度(至少在啟動上)確實比Native Code慢了一截,Interpreter與Script語言也是這點不理想。

      綜觀上述的條件,在 2010 年前的環境中,好像就 Object Pascal 比較符合,因此也就這麼一頭栽了進來;如今小弟仍在 Object Pascal 的路上前行,十年來的喜好只增不減,雖然人氣不如當年,但其風采依然迷人,即使在新語言百花齊放的今天,也難以動搖其經典語言的地位。

      刪除
  3. 從 2000 年進入 Delphi 領域, 寫到 2010 年,真的是好軟體,

    回覆刪除
  4. 公司很多模組都是在Delphi 5上開發出來的,目前開發一般商務軟體都還很夠用,(少不了一些用php去呼叫API的情況),真的是很棒的開發工具,可惜代理商行銷方式、力度需要加強。

    回覆刪除
  5. 臉書稱謂是Tom Lin2025年4月26日 清晨7:42

    如果用到x86 sar,or sal指令,在delphi要怎麼使用(密碼學如RSA,socket,影像處理,或是一些工具程式……)delphi只有shr,shl特別是沒有sar;以前還聽說有人要寫NT SERVICE(上司要求,而那位據說當時不太會C++),從同部門別人的PC抓檔案儀器運作輸出資料,儀器運作後輸出的資料當作文字檔,轉檔,還使用微軟的INI沒用REGISTRY,變成NT SERVICE要使用Windows WnetAddconection2()那種API指令,他去問別人,別人回說GOOD LUCK,等於要和區域網路認證機制搏鬥,寫成DCOM,COM+也不好寫,用Delphi包的NT SERVICE元件也很難寫(應該就卡在局網安全性認證,一些Role API,不是DCOM的Role API函數),搞死他了,或許可以用Internet 13 port像是Time Server或Client那樣,看看有沒有Internet服務能用,但又希望直接在客戶機器上自動安裝程式,又要安全性(越少人能接觸越好),所以才不用更廣域的Internet,真的搞死那個人了。這種很難問到又了解delphi又會寫NT SERVICE for區域網路程式(姑且名之)的人,也是Delphi資源不夠多的特色。會卡住。

    回覆刪除
    回覆
    1. 您好, 正好您提到的"密碼學", "Socket", "NT Service" 都是我用 Delphi 處理過的程式, Socket 可以用 Indy 元件處理, 當中的 TIdDNSServer 是我寫的. DCOM 在近十年已經很少見了, NT Service 不會很難.

      所以您如果對於這些主題有興趣, 歡迎一起討論交流.

      刪除
  6. 我從 Delphi 5 到7 一直現在都用 delphi7 都自己開發元件也支援 unicode 唯一困擾 Delphi 7 編譯的程式容易誤判病毒。

    回覆刪除