What have you found for these years?

2011-11-06

人類、機器、與程式設計者

人類、機器、與程式設計者——談取捨與設計原則

似乎大多數的設計,都是在一連串的元素中選擇。有的元素可以同時被選擇,
有的則互斥導致無法同時被選擇。就像選了白色還是可以選圓形,但選了白色
就不能選黑色了。這是一個顯而易見的例子,然而,在大部份的情況下,元素
與元素之間是否互斥,卻不是這麼明顯。

一個很簡單的例子即是程式裡時間與空間的效能抉擇。一個爛的程式當然能夠
又肥又慢,而一個好的程式當然能夠又小又快。可是這種最佳化卻有著極限在,
到了一定程度後,要再更快就得犧牲體積,但那又不是說要犧牲就真的能犧牲
換到的,很多時候企圖要去換,體積犧牲是犧牲了,速度上卻也不見得真的
比較快。

我想,這種微妙平衡的拿捏,可能是一個永遠沒有解的問題。因為在不同的
情況下,我們本來就會有不同的需求。因此沒有給定一個特定的情境的話,
我們本來就沒辦法評估這樣的拿捏究竟是好或壞。不幸的是,情境這種事不僅
很難說清楚,還會不斷根據情況不斷改變。比方說,我們的硬體愈來愈好,
於是軟體可以更大量地「濫用」資源來換取其他以前不敢奢求的東西——
例如圖形介面。

為什麼我們需要圖形介面?

在談這件事前,先來看看軟體的使用對象有哪些。這邊大略粗分成三種,也就
是本文標題的三種——人類、機器、與程式設計者。設計給這三種東西使用的
軟體,會有非常不同的設計準則。

- - -

電腦(機器)畢竟是人在用的,人類擅長與人類溝通,不擅長跟機器溝通。
這也是為什麼我們要疊床架屋,不斷在現有架構上再架一層抽象層(同步口譯),
使我們不必真的去學對方的語言來溝通。

人類懶惰、容易犯錯、又善變。如果我們溝通的對象死板又不允許任何錯誤,
那溝通起來會異常辛苦。所以設計給人類使用的東西,最好:

* 懂得舉一反三,不同的話可以有同樣的意思。例如我們因為懶惰可能會想用縮寫,
但這不代表用全名是錯的。

* 懂得容錯或是揣測原意,因為我們容易打錯字或是講錯話,說出來的話自己沒
意會到講錯了,重複講一次才意識到自己說的不是那個意思,很常見。

* 軟體本身改變的成本必須盡量低。因為我們善變,又不見得能一開始就真的
表達出自己的本意。

換句話說,除了軟體本身外,重點在於使用介面 (UI) 和使用經驗 (UX)

- - -

然而給機器用的軟體就不同了。與人類相反,機器勤勞、穩定、又不易改變(通常
也沒有學習能力)。因此機器與機器的溝通重點則在於協定 (protocol), 一個穩定
不會改變的介面。簡單地說,如果程式都沒有問題了,也沒有改變需求的話,就
不要去動他 :P

另一方面,要達到這種境界,同時也需要嚴謹的定義,不然我們要怎麼確定他真的
沒有問題了?或許可以很簡單地用 UNIX philosophy (雖然似乎跟 philosophy
無關) 來描述這件事:

Write programs that do one thing and do it well.
Write programs to work together.
Write programs to handle text streams,
because that is a universal interface.

但也不只是這樣。機器終究是給人用的,人用錯誤的方式操作機器終究會在某處
變成機器的錯誤,因此我們還是需要容許這部分的錯誤,問題在於我們要在哪一個
環節點(整個系統架構中的哪一個程式?)處理這個錯誤?

- - -

那麼給程式設計者使用的軟體呢?也就是 library 或是 framework 之類的軟體。
由於錯誤地使用 library 或是 framework 幾乎可以肯定一定是某個 bug, 而在使用
這些軟體 (library and/or framework) 時,目標軟體還沒被製作出來,因此我們
需要盡早知道這些錯誤的使用,才能避免 bug 流入最終軟體裡。

也就是說,這些軟體本身需要極為嚴謹,不容許任何錯誤。一個簡單的想法是,
我們必須用比較強的表達能力,去表達比較弱的東西,而不是反過來。因此我們
需要極為嚴謹的東西去做能夠容許錯誤的東西。

所以程式設計者其實是比較接近機器而不是接近人 (?)

而其實這個原則也同樣可以用在某些純機器使用的軟體中,正如同上面所提到的,
整個系統不單只是一、二層而已,我們也需要一些不容錯的軟體來協助製作容錯的
軟體。參考 fail-fast.

- - -

以前我在設計軟體時,總是試著拒絕不正確的操作。理由非常簡單,因為不正確
就是不正確,而且直接拒絕不正確的操作,對於程式而言比較容易撰寫。畢竟我們
需要考慮的條件比較少,核心程式可以假設輸入永遠都是正確的。

同時,因為我正是開發者,我希望盡早看到錯誤,在比較簡單的環境下注意到錯誤,
才比較容易找到出問題的原因,並將之修好。尤其我讀到 C++ 的 template 有所謂的
two-phase lookup, 更是讓我覺得盡早報錯是一件非常重要的事情。

不過死死板板一板一眼的軟體,應該是沒幾個人類會喜歡用。因為那強迫我們必須在
腦袋清楚的情況下使用。當然危急到人類性命安全的軟體等等除外,那還真的必須
一板一眼地強迫使用者想清楚。但一般工具性軟體,實在不會想要這麼聚精會神地使用。

另一個教訓是,我以前寫的一些東西,別人往往不太會用,因為其他人可能沒那麼清楚
設計這套軟體的脈絡。他們可能不知道這軟體要幹嘛,甚至也不知道他們自己要幹嘛...

- - -

總結來說,給人類使用的軟體,需要謹記人類懶惰、容易犯錯、又善變。不把這些事
放在心上的話,不容易設計出「好用的」軟體。而給程式開發者的軟體,也就是 library
與 framework 等等,則需要嚴謹,不能太過於容錯。給機器使用的軟體,如 OS 或
database 等等,則大抵上介於人類與開發者之間。

而這些東西,其實或多或少可以說是 Rails 教我的。

Rails 是個 framework, 但是他不嚴謹,有大量的容錯,大量的模糊地帶,違反我認為
framework 該有的性質。但是但是,程式設計者,難道真的不是人類嗎?其實我們也想
偷懶一下,隨隨便便,模模糊糊地描述我們要的東西。

我覺得這正是 Rails 帶給程式界很重要的一個精神。他之所以紅翻天,到處都有 clone,
成為許許多多其他 framework/library 的 inspiration, 終究是有其很重要的理由的。
儘管我不斷批評不斷抱怨其莫名其妙之處,但說穿了應該只是性格不合,或是說目的
不同吧。

就像很多人不能接受 Ruby 的隨便程度,而我則是不能接受 Rails 的隨便程度。

也就是說,雖然我覺得 framework 必須是嚴謹的,但不嚴謹也是能走出一片天。
應該是沒有絕對的法則吧,只有一般情況下的說法。重點在於,不同的設計準則,
會做出不同風貌的東西。

- - -

我後來設計的東西,如 rest-graph, rest-core, rest-more, rib, shere, 都努力地
把「人類」的需求考慮進去。東西畢竟是人要用的,過度強求「一致性」反而對人類
而言不方便,那可能或多或少就有點本末倒置了吧。

我想 Ruby 也是如此。Ruby 可是具有豐富的「不一致」呀...

對於這一點,我想我還是得感謝 Rails 讓我體會到這些。

3 retries:

puivujh said...

(膜拜)

Andrew Liu said...

Java 就是另外一個極端了
用很強的 IDE 來保持一致性

寫 Java 都不太需要思考
就直接叫出 completion list 挑一個用就好了

Lin Jen-Shin (godfat) said...

objective-c + xcode 也差不多 @@

不過我還是比較喜歡語言的方式,而非環境的方式

Post a Comment

All texts are licensed under CC Attribution 3.0