關於程式設計時的「模式」一詞,似乎已經有太多的資料了,多到會給開發者一種錯覺,彷彿那是個遠古就存在的東西,只是等待著開發者在接觸與瞭解之後,決定要擁護或反對,最後大家都對這場戰爭厭倦,然後失去戰場的模式被高歌著已死,然而,事情不該是這樣的。

如果從沒接觸過模式?

思考一下,在接觸過「模式」這名詞之前,你是怎麼寫程式的?資深一點的開發者應該有這類經驗,在軟體開發這領域,就模式相關名詞來說,不過就是1994年那本《Design Patterns》之後,才開始被大量討論的,沒有這名詞之前,大家還是在寫程式的。

然後,各式程式語言也都有了模式的相關書籍,而年輕一代的開發者呢?

在學習程式設計、軟體開發的過程中,差不多撰寫過一定行數的程式碼之後,就會因為前人的建議,直接開始接觸模式了,後續無論是在學習程式庫或框架,也容易不自覺地被若干模式導引著,當觸及某些問題情境,想到的也許就是「這能用某個模式來解決」,然而,事實真的是如此嗎?

這個有趣的問號,導因於某天我看著這些日子以來撰寫的3D建模程式庫,突然想到「裏頭有模式嗎?」我設計了一百多個作品,看過大量的建模案例,也從這些作品案例中建立了程式庫,程式庫的目的是作為可重用的元件,裏頭一定有某些模式存在吧!只是回頭來問這個問題的感覺很奇特,因為在撰寫程式的過程中,從來沒意識到思考過程中,有模式的相關概念存在。

這是個很自然的結果,因為OpenSCAD並非主流語言,撰寫程式時的心智模型與主流語言顯然不同,自然不會在撰寫程式前,先去思考能否套用某種模式的問題,那麼,有人在寫OpenSCAD時提及或實際使用到模式嗎?在略為搜尋網路之後,並沒有找到OpenSCAD程式碼方面的模式相關討論,模式似乎還沒侵入這個領域。

然而,設計作品、觀摩案例與建立程式庫的過程中,有不少的問題確實呈現著相似性,而後續為了避開問題,也確實有著一組思考流程,只是這些思考流程還沒有正式定義,沒有歸類,也沒有名稱罷了。

我用了哪些模式?

那麼就過去寫過的建模來說,我嘗試過哪些思考流程呢?

對於從沒有設計過的模型,在還沒有辦法確切地切割出子組件的時候,我會隨意地撰寫程式碼,並且儘可能地將其中不是常數的數值進行參數化,如果發覺有一些程式碼共用著某一組參數,就算它們在程式碼中是散佈在不同段落,也可能意謂著它們是同一個子組件,這時就可以將它們集中在一起,而後嘗試抽離出一個子模組,為了本文後續的方便溝通,姑且用「參數化模組」來表示這個過程吧!

參數化模組在經驗中非常頻繁地使用,因為若不儘早將使用到相關參數的程式碼模組化,程式碼會迅速地膨脹到難以理解;進一步地,在傳遞引數給已經被參數化的子模組時,若發現總是只調整某組引數,這表示子模組中有著更小的部份,僅受這組引數控制,這時可以從子模組中再抽出更細的子模組,最後,就能得到扇形、弧形、線段等這些可以被放到程式庫中,可重複使用的基礎元件了。

現在假設想讓正方體繞著圓柱盤旋,不久之後,又想要讓五芒星繞著圓柱盤旋,這時就會想到,想繞著圓柱盤旋的不只是正方體或五芒星,而可以是任何子模型,這時程式碼中的球體或五芒星,就可以換成children(),之後就可以用這個盤旋模組,讓任何子模型繞著圓柱盤旋,同樣地,取個「泛化子模組」來表示這個過程以便溝通。

泛化子模組乍看很像是設計模式中樣版、策略之類的模式,因為除了children()之外,重複使用程式流程的概念是類似的,不過實際上,除了可指定索引表示會用到第幾個子模組之外,無法傳遞其他引數給children(),因此,如果只是讓一個A重複地繞著圓柱盤旋是無妨,但如果想要的是3.14159……中每個字依序顯示在盤旋時的各個位置,就不是這個模式能解決的了,OpenSCAD中沒有物件,也沒有一級函式的概念,泛化子模組終究不是樣版、策略之類的模式。

來仔細想想吧!如果撰寫了專用的盤旋模組來處理3.14159……的問題,那麼,與通用的圓柱盤旋版本有哪些「差異」?程式碼中一個是放text(),而另一個是放children()?不對!差異在於前者還必須知道這是盤旋時的第幾個點,而後者只需要點的座標,為此,我採取了「剝離資料」模式,將泛用版本的盤旋模組中計算資料的部份重構出來,成為一個可以傳回盤旋路徑上各點清單的函式,接下來在迭代這個清單的過程中,自然就能知道是第幾個點,也就能依此取得對應的數字來盤旋。

所以我有了模式?

當然,我還有應付其他問題時的思考流程,也可以再繼續為每個流程,取個酷炫名稱,不過,你不一定玩過OpenSCAD,也沒興趣瞭解相關細節。而這邊的重點在於,如果踏進了某個問題領域,其中還沒有任何模式名詞的存在,那麼,你會如何觀察出相關性、重用程式流程、找出程式中真正差異的部份呢?

顯然地,總是要有重複的「事實」,也就是一定量的案例,當然,也許開發者已經看過了大量設計模式的文件或書籍,從他人的經驗吸取了一些重複的「案例」,沒必要先浪費時間先寫出有重複的程式碼再來重構,然而,如果聲稱程式碼中使用了某個模式,那麼開發者應該能將程式碼逆向推至未使用模式的狀態,也就是程式碼經過「反重構」之後,程式依然能夠運行。

剛剛的參數化模組、泛化子模組、剝離資料,這些名詞背後代表著什麼呢?代表著背後有一套思考過程,這個過程能引導開發者,在一堆看似沒有相關性的程式碼片段中,觀察出其中的相關性,就像參數化模組,運用了一組總是相關聯的參數來辨別相關性,之後開發者才能整理程式碼,使之形成易觀察的流程,從中抽離出可共用的程式碼。

然而,就像泛化子模組時會遇到的問題,有時明明看出了有相同的程式流程,卻因為語言或技術的限制,似乎無法更進一步改善彈性了,這時開發者就需要有另一套思考流程,進一步觀察出程式中真正有「變化」的部份,而不是繼續被既有的模式框住,死盯著程式碼中顯而易見的重複部份意圖改進,從《Design Patterns》之後又陸續冒出的新模式,就是這麼來的。

當然,剛剛這幾個模式僅存在於我的腦海之中,如果真要說個仔細,最好是有範例程式碼,指出問題所在並進行重構,然後再提供重構之後的程式碼,接著為這個過程取個名字,這樣日後我想要另一位開發者,將程式碼重構為某個模式時,對方就能大致想像套用模式後的程式碼,應該會是長什麼樣子?

該如何做,才是模式?

如果是這樣的話,那我還是不要提供重構前後的範例程式碼,也不要為模式命名好了,這樣的模式只會帶來思想上的限制,而不是面對問題時,發展出更多解決方案的可能性。

就我來說,模式其實代表著是一種過程,這個過程能在初次面對問題時,讓思考逐步專注,投入於觀察相關性、重複性、差異性的活動上,而不是馬上進入程式編寫的細節,如此在未來遇到類似問題時,曾經從事過的思考過程,就能帶來快速的解題過程。

若是遇到的問題是嶄新的,這種思考過程也才能帶我進入解題時的神馳(Flow)狀態,從中發掘出新的思考路線,也就是產生新的模式!

作者簡介


Advertisement

更多 iThome相關內容