鴨子介面
2005 年 12 月 21 日
也許我太天真了,但我從未預期過我在 HumaneInterface 上發表的文章會引起這麼大的討論。遺憾的是,大部分的討論都集中在 Ruby 的陣列和 Java 的清單的相對優缺點,而不是我試圖提出的基本觀點,但儘管如此,我認為還是出現了一些不錯的對話支流。
(雖然我必須指出,我並非有意表示我認為 Ruby 的陣列比較好,或者 Ruby 比較好 - 我不認為在沒有更多背景資訊的情況下,哪一個比較好。事實證明,我喜歡 Ruby 的陣列和 Java 的清單,儘管它們的設計相當不同。就像任何軟體一樣,它們都有缺陷,因為沒有人寫的類別能完全符合我當下的需求,但我不想拿我的任何程式碼與它們相提並論。重點是它們都很有用,而且我經常使用它們 - 這就是為什麼它們會浮現在我腦海中作為範例。)
其中一個對話串指出,除了人道/極簡主義的理念之外,陣列和清單之間還有其他差異的原因。其中一個原因與類似功能在兩種語言中扮演不同角色的方式有關。
Ruby 的陣列有許多方法,當人們看到清單時會感到困惑。推入和彈出 - 正如 Elliotte 所說「有人把堆疊推入清單中」。還有位移和取消位移,它們就像清單。這看起來不對 - Elliotte 再度表示「如果你正在使用佇列或堆疊,你不應該使用佇列或堆疊類別,而是使用清單類別嗎?」
讀到這段話觸發了我記憶深處的一個想法。我找出了 Smalltalk 最佳實務模式,這是一本很棒的書,任何認真想了解物件導向的人(儘管語法很奇怪)都應該閱讀這本書。
「許多人在接觸 Smalltalk 時寫的第一個物件就是堆疊。堆疊是基本的資料結構,在歌曲、故事和數百篇關於理論程式語言的論文中都曾被傳頌... 在任何基本映像中都沒有堆疊類別。我已經看過無數次有人寫堆疊,但它們似乎從來沒有持續多久。」
-- Kent Beck
至少當時,Smalltalk 的方法是使用 OrderedCollection
,它是 Smalltalk 中等同於 Ruby 陣列的類別。甚至沒有推入或彈出 - 相反地,Kent 展示了如何使用 addLast:
和 removeLast:
。
Kent 沒有說明為什麼沒有堆疊(和佇列) - 「為什麼 Smalltalk 中沒有堆疊?嗯,『就是這樣』。模擬堆疊使用 OrderedCollection 是文化的一部分。」
我不確定我對此有何感想(而且我從 Kent 的文章中得到一種明顯的不確定印象)。如果你要使用類似佇列的東西,那麼說 Stack new
而不是 OrderedCollection new
是有道理的,更不用說使用 push
和 pop
而不是 addLast
和 removeLast
了。
我認為這種情況的部分原因可能是靜態語言和動態語言之間的差異。靜態語言喜歡透過嚴格的類型介面與物件對話,動態語言有可以扮演多重角色的類別 - 鴨子型別。Java 也有兼具佇列功能的清單:LinkedList,但你通常會透過不同的介面使用它,而不會意識到它們的共用實作。Smalltalk 的感覺是,我們可以用 OrderedCollection 得到我們想要的,那麼為什麼還要建立另一個類別?Ruby 似乎呼應了這種反應,並為在這種情況下使用它的人新增了有意義的方法名稱。(雖然公平地說,我不知道 Rubyist 是否真的滿意 Array 是個堆疊,或者這是否是一個令人遺憾的遺產。)
另一個因素是語言鼓勵如何實作這些結構。正如 Charles Miller 所說的,「Java 的設計提供了小型介面,以及提供為輔助類別上靜態方法的公用函式。Ruby 的設計提供了較大的類別,並混合了公用函式方法。」
或許從中得出的結論之一是,我們應該小心使用另一種語言的價值來判斷一種語言中類別的功能。Array 是否等於清單或清單加上集合套件中的各種介面和實作 - 或者它甚至更複雜?有些人可能會對對清單執行 78 項操作的想法感到退縮,但我懷疑 Lisp 使用者會想到更多要新增的操作。Ruby 的 Array 有其缺點,但我必須承認,我喜歡使用它多於 Java 集合,儘管這在多大程度上歸功於人性化介面指南,以及在多大程度上歸功於 Ruby 語法支援,我並不確定。
總而言之,我不確定我同意這裡的誰,或者這是否真的那麼重要。就像許多這樣的論點一樣,我認為最有趣的事情是嘗試理解這兩種觀點。