這個標題有語帶雙關的意味自沧,是如何 “寫好” 程序?還是如何寫 “好程序”树瞭?如果是問如何 “寫好” 程序拇厢,有很多人可能會下意識地說:很簡單啊,就是把源代碼打好移迫,按下運行程序旺嬉、沒有問題就 “寫好” 了啊厨埋!但是,“寫好” 了就是 “好程序” 嗎捐顷?
好程序具體的標準
“好程序” 的標準其實很主觀荡陷,因為程序好不好最終還是要回歸到是否有符合用戶的需求,畢竟程序開發(fā)的目的是要給 “人” 使用的迅涮,所以使用的人主觀覺得好用就是好程序废赞。所謂的 “臺上一分鐘,臺下十年功”叮姑,要怎么樣讓用戶覺得程序好用唉地,在開發(fā)的過程之中就隱藏著許多的細節(jié),這些細節(jié)牽動著用戶對程序的觀感传透。如果要把整個開發(fā)的過程耘沼,也就是軟件的生命周期 (SDLC) 中相關細節(jié)都列入文章的內(nèi)容,所涵蓋的范圍太過廣泛朱盐。所以在這篇文章中先撇除需求和設計間落差的問題群嗤,單就程序設計來看如何寫 “好程序” 。
程序設計通常都被安排在需求分析的活動之后進行兵琳,畢竟總要先知道用戶要的是什么樣的功能才有辦法著手寫程序狂秘。需求確立后骇径,大多是經(jīng)過軟件架構(gòu)、軟件設計者春、程序編碼等過程破衔,也就是要先制作藍圖、才開始蓋房子钱烟。只是在實際操作上晰筛,如果是一個規(guī)模不大的團隊,把這些過程混在一起忠售、且戰(zhàn)且走传惠、邊寫邊設計的也還不在少數(shù)。
由于是接在需求分析之后的活動稻扬,在程序設計這個階段對需求并沒有太多左右的能力卦方,說的不值錢一點,這個階段就只是把需求文件里用自然語言描述的內(nèi)容轉(zhuǎn)譯為編程語言泰佳。當然最終產(chǎn)出的標的物就是程序本身盼砍,代表著用戶的期盼、結(jié)案的關鍵逝她、收錢的依據(jù)浇坐!
翻譯三原則
在自然語言的翻譯中有 “信、達黔宛、雅” 三大原則用來衡量翻譯結(jié)果的良窳近刘。照剛才不值錢的說法,寫程序也勉強算得上是翻譯事業(yè)臀晃,是不是也適用這三大原則觉渴?符合這三大原則精神的程序是不是就是好程序?
首先徽惋,以第一項 “信” 的原則來看案淋,就如同這項原則的精神,翻譯的結(jié)果應該要忠實地呈現(xiàn)原文的意思险绘。換句話來說踢京,按圖施工本來就是放諸四海皆準的道理,若是不按圖施工又振振有詞、一堆借口,會被貼上 “無良” 的標簽應該也是意料中之事冻记。所以组力,無庸置疑地程序?qū)懗鰜淼慕Y(jié)果本來就應該要符合需求的敘述,不然就等著被 PM、用戶、業(yè)主打槍卸留,最后落得收不到錢白华、做白工的下場慨默。
看起來第一項 “信” 的原則還滿適用的,接下來第二項 “達” 的原則是期望翻譯的結(jié)果能夠語句通順弧腥。雖然源代碼不像自然語言有各式各樣的變化厦取,充其量不過是一大堆的 if-else 判斷式和回圈的組合,要求通順似乎沒有太大的意義管搪。但不管是早期的程序式或是現(xiàn)今流行的面向?qū)ο蟪绦?/a>設計風格虾攻,適切地分割與配置源代碼區(qū)塊,讓運行的順序合理更鲁、順暢一點霎箍,以便能增加運行效率,是編寫源代碼的一項重要工作澡为。像是面向?qū)ο蟪绦?/a>中的 Design Pattern 就是為了讓開發(fā)人員在遭遇已知的設計情境時漂坏,借由其他人的經(jīng)驗快速地決定源代碼最佳分配方案。以此來看媒至,似乎 “達” 的精神也可以用在好程序的標準上顶别。
最后一項 “雅” 的原則是期望選用的詞句能夠盡可能地優(yōu)雅,這對繕打源代碼這項工作來說就有點難度了拒啰。會來寫程序當工程師大多不是文青的料驯绎,在進度壓力之下應該也沒有興致對著機器附庸風雅。況且編程語言寫來寫去也就那幾個保留字谋旦,能夠用來展現(xiàn)優(yōu)雅的空間非常有限剩失。有人可能會引申 “雅” 就是讓源代碼愈簡潔愈好,源代碼能夠縮就縮册着、能夠少打就絕不占版面赴叹。但我卻不是很贊同這種評斷程序好壞的觀點,我倒覺得源代碼應該要能清楚地表達運行上的意圖指蚜,所以不應該是字愈少愈好。當然也不是無意義地增加源代碼來充版面涨椒,畢竟源代碼字數(shù)的多寡不是重點摊鸡,而是能不能提供足夠的資訊讓閱讀的人了解程序運作的過程。在之前發(fā)表的 “軟件開發(fā)團隊管理雜談” 文章中有提到相關的內(nèi)容蚕冬,有興趣的人可以參考一下免猾。所以 “雅” 要成為好程序的標準應該要解釋為:源代碼的寫作風格是否夠優(yōu)雅。
“信囤热、達猎提、雅” 這三個原則看下來好像都可以成為好程序的標準,但仔細想想又似乎有一些不足旁蔼,沒有辦法精確地掌握軟件開發(fā)工作的一些特性锨苏。就像之前提到的疙教,整個程序設計的過程并不是只有把源代碼打進去,還包含了許多在 “翻譯” 前周邊的構(gòu)思伞租、規(guī)劃與設計等等的工作贞谓,以便讓需求翻譯結(jié)果能夠符合 “信、達葵诈、雅” 裸弦。這些工作雖然不像源代碼能直接影響程序運作,但也和程序有一定程度的關連作喘,像是:軟件架構(gòu)理疙、介面的配置、操作動線的規(guī)劃泞坦、數(shù)據(jù)庫的調(diào)校窖贤、資訊安全考量以及非功能性的需求等等。
好程序的四項原則
所以我依據(jù)過去的工作經(jīng)驗和對于這個產(chǎn)業(yè)特性的了解暇矫,歸納出了好程序應該具備有 “穩(wěn)主之、引、速李根、諧” 四項原則槽奕。
- “穩(wěn)” 就是讓程序不容易出現(xiàn)錯誤。
- “引” 就是讓程序可以直覺地房轿、方便地被操作粤攒。
- “速” 就是讓程序有效率地運行。
- “諧” 就是讓程序看起來不覺得礙眼囱持。
我在設定這四項原則時夯接,是依據(jù)取舍的優(yōu)先次序來排序的,也就是當原則間有沖突時纷妆、沒有辦法同時套用盔几,則舍棄次序較后面的原則,以次序較前面的原則為主掩幢。舉例來說逊拍,在實際進行開發(fā)工作的過程中經(jīng)常會出現(xiàn):如果采用了 “增加檢查點” 之類比較保險的作法,但是卻會因要運行的檢查動作增加际邻、讓程序在運行時的效率變得比較差芯丧,或是程序在操作時變得比較不直覺。這時應以確保程序不容易出錯為優(yōu)先世曾、再來是考量如何讓程序的操作更容易了解缨恒、最后才是針對前二項考量的結(jié)果對程序進行效能上的調(diào)整。
接下來就分別對這四項原則做更進一步的說明。
穩(wěn):讓程序不容易出現(xiàn)錯誤
這是四個原則中最優(yōu)先的原則骗露,不論程序的功能再強大岭佳、畫面再華麗、運算再神速椒袍,如果程序一碰就崩潰驼唱,豈不是根本沒有人可以體驗這些程序帶來的優(yōu)點!
這個原則如同字面上的意義 - 不容易出錯驹暑,對于不可預期的情況要有適當?shù)奶幚砻悼遥苊獬绦驘o預警的中止, “程序無預警的中止” 用移動平臺的說法就是 “閃退” 优俘。在使用經(jīng)驗上最讓人生氣的大概就是數(shù)據(jù)輸入到一半程序消失京办、所有的內(nèi)容全部不見,然后要再輸入一次帆焕。尤其是要輸入的數(shù)據(jù)數(shù)量眾多惭婿、輸入很費時耗神之下,程序瞬間消失在眼前的情況一出現(xiàn)叶雹,實在很難不讓人理智斷線财饥。
要讓程序不容易出錯,講起來很輕巧折晦,但卻是個很復雜的課題钥星,這是要靠一連串對細節(jié)的堅持所累積出來的結(jié)果。程序開發(fā)是一種講求精確的活動满着,這主要是因為目前程序最基本的運作基礎就是二進位谦炒,不是零就是一,沒有第三種可能风喇。而程序的邏輯判斷也只有 “是” 與 “否” 宁改,所以在寫程序時一定要明確的區(qū)分各種數(shù)據(jù)的狀態(tài)并做對應處置。一但情況超出程序可處理的范圍就會產(chǎn)生例外魂莫、影響程序的判斷还蹲,最終極的結(jié)果就是被操作系統(tǒng)中斷程序的運行、出現(xiàn)閃退的情況耙考。
沒閃退就沒事嗎秽誊?其實,程序被操作系統(tǒng)中止感覺上很嚴重琳骡,但卻是最幸運的結(jié)果,最少錯誤沒有持續(xù)地擴大讼溺。在出現(xiàn)了不可預期的情況楣号、程序仍然繼續(xù)運作,代表沒被考慮到的數(shù)據(jù)一直在運算、儲存著炫狱,對程序和數(shù)據(jù)的影響更是難料藻懒。當運作的是無關緊要的數(shù)據(jù),頂多只是垃圾進垃圾出视译,等有人看到再校正就沒事了嬉荆。但如果是商業(yè)價值很高的資訊呢?再加上經(jīng)過很長一段時間才被發(fā)現(xiàn)呢酷含?數(shù)據(jù)的錯誤可能因為連鎖反應鄙早,已被延伸、擴展到許多的周邊數(shù)據(jù)中椅亚,形成一個錯綜復雜的結(jié)構(gòu)限番、完全無法修正或回復。
說得太虛無飄渺呀舔,感覺不到嚴重性嗎弥虐?假設今天開發(fā)的是加班系統(tǒng),你寫的程序沒有考慮到某個工時組合媚赖,于是在這種組合出現(xiàn)的情況之下會把應該是 2 倍率的部份全部誤以 1.5 來計算霜瘪。原本是個很罕見的工時組合,但在工作型態(tài)的改變之下開始持續(xù)出現(xiàn)惧磺。問題一直到幾個月之后才終于被某個錙銖必較的雇員發(fā)現(xiàn)颖对,加班費竟然少給了!是變相苛扣嗎豺妓?不是的話惜互,當然是要重算清楚再補發(fā)!但麻煩的是琳拭,發(fā)現(xiàn)的時間點已跨過會計年度训堆,年度結(jié)算已經(jīng)完成、上年度會計數(shù)據(jù)已經(jīng)被涷結(jié)白嘁、主管提報數(shù)據(jù)已依據(jù)錯誤的加班信息送出坑鱼、敘薪晉用獎懲都已底定,更糟糕的是財報已經(jīng)被公布...一堆遠比補算加班費要麻煩的問題待處理絮缅。
光是只以修正錯誤的直接成本來看鲁沥,如果是負責寫程序的人應該會覺得:為什么程序當初不直接當?shù)艟退懔耍∵@個例子對負責的開發(fā)人員來說是不可承受之重耕魄,但在規(guī)模上不過就只是一個企業(yè)茶壺內(nèi)的風暴画恰,隨著程序使用的范圍加大,不難想象還有可能會有更嚴重的結(jié)果吸奴。
所以 “穩(wěn)” 是指程序的可靠度允扇,也就是質(zhì)量缠局,而不僅僅只是程序運作時的可用性。然而考润,穩(wěn)就是可靠度愈高愈好狭园,最好達到百分之一百嗎?理想上是糊治!但現(xiàn)實上唱矛,有負責過測試工作的人應該可以理解,不是不愿意井辜,絕大部份瓶頸都是高層不同意投入達成百分之百可靠度所需的成本绎谦。也的確,要達成百分之百可靠度的成本很有可能會遠高于收益抑胎,是非常不符合經(jīng)濟效益的一種做法燥滑。
可靠度通常都是妥協(xié)之下的結(jié)果,在可接受的成本下讓可靠度達到最大化阿逃。而要提升程序的可靠度可分二個層面來看铭拧,一是開發(fā)工作的質(zhì)量以維持程序運作時的可用性,一是源代碼的可維護性以延長程序的生命力恃锉。
開發(fā)工作的質(zhì)量包含了開發(fā)人員需具備足夠的知識來預測并回避可能出現(xiàn)的問題搀菩,并且對需求內(nèi)容要有足夠的自主意識,而不是照單全收破托,再來就是看測試和驗證的功夫肪跋。這里所謂的知識并不是光指會使用異常處理的語法,有時候一知半解是很可怕的土砂,盲目地增加異常處理州既,有的時候反而可能是造成程序崩潰的原因。更何況攔到異常并不是處理問題的結(jié)束萝映,而是處理問題的開始吴叶。而自主意識是指對于需求的內(nèi)容要有反思、辨證的能力序臂,寫需求的也是人蚌卤,哪有吃芝麻不掉燒餅的。這句諺語并不是記反了而是真的發(fā)生過奥秆,我就曾遇過好幾份需求內(nèi)容是對芝麻小事鉅細靡遺的描述逊彭,唯獨就漏了燒餅。
源代碼的可維護性與程序的生命周期息息相關构订,如果把程序看成是有生命的個體侮叮,當程序被需要、有在使用才算是活的悼瘾。但人的需求是會改變的签赃,如果程序沒有辦法跟上需求的變化做出調(diào)整谷异,程序就逐漸變得難以使用,代表著程序開始走向死亡锦聊。一直到最后因為所有的功能都不符合需求而被下線,成為生命終結(jié)的程序箩绍。
在需求變遷的過程中孔庭,源代碼的可維護性就成了左右程序生命的關鍵。源代碼的可維護性愈低材蛛,跟上需求變化的成本就愈高圆到,背離需求的可能性就愈大。當碰到變量命名毫無邏輯卑吭、排版混亂芽淡、源代碼次序怪異、沒有或錯誤注釋的程序時豆赏,修改的人如果不是原作者挣菲,就需要花很多的心神、像看推理小說一般地掷邦,去抽絲剝繭讀懂盤根錯結(jié)的程序白胀。或是類似的源代碼片段到處都有抚岗,像是有人施展了多重影分身之術或杠,修正一個問題要改上數(shù)次的源代碼,還完全不知道程序改了會出現(xiàn)什么副作用宣蔚。有改過這種程序的人都應該有共同的心情向抢,會有一股想打掉重練的沖動,但礙于進度的壓力落入了進退維谷的窘境胚委,不打掉揪心挟鸠、但重練卻會傷肝。
有人會以 “寫出別人看不懂的程序” 為榮篷扩,但我覺得 “寫出讓人容易了解的源代碼” 境界更高兄猩。要不要做取決于心態(tài),能做到什么程度端看個人修為鉴未。但也并非無跡可循枢冤,開發(fā)人員間的默契是增加源代碼可維護性的方法之一。默契應該就是一種相互預測意圖的互動過程铜秆,明文規(guī)定則是最有效形成默契的方式淹真,這也就是為什么需要有 Coding Conventions。接下來要做的就是在團隊 Coding Conventions 框架下连茧,以命名核蘸、注釋巍糯、排版等方式來建立 “雅” 所提到的撰寫風格。
基本上可以朝以下幾個方向來進行:
- 為增加閱讀上的便利性客扎,源代碼應保持簡潔祟峦、一致性、可預測性徙鱼,讓閱讀者能以合理的邏輯來推演宅楞,避免過于跳躍式的邏輯。
- 延續(xù)上一點袱吆,在撰寫源代碼前厌衙,先思考如何配置源代碼,將源代碼的重用性及擴充性在合理的成本下绞绒,達到最大的利用范圍婶希。
- 源代碼最好能明確地表達程序的意圖,減少使用過于精簡蓬衡、合并或隱晦的語法喻杈,多輔以注釋說明。
- 源代碼注釋應該是明確撤蟆、有效的奕塑,不只是要補充說明程序運行了什么動作,也應該要解釋程序設計背后的原因家肯。
就以上的幾點龄砰,我覺得撰寫源代碼其實和管理團隊很像,跳脫不了把源代碼分配到適當?shù)牡胤教忠拢屍涓魉酒渎毣慌铩⒈苊庵毓さ鹊鹊目剂俊?/p>
引:讓程序可以直覺地、方便地被操作
“引” 指的是導引反镇,用戶界面是用戶接觸程序的第一線固蚤,所以用戶在評價程序好壞時,往往用戶界面占了很大的因素歹茶。最完美的用戶界面設計應該是讓用戶不需要靠任何外在文件的說明夕玩,在看到畫面時就能夠直覺地反應要進行什么樣的動作,以便驅(qū)使程序達成想要的結(jié)果惊豺。也就是說程序不只是要開發(fā)功能燎孟,同時也要思考如何引導用戶來操作功能,才能讓程序發(fā)揮出功效尸昧。就像是前一個原則提到的揩页,程序的功能再強大,如果不能被使用烹俗,那這些功能的意義不過就是一堆機械碼罷了爆侣。
不過萍程,“最完美的界面” 定義上要附加一個但書,那就是用戶要具備操作軟件的對應知識兔仰。只有開發(fā)程序的人和用戶在共同的知識基礎下茫负,所謂最完美的界面才有意義。否則乎赴,再怎么調(diào)校朽褪,都不可能會有 “不需要靠任何外在文件的說明就可以直覺地使用” 這種情況出現(xiàn)。像是要一個沒有會計背景的人去操作會計軟件无虚,就算是在畫面中加上各式各樣的使用指引,要能順利地操作基本上是天方夜譚衍锚。
在畫面上配置輸入或顯示的控件和室內(nèi)設計的概念很接近友题,有些室內(nèi)設計在規(guī)劃時往往太著重主觀的視覺效果來討好業(yè)主,卻犧牲了生活中動線的細節(jié)戴质。往往要等到施工完成之后入住了度宦,才發(fā)現(xiàn)原來就只是好看而已,在使用上卻是另外一回事告匠。像是冰箱開門的方向沒考慮好戈抄,冰箱打開后的門會擋在流理臺和冰箱之間,以致于拿取食材時要先把冰箱的門關上之后后专,才能把食材放到流理臺上划鸽,食材一多就要反覆的動作,使用上非常地不方便戚哎。
操作的動線和排列畫面的潛規(guī)則
所以導引的第一個重點應該是規(guī)劃用戶操作的動線裸诽,在現(xiàn)今主流 Window 操作系統(tǒng)的制約之下,大家都習慣由窗口的左上往右下開始了解畫面的構(gòu)成型凳。所以比較常見的動線規(guī)劃手法都是重要的畫面元素排在靠上丈冬、靠左的位置,并按重要性依序排列甘畅,動作置于靠下埂蕊、靠右的位置,也就是按鈕通常都擺在這樣的位置上疏唾。借此來導引用戶蓄氧,先把重要的資訊輸入完成之后,再來選擇要觸發(fā)的動作荸实。
在排列畫面元素的時候匀们,可以增加使用 Frame 或分隔線區(qū)分不同特性的內(nèi)容,讓關連性高的資訊形成一個群組准给。這樣的方式可以導引用戶歸納畫面的資訊泄朴,快速地在心里形成一個操作步驟的概念重抖,達到直覺使用的目標。
如果要輸入的數(shù)據(jù)數(shù)量較多的時候祖灰,采用分頁的方式也很常見钟沛。比較重要的數(shù)據(jù)放在較前的頁次,逐頁地導引用戶進行信息的輸入局扶,并且在觸發(fā)動作前提供檢視的畫面恨统,來供用戶確認是否輸入正確。有在使用線上購物的人三妈,對于這樣的操作模式應該不陌生才對畜埋。
類似的技巧五花八門,在此就不一一列舉畴蒲。但左上右下這個模式是絕對的嗎悠鞍?我想不是,就我所知道的模燥,阿拉伯語系的用戶界面就不是咖祭,是一個和我們熟悉的習慣完全左右相反的世界。這也帶出另外一個議題是用戶界面設計不應該是一體適用蔫骂,而是要依據(jù)使用的對象來量身訂做么翰,像是考慮到使用對象的生理狀況、操作習慣辽旋、生活背景浩嫌、成長文化、使用情境等因素來做適當?shù)恼{(diào)整戴已。
有一個全世界很常用的程序可以用來做為負面的例子固该,那就是拒老的 Facebook Android App。也許是 Facebook 產(chǎn)品規(guī)劃人員自詡這是年輕人使用的軟件糖儡,所以完全不體諒年長者的生理需求伐坏,顯示的字型大小預設都是年長者沒有辦法順利閱讀的大小。這就算了握联,竟然還限制程序不隨著系統(tǒng)的字型設定而改變桦沉,App 本身又沒有調(diào)整的功能(也許是藏在一個我找不到的位置),最后都只能是放棄使用 App 尋找替代方案來解決年長者的不便金闽。
Facebook 的這個生理狀況類型的問題如果是在 Windows 平臺上纯露,也許就不是大問題,最少還可以調(diào)個分辨率或是買個無敵大的屏幕代芜。同樣地埠褪,在使用情境的類型上,界面的平臺不同,設計的細節(jié)就必須要再調(diào)整钞速。例如網(wǎng)站有一陣子很流行使用 Hover 的效果贷掖,就是讓鼠標的光標移到特定的位置就會有一些畫面上的特效,像是跳出菜單供進一步點選渴语。但想要讓這樣的網(wǎng)站也能在移動平臺上瀏覽苹威,就可能會造成很大的使用障礙,除非是你有氣功驾凶,才有可能讓觸控屏幕偵測試你的手指懸空移到了可以 Hover 的位置牙甫。
曾經(jīng)還有遇過一個和操作習慣有關的案例,現(xiàn)在的人如果是由 Windows 開始接觸電腦调违,大概都很習慣鼠標的操作模式窟哺,所以在操作以鼠標為主的界面設計不會感到有什么困擾。而在協(xié)助經(jīng)歷過 Clipper 時代的資深會計人員開發(fā)系統(tǒng)時卻是截然不同的體驗技肩,當時 Clipper 還是運作在 DOS 之下脏答、鼠標還不是主流的輸入設備。輸入的工作都只靠鍵盤來完成亩鬼,控件的切換靠 Tab 或 Enter 鍵來觸發(fā)。也因為工作型態(tài)的關系阿蝶,所以他們一般都練就了盯著紙張進行數(shù)據(jù)盲打的功夫雳锋。對他們來說使用鼠標是一種工作上的阻礙。一來手要在鍵盤和鼠標之間移動羡洁,二來使用鼠標一定要移動目光回到屏幕上玷过、不可能盲移(有練就這等神功的高人請讓我知道,以便立碑膜拜)筑煮,讓原本的順暢的工作節(jié)奏變得很沒效率辛蚊。
當然只要設計得當,在 Windows 下還是可以用 DOS 的操作模式來使用程序真仲。只是遙想 Visual Basic 這種 RAD 剛出來的年代袋马,開發(fā)人員對于只要拖拉就可以完成程序的革命性變化感到興奮,以為控件放定位靠著鼠標就可以天下無敵了秸应。事實上果實并不甜美虑凛,但嘗到苦果的卻是這群資深的會計人員,因為一般開發(fā)人員都會忽略了所有可接受 Focus 的控件屬性清單里都有一個叫 TabIndex 的屬性软啼。當用戶按下 Tab 期望光標依序跳到下一個控件時桑谍,光標卻像打地鼠一般地亂跳,以致于用戶被迫要使用 “鼠標單擊” 這種沒效率的輸入模式祸挪,這就是很典型的沒考慮操作習慣的案例锣披。
如魔術表演般吸引用戶的目光
第二項重點是吸引用戶的目光,使用這個觀念最極致的當屬魔術表演,不同的是魔術是以誤導為操作的方向雹仿,和這里說的引導是相反的效果增热。常見的舞臺魔術為了要制造驚喜的心理感受触机,表演者會使用眼神重虑、言語、肢體嵌灰、聲光票顾、環(huán)境等花招來讓觀眾聚焦在特定的角落础浮,同時間會在不受觀注的角落準備下一步要呈現(xiàn)的內(nèi)容。當一個觀眾沒有預期到的結(jié)果突然出現(xiàn)奠骄,就會產(chǎn)生不可置信的心理落差豆同。
在操作介面的設計中,則是要讓用戶把目光專注在重要的操作元素上含鳞,例如使用動畫影锈、圖像、高反差的顏色來引起用戶的注意蝉绷。有使用過 Android 平臺上新的 Material Design 的人應該會發(fā)現(xiàn)鸭廷,在 Gmail 畫面右下角會出現(xiàn)一個醒目的圓形圖案,點了之后就可以開始撰寫新電郵熔吗。我想這樣設計的概念是因為進信箱的 App 后辆床,不是要讀信就是要發(fā)信。要讀信的話所有收到的信已經(jīng)在畫面的清單里桅狠,要看哪一封就點哪一封讼载。然后就剩下發(fā)信這一個重點功能,所以使用明顯的色調(diào)來吸引你的注意中跌,告訴你要發(fā)信點這里就對了咨堤!
導引不見得要使用顏色、圖示之類這么噯昧的方式漩符,直接了當?shù)挠梦淖謥黻愂鲆膊皇遣恍幸淮袷前粹o上搭配的文字就是最明確的導引,直接告訴你按下按鈕會產(chǎn)生什么效果嗜暴。還有另外一個以前很辰蛑停看到使用文字的例子,記得在 Windows XP 的年代灼伤,只要剛安裝完 Windows XP 進入桌面就會有一個很明顯的特效触徐,并且有文字告訴你要開始使用 Windows 就從左下方的開始按鈕開始 (嗯,真繞舌)狐赡。
告知但不要恐嚇
第三項重點是讓用戶了解程序發(fā)生了什么事撞鹉,但卻不要造成用戶的恐慌。以最常見的訊息顯示來說,有的開發(fā)人員會習慣在攔到異常之后就把信息寫入 Log 內(nèi)鸟雏,就當是完成異常處理了享郊。對用戶來說,看到的是程序沒有任何異樣孝鹊,如果今天是新增一筆數(shù)據(jù)炊琉,發(fā)生錯誤了,但程序并沒有回饋給用戶又活。用戶當然以為數(shù)據(jù)被存起來了苔咪,哪知下次要編輯時卻找不到這一筆數(shù)據(jù),才發(fā)現(xiàn)被騙了柳骄,這時的心情絕對好不到哪里去团赏!
另外還有一個很常見的情況是,用戶觸動了程序要求進行數(shù)據(jù)處理耐薯,程序也依指令開始動作舔清。但這時候用戶卻發(fā)現(xiàn)程序沒反應了,好像已經(jīng)在處理了曲初、又好像當?shù)袅颂遐耍恢涝摰却窟€是該強制中止程序臼婆?(噯昧總是讓人受盡委屈...) 會有這樣的現(xiàn)象大多起源于在開發(fā)的階段营密,開發(fā)人員在對功能進行測試時都以為瞬間工作就會完成了,殊不知這是測試用數(shù)據(jù)量不足目锭、環(huán)境單純的假象。等到真正上線使用纷捞,運行沒有瞬間完成痢虹,反倒是畫面就像是灌了液態(tài)氮一樣,瞬間凍結(jié)了主儡!
為了避免這樣的情況奖唯,在二大移動平臺 iOS 和 Android 上都會建議開發(fā)人員不要在 Main Thread 也就是 UI Thread 上進行數(shù)據(jù)處理(1, 2),甚至會限制不得在 Main Thread 上運行遠端呼叫這種不確定何時會結(jié)束的程序糜值。如果一定要封鎖畫面丰捷,也許是為了要避免用戶進一步的輸入干擾數(shù)據(jù)處理的進行,這時也應該提供工作進度的顯示資訊寂汇,讓用戶可以清楚地知道工作的進度和預估完成的時間病往,最低限度也要讓用戶知道程序有在動作。在可允許的情況下骄瓣,最好也能提供取消的機制停巷,讓用戶決定是不是要繼續(xù)等待沒有完成的工作。
然而,過猶不及也不是件好事畔勤,像是把底層的信息直接顯示給用戶蕾各。這種信息看起來是很專業(yè),但對用戶來說感受到的可能只有恐慌庆揪。因為用戶大多不可能理解底層信息的意義式曲,相對地就不可能知道要如何處理,慌張的反應是必然的缸榛×咝撸或者是過于頻繁的信息顯示,不管什么雞毛蒜皮的小事都跳個信息出來仔掸,對于使用經(jīng)驗并不會有太大的幫助脆贵。所以信息應該要經(jīng)過適當?shù)暮Y選、包裝起暮,最好不要只是通知用戶發(fā)生了什么事卖氨,而是建議用戶可以怎么做,讓用戶在操作程序時的不安全感降到最低负懦。
貼心地提供的輔助
第四項重點是方便性筒捺,程序并不是只要功能具備就足夠了,是否能夠方便地操作也必須要被考量在內(nèi)纸厉。再以室內(nèi)設計來做例子系吭,如果在設置洗手臺時沒有考量到居住者的身高,雖然有洗手臺颗品、有水龍頭肯尺、有鏡子,而且功能都正常躯枢。但使用時因為設置的高度較低则吟,洗手臺的鏡子沒有辦法在站立時照到全身、臉被鏡子切掉一半锄蹂,所以每次檢視儀容時還得采半蹲的姿勢氓仲;或是洗碗槽高度不夠,以致洗碗時要一直弓著身體來遷就洗碗槽得糜,長久下來讓腰產(chǎn)生很大的負擔敬扛。
回到畫面的設計,以一個最常見的操作情境來看朝抖,輸入日期時讓你年月日一個字一個字輸入比較方便啥箭?還是跳出一個日歷畫面讓你點日期比較方便?很難說治宣!為什么捉蚤?如果是要輸入最近的日期抬驴,而日歷是以今天為基準點顯示日歷,那還滿方便的缆巧。但如果是要輸入出生年月日布持,又沒有辦法快速跳到指定的年份,對比較早出生的人來說那就是一場惡夢陕悬,這個時候我還寧愿一個字一個字打還比較有效率题暖,而這就是一個直覺但使用上不方便的例子。
不方便的操作會中斷用戶的注意力捉超,使得原本設定好的導引效果打了折扣胧卤,甚至是完全沒有達到目的。所以這幾項重點都是相輔相成的拼岳,必須要相互配合才能達到最大的導引效果枝誊,也才有可能讓用戶在沒有外在文件的協(xié)助之下,順利地使用程序所提供的介面惜纸。
速:讓程序有效率地運行
“速” 就是程序運行的速度叶撒,也就是盡可能地縮短運行的時間,有多快跑多快耐版。當然人的欲望是無止境的祠够,永遠不可能滿足用戶對速度的期望,所以重點就是要讓程序的運行效率達到最大化粪牲。
一但出現(xiàn)程序運行速度不如預期古瓤,當然就是做效能的檢測,找出運行速度的瓶頸點腺阳。如果程序有發(fā)現(xiàn)明確的效能瓶頸造成運行速度不理想落君,就針對這個部份做調(diào)校,一定會有顯著的改善效果呈現(xiàn)出來亭引。最擔心的是檢測沒有辦法區(qū)分出產(chǎn)生瓶頸的源代碼绎速,也就是慢是所有源代碼的共業(yè)。這時能做的大概只有逐一地對源代碼進行檢視痛侍、調(diào)整,其實也差不多就是重寫所有的源代碼魔市。
為了避免這種情況出現(xiàn)主届,當然最好是在編寫源代碼的時候就謹慎地選擇要輸入的源代碼。以下面的二段源代碼為例:
for (i = 0; i < n; i++) {
a = b + c;
d[i] = i * 2;
}
a = b + c;
for (i = 0; i < n; i++) {
d[i] = i * 2;
}
運行后達到的效果都一樣待德,但第一段的寫法在效能的表現(xiàn)和第二段相比是有差距的君丁。有些人可能會想,只不過是一行簡單的指令将宪,現(xiàn)在 CPU 都這么夠力绘闷,差不了多少時間橡庞!如果把這二段源代碼以時間復雜度的時間函數(shù)來表示,分別可以簡單地表示成 2N 及 N+1印蔗,把這二個方程序轉(zhuǎn)成圖形會得到二個斜率不同的直線扒最。隨著 N 的數(shù)值變大,二條線之間的垂直距離會愈來愈遠华嘹,也就是說就算 CPU 的運算能力再強大吧趣,只要循環(huán)的次數(shù)夠多,二段源代碼在運行時間上終究會出現(xiàn)不可忽視的時間差距耙厚。
這只是個簡化過的示范强挫,真實的世界中會出現(xiàn)的情況遠比這個示范要來得復雜許多,效能差距顯現(xiàn)的門檻就會低很多薛躬。但有經(jīng)驗的開發(fā)人員會犯這么明顯的錯嗎俯渤?就像剛才說的,范例中使用的是一個很簡單的循環(huán)結(jié)構(gòu)型宝,所以很輕易地就可以看出問題所在八匠。在復雜的循環(huán)結(jié)構(gòu)之下,是不是還能這么輕松地察覺程序的效能問題诡曙,可能就要打上個問號了臀叙!
再來看一個例子,在很多時候要達成需要指定的效果价卤,可能會有很多的選項劝萤,表面上看起來挑哪一種都無所謂,但常常會失之毫厘慎璧、差之千里床嫌。像是在 .NET Framework 里有 String 和 StringBuilder 二種可用來處理字符串的選項,如果要處理的字符串會出現(xiàn)頻繁地異動胸私,選擇 String 和 StringBuilder 在功能上都可以達成目的厌处,但在效能上卻有著明顯的差異。
所以如果不希望寫出來的程序永遠都比別人慢上一線岁疼,但又沒有什么明顯的效能瓶頸阔涉,就要開始注意細節(jié)上的效能考量,畢竟聚沙能夠成塔捷绒、滴水也能夠穿石瑰排。再來就是充分利用 CPU 運算的技術,最常見的就屬多線程或是并行計算暖侨。因應 CPU 的物理制限椭住,新一代的 CPU 開始朝向多核心的方向發(fā)展,也就是說現(xiàn)在的數(shù)據(jù)處理工廠都配備了多條生產(chǎn)線字逗。在處理數(shù)據(jù)的時候京郑,二條宅广、四條生產(chǎn)線同時處理數(shù)據(jù),絕對比只用一條生產(chǎn)線來處理數(shù)據(jù)要快上很多些举。
但使用多線程來設計程序絕對不是件容易的事跟狱,最少不像喝水那么簡單。如果是要同時處理多項不相關的數(shù)據(jù)金拒,那情況會比較單純兽肤,一種數(shù)據(jù)一條生產(chǎn)線、一個蘿卜一個坑绪抛,大家各做各的井水不犯河水资铡。反之,如果要處理很大量的單一數(shù)據(jù)幢码,又想要利用所有生產(chǎn)線的產(chǎn)能來縮短運行時間笤休,就可能會有工作如何切割、數(shù)據(jù)處理次序等問題需要煩惱症副。再進階一點店雅,因為數(shù)據(jù)都在同一個記憶體區(qū)塊里,所有的生產(chǎn)線同步進行會產(chǎn)生競爭冒險贞铣、可重入的情況闹啦,或是莫明其妙地進入無止盡相互等待的死鎖狀態(tài),這時程序就會出現(xiàn)呆滯的反應辕坝。如果想要偵察這種錯誤窍奋,出現(xiàn)問題的數(shù)據(jù)組合可能稍縱及逝,不是單純地設定斷點就能攔到酱畅,沒有一點想象和推理的能力琳袄,還有可能根本查不出問題的根源。
不過這細節(jié)已經(jīng)脫離了這個小節(jié)的內(nèi)容纺酸,牽涉到的是開發(fā)人員的素質(zhì)問題窖逗,會在之后的內(nèi)容說明。
這項原則為什么會排在 “引” 后面餐蔬?
當一個有提供進度信息碎紊、有取消按鈕但要運行二分鐘的程序,和一個運行時間只要一半樊诺,但運行期間畫面會凍結(jié)仗考、沒反應的程序,你選哪一個啄骇?我選前者痴鳄!
還記得求學時等公交車是個很深刻的記憶瘟斜,尤其是等末班車缸夹。學生時代沒有錢痪寻,打的一定是最后不得已的手段。這時候等末班公交車的心理就變得很微妙虽惭,等待是一種生活體驗橡类、不知道何時結(jié)束的等待則是一種折磨。在不確定公交車過站沒的情況下芽唇,這時能做的只有不住地望向公交車來的方向顾画,怕一個閃神被公交車給跑了,但又會懷疑末班公交車是否已經(jīng)過去了匆笤。如果是研侣,應該忍痛花錢改打的早點回家,但又怕公交車其實還沒過炮捧,一打的卻看到公交車在遠方緩緩地駛來庶诡,心里不甘,想著如果再多等一下就好了咆课。人在候車亭里末誓,心里會呈現(xiàn)一種膠著、煎熬的狀態(tài)书蚪,覺得度秒如年喇澡。
假設那時的候車亭有像現(xiàn)在這樣,提供智慧型站牌能夠知道公交車到底收班了沒殊校、或是有 App 可以用 GPS 查詢公交車位置晴玖,就不用遭受這種心理摧殘。類似的心理效果也會出現(xiàn)在程序的使用上箩艺,一個不知道多久的等待在心理認知上可能會比有限度等待的時間要久窜醉,縱使實際上短很多。再加上用戶可能會因為程序沒有回應艺谆,心里慌亂榨惰、急于改變現(xiàn)狀,所以干擾静汤、中斷運行琅催,以致程序根本就沒有機會完成工作,雖然他處理得比較快虫给。
基于這樣的理由藤抡,導引用戶當然要比增加程序效率來得重要,在調(diào)校程序效能之前應該先解決用戶感受上的問題抹估。
諧:讓程序看起來不覺得礙眼
“諧” 所想表達的概念是用戶界面在配置上的和諧缠黍,讓用戶在看到程序時能產(chǎn)生優(yōu)雅的感受,和 “雅” 有點異曲同工之妙药蜻,只是之前看 “雅” 的時候是比較專注在源代碼的部份瓷式。
程序的操作介面優(yōu)不優(yōu)雅也許太過抽象替饿、許多人沒有辦法在腦海中形成畫面,可是只要一拿 Android 早期的畫面風格和現(xiàn)在 Material Design 相比應該就會很有感贸典!一個很... “工程” 视卢,但另一個就給人有截然不同的感受,比較 “藝術” 廊驼。同樣是這幾個控件做的畫面配置据过,卻在套上不同的呈現(xiàn)手法后讓使用的感受上有很大的差別、使用的愉悅度大為提升妒挎。Apple 在這方面一直都是佼佼者绳锅,也難怪 iOS 的界面設計能讓這么多人擁護。
要做出讓人在視覺上感到舒服的畫面酝掩,是需要有美感及配色上的敏銳度榨呆,可惜這是天生的!如果你沒有天份庸队,又得不到視覺設計人員的奧援积蜻,除了多閱讀一些官方提供的設計準則、模仿專業(yè)之外彻消,還是有一些不需要天份就可以進行的細節(jié)竿拆,那就是 “對稱” 和 “平衡” 。
曾經(jīng)看過不少的本土自行開發(fā)的軟件宾尚,在功能上比起國外同類型的產(chǎn)品毫不遜色丙笋,但使用起來總覺得欠了點質(zhì)感。這是很可惜的一點煌贴,用廉價來形容可能太不厚道了御板,但的確會讓人有 “這應該是不同價位帶產(chǎn)品” 的念頭,即使和國外的軟件是同級的產(chǎn)品牛郑。會有這樣的差別怠肋,就我的觀察大部份是缺了對稱和平衡,像是有些會看到畫面中輸入框的字有的大淹朋、有的小笙各,但大小又沒有一定的邏輯;或是在一樣的字型大小的設定下础芍,有些輸入框比較緊貼著文字杈抢、有一些有留下不小的空間,很明顯地有胖瘦不一致仑性、排列沒有對齊的情況惶楼。
而有一些則是在固定大小的視窗中,把所有可以操作的控件集中在畫面的左上方,右方和下方留下一大塊空白的區(qū)域歼捐∧凼担可能是我沒慧根,我實在沒有辦法領會這樣設計的意圖窥岩,也許這其實是開發(fā)人員讓用戶在屏幕上書寫注記或貼上便利貼的空間?宰缤!
這樣的畫面配置結(jié)果颂翼,在使用時會讓人覺得礙眼、增加潛意識里使用程序的負面觀感慨灭。曾經(jīng)聽過有一個心理測試是測試者拿著一張點了一個黑點的白紙朦乏,問受測者看到了什么,大部份的人會回答黑點而不是白紙氧骤。所以人的大腦是很容易被操弄的呻疹,同時人的大腦會被對稱的東西吸引,于是保持畫面上的 “對稱” 和 “平衡” 就是讓大腦覺得愉悅很重要的潛規(guī)則筹陵。
雖然在大家的印象中微軟的 Windows 介面設計常常是被批評得一無是處刽锤,但其實仔細地觀察會發(fā)現(xiàn)在 Windows 中,畫面的設計是經(jīng)過很嚴謹?shù)囊?guī)范朦佩,包含了許多 “對稱” 和 “平衡” 的細節(jié)并思。以對話框為例,可以看到窗口的上语稠、下宋彼、左、右邊緣和最靠近邊緣的控件一律保持著一定的距離仙畦,包含控件與控件之間也使用相同的距離做為間隔输涕,當然輸入框不會有胖瘦不一致的情況。而所有的控件也都以一致的對齊規(guī)則排列著慨畸,例如橫向一定是對齊中心線莱坎、直向則是標題全都靠左切齊。在顯示文字時使用的是相同的字型寸士、在大小上除非有特殊的目的外也保持一致型奥。這些配置的方式,不只是在同一個窗口中碉京,就算是不同的窗口也維持著統(tǒng)一的風格厢汹。
這些細節(jié)組合出來的結(jié)果,也許不是讓人驚艷的蘋果經(jīng)驗谐宙,卻是扎扎實實地表現(xiàn)出大公司應該展現(xiàn)的水準烫葬,而這也是 “諧” 希望達到的效果。雖然這是四項原則中的最后一項,但卻是門檻最低的一項搭综,并不需要血統(tǒng)而是每一個人都可以辦到垢箕,只要訂下畫面配置的標準、按部就班兑巾,就可以打造出一個使用起來不礙眼的程序条获。