標籤:

設計中的變與不變

古希臘哲學家巴門尼德認為:「人的思想和言語都有一個載體,如果你在這一時間和另外一個時間想到或者談到同樣一件東西,那就說明這件東西在這段時間內沒有變化,如有變化的話,你說的就不是同一件東西。」

古希臘哲學家:巴門尼德

這讓我想起對象的實例。在面向對象設計中,默認情況下並沒有約束類的實例是否為可變,這意味著我們可以通過某種方式改變實例的狀態。這體現了實例的可變特徵。然而,若是站在內存的角度觀察實例,則又不然。無論它在內存中存儲的狀態如何變化,該實例的對象標識依舊是保持不變的。顯然,變與不變是相對的。

切換到DDD的命題中,所謂「實體」就是那種具有唯一的可識別可跟蹤ID的對象。這個ID並非程序語言在內存中為它分配的對象標識,而是從領域角度來看,由設計者為其識別,由創建者為其分配,因而具有領域語義。實體的狀態當然是可變的,然而實體ID在這個實體的生命周期中卻是不可變的。

與之相對的是值對象。在DDD中,強調將領域對象嚴格區分為實體和值對象。一個指導原則是,當你無法分辨某個領域對象究竟是實體還是值對象時,應優先將其建模為值對象。這有助於我們更好地利用值對象的不可變性。

不可變的對象能夠更好地維護,因為你不用操心它的值變化,也無需追蹤變化的軌跡。不變性天生支持並發。這就衍生出面向對象設計中的Immutable模式。例如Java和C#中的String類型,皆為Immutable模式的實現。

可若放在函數式編程中,這種模式就顯得有些可笑了。尤其在純函數式編程的世界裡,任何東西都應該是不變的。

這種不變意味著只要它存在,就不可修改,而且恆古不變。這種追究變化背後的不變性,一直是古希臘哲學乃至科學的基本原則。物質是否永恆不變,在哲學中一直是引人深思的命題或假設;但在函數式編程中,它幾乎被證明了。例如,在Haskell中,對List的任何操作,即使調用++對List進行合併,返回的都是全新的List對象,原有對象不會有任何變化。

羅素在《西方哲學簡史》中寫道:

有的神秘主義者認為永恆並不是指時間上的永久,它是獨立於時間之外的,無前無後、無因無果,也沒有邏輯可循。

我覺得函數式編程追求的不變性,可以劃入這個範疇。

赫拉克利特說:「人不能兩次踏進同一條河流」。這是赫拉克利特終極的哲學觀,即萬物隨時在變。軟體系統就是這樣一條河流,它無時無刻不在變化,正如水不斷的流動,需求也總是在變化。但若拋開原子裂變、放射衰變的科學原理,我們似乎也可以將組成整條河流的每一滴水,看做是不變的基本組成要素。這個要素就是Monad中的Identity(幺元或單位元)。這個Identity表達了單一、恆等的概念,例如Int類型中加減法運算半群(SemiGroup)中的Zero,就是一個Identity,因為半群中的任何元素a與Zero結合,依然是元素a本身。

古希臘哲學家:赫拉克利特

水是如何組成一條河流的呢?這取決於組合子(Combinator)的設計與組合。只要我們找到萬物的基本要素,繼而設計出各種組合子,就可以演繹出世間不同的物。例如水滴雖可以組合為河流,卻也可以組合為橙汁,只要我們加入橙子的另一個組合子即可。這就是面向組合子(Combinator Oriented)的設計思想。顯然,它與面向對象的設計哲學背道而馳。

以哲學史觀之,函數式思想更符合古典的樸素哲學觀。在古希臘哲學家中,泰勒斯認為世界的元素為水,阿那克西美尼認為世界的元素是氣,赫拉克利特認為世界的元素是火,而恩培多克勒則糅合了這些思想,認為世界的元素有土、氣、火、水四種。而觀中國古代哲學,則有五行學說認為宇宙萬物都由金木水火土五種基本特性的運行和變化所構成。

不論構成萬物的基本元素為何,這種哲學觀不正是函數式編程的設計觀嗎?

推薦閱讀:

Bash 的威力
什麼是軟體開發中的過度設計?
為什麼 Windows 環境下不能刪除被載入內存的磁碟文件?
功能安全中的軟體隔離(二)
哪個 App 整體都挺好,但卻因為一個小細節讓你特別討厭?

TAG:软件设计 |