最近重新整理一些關於模式的看法,我想起了看過有不少人認為,模式暗示、代表或者其實就是語言的缺陷,而他們有些人談到的模式,是狹義的Gof設計模式,總會列舉其中一些模式,稱在某語言中根本不需要這些模式。

語言缺陷的說法來源?

關於語言缺陷的說法來源,不乏許多大師級人物,例如,《松本行弘的程式世界》作者,在書中談過:「有些模式在C++、Java這類靜態定型語言中很有效,但在Smalltalk與Ruby這類動態定型語言,沒有多大意義」,甚至Gof的Ralph Johnson在〈Design Patterns 15 Years Later〉也談到:「有些語言不需要某些設計模式,這些語言提供了問題的替代方案」!

也有不少人會引用Peter Norvig的演講〈Design Patterns in Dynamic Programming〉內容,例如,Brendan Eich在《Coder at work》就曾提過:Peter Norvig說模式顯露了語言的某種缺陷,Brendan Eich後續進一步說到,應該尋求語言的演化,加入適當的元素。

有的人則指出,函數式語言不需要設計模式,例如,Weblocks創建者Slava Akhmechet曾在〈Functional Programming For The Rest of Us〉中,直指「函數式語言不需要設計模式,因為語言夠高階,程式設計使用的概念會消除設計模式」。

有些人談到模式時,沒有特別說是Gof設計模式,而是指出現類似設計,甚至是某種規律,例如,Paul Graham曾在〈Revenge of the Nerds〉最後談到:「在程式中看到模式,他認為是個麻煩的訊號」、「程式碼中任何規律性都是個訊號,至少對他而言,代表著目前使用的抽象元素不給力」。

設計權衡與語言限制

單就Gof的設計模式來說,由於作者們使用靜態定型、物件導向的C++來呈現範例,後續又很大程度地被同為靜態定型、物件導向的Java發揚光大,經常地就會引來動態定型、不愛物件導向、支持函數式語言的人們進行批判,聲稱自己熱愛的定型方式、典範,不需要這些設計模式,進一步宣稱C++/Java需要這些模式,是因為它們有問題!

在我看來,這是太過於重視設計模式的名稱、實現形式,而不是去重視設計模式的思考過程了,而且,有些人的論調會讓人覺得:他們只是純粹討厭靜態定型、物件導向,或許是因為身處的領域中,動態定型、函數式可以更愉快地解決他們的問題,卻不去看待其他領域,有些人使用靜態定型、物件導向,其實能適切地解決需求的這個事實。

很多人會引用Peter Norvig的演講,然而,因為我找不到影片或逐字稿,單就演講用的投影片來看,並沒有直接提到設計模式是語言缺陷之類的內容,倒是常提到設計的權衡(trade-off)與語言的限制(limitation),像是第四頁談到什麼是模式時,其中的兩個要點就是「用來討論、評估、記錄設計上的權衡」、「避免實作語言的限制」。

他也談到,設計模式有著無形(Invisible)、非正式(Informal)、正式(Formal)等不同的封裝層次,由於動態語言的限制較少,Gof的23個設計模式中,有些模式在動態語言會是無形,例如,具有類別封裝概念,然而沒有正式類別語法,有的模式會被簡化,例如物件間的協定,不見得像Java使用interface來定義,鴨子定型就可以了。

那麼,我們能說類別、靜態定型限等制、16個模式代表著C++/Java的缺陷嗎?既然如此,為什麼JavaScript後來要加入類別語法?TypeScript基於JavaScript增加了靜態定型?Python的型態提示會需要Protocol呢?

請不要因為長期以來,使用的語言一直難以解決你某些需求,看到另一門語言簡單幾行就能解決掉,就開心地擁抱另一門語言,畢竟該語言可能在面對其他需求時,有更多難以施力之處,想一下,是不是因為長期以來的積怨確實獲得宣洩,導致你忘了、沒時間或不想去檢視那些需求罷了?

程式碼中的規律性

有人會說JavaScript是有缺陷啊!因為不支援類別,才會產生許多模擬類別的模式,因而ES6以後才制定了類別語法,這不就是模式代表語言缺陷,新特性補上後就不需要那些(模擬類別)模式了啊!

這就引發了一個問題!若單純認為模式是語言缺陷,語言的新版本用新語法就是補上缺陷,那麼,總有一天,會出現一門沒有缺陷的完美語言嗎?

絕對不會!因為在不斷因模式出現而彌補缺陷的過程中,這語言會逐漸被不斷新增的特性給壓垮,而越來越多的特性,造成學習曲線越來越高、選擇越來越多、程式碼越來越複雜、越來越看不懂、越來越難以維護……,然後越來越多的人遠離該語言……,直到有一日該語言迎來終焉。

無論是設計模式或廣義的模式,代表的是一群人在目前語言的抽象上,建立起來的相似性,代表有著越來越多人,試圖解決類似的問題,Paul Graham說,這類規律性,是目前使用的抽象元素不給力的訊號,然而,若因此為語言加入新特性,語言終究會被特性壓垮,這也是越是悠久的語言,面對新特性越是躊躇不前的原因。

而且,你知道〈Revenge of the Nerds〉在談什麼呢?談為何現代主流語言有個趨勢,新特性多少有著Lisp的影子,或者更進一步地,朝著函數式設計、lambda演算,甚至是數學層次抽象靠近的趨勢,某些程度上,是在吹捧Lisp的文章,而且,函數式不也是有些模式的存在嗎?

另一方面,往數學層次抽象靠得越近,確實就能解決越多的問題,文中談到Lisp一開始不是以語言為出發點來做設計,而是基於數學的抽象來做設計,因而Lisp有些元素是允許你實現方言,來解決某群人面對的類似問題,然而,這也是程式庫或框架在解決的事,只是有些語言可以讓它們,看來像是語言的一部分罷了。

模式是程式碼中規律性的表現,然而,規律性來自於某群人面對類似問題時,是怎麼分解問題、衡量限制、思考對策及實現;如果模式代表語言的缺陷,這就好像在說存在一門語言,用它來撰寫的話,每個人寫出來的設計、風格絕對不會構成相似性,有這種語言的可能性嗎?而且,這另一方面也是在說:不會有程式庫或框架的需求了!

運算本質上就是模式

某模式不代表語言的某缺陷,而是代表過去一群人面對某些問題到解決的過程,發現某些共通、抽象的概念,只不過你試著用這些概念解決問題時,實現上困難或簡單罷了,例如,C仍能實現物件導向的概念,只是比較麻煩,C有自身應用的領域,不能因為想實現物件導向麻煩,就說那是它的缺陷!

許多人會說是缺陷,可能只是實作上覺得麻煩,或是反對不假思索地套用模式,反對過於重視模式的名稱、實現形式,反對將程式碼寫到像某種模式作為終點,而不是將之視為思考過程的參考之一。

不要單純地從模式是語言缺陷來思考,畢竟模式構成的過程中,要考量的事情太多了,有時甚至是因為從使用的工具、團隊合作、管理等角度來考量,才會構成某種模式,而且,別忘了,語言的本質就是運算,而運算的本質就是重複,運算本質上就會構成模式,不會構成模式,又何來運算呢?

專欄作者

熱門新聞

Advertisement