良好程式碼的優點大同小異。
不好的程式碼的糙點卻各有巧妙之處。
在此引用大師 Donald A. Norman《The Psychopathology of Everyday Thing》(中譯: 《設計的心理學》翻譯品質很好哦)用來分析設計的框架,試著用來分析「程式設計的開發體驗」看看!!
優良設計的兩個重要特點
如果是複雜的設備,就要用手冊或指南。簡單的設備不用。
書中含蓋包含複雜與簡單的設計。程式開發的對象,往往是複雜的設備,試著以這本書的方式來看看程式碼在這個面相有什麼有趣的觀點吧。
書中主張 Human-center design,重視人類需求、能力、行為用設計滿足這些條件。
這篇試著主張 Developer-center design,重視開發者需求、能力、行為用設計滿足這些條件。(哈哈~~ ),不過這一類的設計書,簡單的假設都是「使用者是笨蛋」,想對程式品質講究的小弟我,應該也是腦容量小、記憶力不好,但是又試圖的可以處理一整個系統性的專案,這樣的技巧確實可以放大處理專案的大小。
書中提出了幾個 Human-center design 的原則
- 避免過早定義問題
- 利用重複漸進的方式來趨近問題
- 快速的測試設計的想法
- 每次測試後修改設計方式和問題的定義
結果上,會是真正滿足人們需求的產品。
在嚴格規定的 時間、成本、資源 這將會是一種挑戰。
其實以軟體開發而言,這不就是敏捷精神?快速迭代、建立回饋、即時修正方向....
當我們與產品互動,需要弄清楚如何使用它。
包括發現它能做什麼事以及該如何操作: 稱為「可發現性」
它包括五個基本的心理觀念,再加一個對系統的「概念模型」
第 6. 點,呼應了《沒有銀彈》中提到的「整體概念性」的重要。
簡單的說: 「能夠派上用場的用法」
環境中豐富的訊息,有著「如何互動」這一類的訊息,稱為「預設用途」。即使沒有察覺,預設用途還是存在。而被人為設計出來的「預設用途易視性」是最重要的。
在程式碼,常常看見不照文件寫,自己發明語法的巧妙用法,都算是在「預設用途」的範圍裡,也就是說,只要能用、complier 編得過、可以執行,都能算在預設用途中。
當然,也包含了程式碼中未定義行為!
開發者可以辨別開發工具、程式語言,好的預設用途和不好的預設用途,往往是很重要的能力。
不然也就不會有《JavaScript 優良的部份》這樣的書了,對吧?^^
向人傳達適當的行為方式的指示方法
強調、提示 的預設用途
有價值的線索
在此開發者是可以有意識的設計程式碼語意,讓建構式處理初始化的問題、判斷是否要處理例外處理,解構式處理資源釋放的問題,而且避免發生任何例外。
而語意這件事,必須要了解非常多的東西。
如何適當的暗示或無感的讓開發者重複使用你設計的功能!可以說是相當的重要呀。
看一個 C++ 的例子
像是下面的程式碼,這個 getName
的 method。前後加上 const
是什麼意思呢?
class Human {
public:
const string getName () const;
};
每個語言都有自己的細節可以處理指意問題。
在此是 C++ 的技巧。前面的 const
意思是回傳的字串不可以修改,取得別人的名字,怎麼還可以改呢?而後面的 const
意思是一種保證,一種「實作修改 Human 物件屬性的保證」。語言細節附帶的語意,是程式設計師可以掌握而運用的。而且運用得好,往往會帶給你意想不到的幫助呀!!
因為要使用 C++ STL 的 sort,裡面的 callback 呼叫的必須是 const function。所以這樣的細節掌握在你偶爾想要用 sort 時,感覺到過去的自己有如神助一般美妙的設計。
而不好的程式碼
開發者不熟悉或尚未掌握夠多的語言細節,它所表達出來的語意與語言本身沒有「詞曲咬合」而造成某一種誤解。
string initialName() {
//...
// not thing about naming
return "chris";
}
class Human {
string name;
public:
Human:name(initialName()){
//...
};
};
在這個例子 initialName
就是一種不良語意,初始化 name
是用另一個全域的 function 處理,還順便處理了其它的事。這種事不是應該放在建構式嗎?
元素集合對元素集合之間的關係,此關係為一種概念模型
依文化而改變「自然對應」
這個概念,可以用函數的映射來理解它。
主要要處理的有三件事的關係,引用到開發者行為可以這麼舉例
概念 | 開發者行為 |
---|---|
控制介面 | 函數介面 |
人的行動 | 呼叫函數 |
想得到的結果 | 執行結果 |
舉一個原生 JavaScript 和 jQuery 的例子。
原生 JS 的 ES6 版本中,有一個叫 querySelectorAll
的功能,會得到 NodeList
型別的 array-like
物件,可以這麼使用
document.querySelectorAll('div') // return NodeList
而簡潔聞名的 jQuery ,有一個著名的 $()
的功能,會得到 jQuery 物件,可以這麼使用
$('div')
而它們,都是 array-like 也就是「像是 Array 的物件」有 method 可以丟一個 callback。
NodeList
vs Array
用 NodeList
跑 forEach
document.querySelectorAll('div').forEach((item, index) => console.log(index))
用 Array
跑 forEach
利用 [...arrayLike]
得到「真。Array」
[...document.querySelectorAll('div')].forEach((item, index) => console.log(index))
可以得到一樣的結果
jQuery 物件
vs Array
用 jQuery
跑 map
$('div').map((item, index) => console.log(index))
結果竟然不是印出 index
?
是印出內容
用 Array
跑 map
利用 [...arrayLike]
得到「真。Array」
[...$('div')].map((item, index) => console.log(index))
這次是印出索引值
初學 jQuery 與原生 JavaScript 的朋友們,一定常常遇到這問題吧!
每次都要查一下。[1]才發現原來它的 callback 是長這樣,開始懷疑自己是不是老是記不住、每次都查是不是不適合寫程式。
就像是「使用者經驗」領域裡,常常看到的「讓人們使用時覺得自己很蠢」類似的描述。[2]
在「如何寫高品質 function」使用三篇介紹如何設計良好的函式,其中參數的安排上,如果有良好的對應慣例,就可以省下一些記憶力更專注的面對問題。當然 jQuery 也許有它的用意,但是糙 code 之巧妙就是沒有特別想過就可以設計更花費開發者意識與注意力的程式碼。
[1]: jQuery.map(), jQuery.map(array, callback)
[2]: 《使用者經驗要素》, Ch1 使用者經驗為何如此重要, p.10