village

離開JavaScript新手村:Module Pattern

最近在開發「按讚」功能,也就是類似對商品列表「按讚」或是「加入我的最愛」之類的功能。

(本篇不討論server side與database實作,僅討論client side的JavaScript)

按讚的對象是衣服,於是我先開份js檔:

然後在所有需要「按讚」功能的頁面,加入這些程式碼:

寫完之後,再看了一眼,一股涼意上心頭,覺得這些程式碼真的很可悲。

  • 這只是把一串js code,用function隨便包一包、把功能隨便分開而已
  • 在所有需要「按讚功能」的頁面加上一行registerLikeButtonEvent(),感覺不是模組化(一點也不物件導向),依然停留在procedural programming(這邊執行一串code、再跳過去執行那邊一串code,有bug的時候光tracing code就累死人)
  • setupToLikeButton跟setupLikedButton可能會被誤用。應該禁止在registerLikeButtonEvent以外的地方執行(有點像private method)
  • 多了setupToLikeButton、setupLikedButton、registerLikeButtonEvent三個global function,感覺就是很不爽

我對這串code很不滿意、覺得應該有更好的pattern。

立馬上網買三本書

  • JavaScript 設計模式
  • JavaScript大全(第六版)
  • JavaScript:優良部分

其中「JavaScript 設計模式」果然是開卷有益。下面就是應用Module Pattern之後的寫法。

然後在所有需要「按讚」功能的頁面,加入這些程式碼:

各位覺得,有沒有比較模組化的感覺呢?

Q&A

Q1: 為什麼 setupToLikeButton函式前面多了底線?

只是命名慣例,提醒developer它有private性質。

Q2: 那setupToLikeButton為何會得到private性質?

因為它是function內的一個var,被限定在function的closure內,所以function以外的地方不能執行它。

Q3: LikeButtonModule只有提供一個initialize函式給別人使用?這什麼爛模組?

等到「按讚系統」需要的功能更複雜、更豐富時,可以在最後return的物件內定義模組公開API,到時這個pattern會看起來很完整很漂亮。

Q4: 我看到一個很怪的文法 (function(){}()); 請問它是什麼?

那是JavaScript的立即函式。簡單來說就是定義一個function然後馬上執行它。注意這個函式最後return一個物件,而這個物件也就是這個模組的公開API介面。

(Photo via  Alison Christine, CC licensed)

castle

如何寫好code:用Guard Clauses與EAFP打造你的城堡。

以前寫project的時候,常常越寫越不安。

心裡頭總是有兩個聲音:

  1. 為了更能應付不同狀況,程式碼要處理的情況越來越多、越來越複雜。if/else跟switch/case越寫越多層、一層又在一層又在一層裡面,越寫越像在挑戰自己智商的極限。
  2. 這個project再繼續維護(擴充功能、修bug)三個月沒問題、半年還撐得住,之後一定越寫越慢。不到一年,程式碼將龐雜到難以挽救。

總覺得哪裡出了問題:如果之後寫商用軟體、有持續擴充的野心,這樣寫code下去一定會遇到大麻煩。莫非要跟客戶或是老闆說:這project我無法繼續維護了,因為我能力不夠、您可能要另請高明。

難道我真的智商不足,沒能力寫出規模比較大的軟體?

終於,有一天,我學到了兩招,從此不再有這種擔憂。

今天,就來跟各位分享我的祕密。

(天阿,口氣真誇張,好像要推銷產品還是賣保險,哈)

這兩招是Guard Clauses與EAFP原則(It’s Easier to Ask Forgiveness than Permission)。下面跟各位分享,有寫錯的部份也期待各位前輩指教

Guard Clauses

與Guard Clauses相對的是Nested Structure。

下面這種code是典型的Nested Structure,相信也曾出現在你的project。災難一場。

遇到這種情況,要怎麼應用guard clauses?請看下面的範例。

以電子商務網站為例,下面是某class建立一張購物訂單的method。要輸入單品的id陣列、買家id、付款方式與折扣金額:

後者是不是清楚非常多?

EAFP原則(It’s Easier to Ask Forgiveness than Permission)

*註1

與EAFP相對的是LBYL原則(Look Before You Leap)

想像下面這個用LBYL寫的除法函式:

接著是用EAFP寫的除法函式:

後者有沒有比較乾淨俐落?

(好吧,這邊sample code看起來好像沒有,哈哈)

LBYL代表你小心處理各種情況;EAFP代表你直接執行最重要的程式碼,然後碰到問題(出現exception)再面對。

在MVC web開發領域,通常就是controller寫try/catch去執行model的某些method(EAFP),而不要在controller內自己寫一大堆判斷式檢查(LBYL)。
對應到剛剛電子商務訂單的例子,某個成立訂單的controller根據EAFP會寫出這樣的code:

若是根據LBYL原則去寫controller,則會寫出一大串if/else if/else去檢查$ids, $user_id, $payment_method, $deduction_amount這幾個變數、小心處理完各種情況才去呼叫create_one這個method。程式碼會又長又醜,看不懂整段code重點在哪。

以上是個人學習寫code學到最重要的其中兩招。其中有寫錯的部份也期待各位前輩指教。

Q&A

Q1: guard clauses看起來超直覺的啊,為什麼我到今天才知道可以這樣寫code?

這跟學校教育有關。正統教育有個名詞叫做single entry/exit point,說是為了維持code的高品質,叫你應該這麼做。

Q2: 有時候我還是覺得寫一串if/else比guard clause直覺啊?

程式碼的外觀其實直接說明了某些事情。guard clauses的code看起來就像在說:如果發生這件事,趕快處理完、然後滾吧。一串if/else if/else像是在說:你看,各個情況都一樣重要、所以大家都在一樣深層的巢狀結構裡面,真正重要的code跟某些狗屁情況一樣重要,所以他們一樣在這恐怖程式碼的內部第N層!

到底該怎麼寫還是看實際情況。不妨都試試,看當下哪個適合?

Q3: 我就老實告訴你吧!我根本不想學exception或是try/catch是什麼!因為我的code用if/else或是switch/case/default就把全部情況都處理好了!

會這樣想是因為你以前學C語言對嗎?C語言沒有exception機制,工程師必須寫出完整處理各種情況的程式碼。畢竟LBYL(Look Before You Leap )也是一種撰寫風格。那就寫到你覺得痛苦的那天再回來學這幾招吧。
丟exception不但code較易讀,還能在原本function之外去處理相關錯誤(把問題丟給「其他層級」去處理,像是model丟給其他model、model再丟給controller之類的)、追蹤exception在各stack內的流動。

Q4: 好吧,我剛是騙你的。其實我對正在用的語言的exception機制不熟。我還能用guard clauses嗎?

可以!上面sample code的throw new Exception全部寫成return false即可。然後EAFP就不會寫成try/catch了。

以同一個建立訂單的例子來說,如果你覺得成功跟失敗都是人之常情,請寫成這樣:

如果你覺得在你系統內應該不太會失敗才對,請寫成這樣:

Q5: 你Q2、Q3好像在說有時候的確需要寫一串醜醜if/else if?可以再做一點說明?

if/else if 可以想成在處理「合理的各種情況」。它們都一樣重要,所以code在同層級各佔了一整串。
throw exception則是在處理「不合理但確實可能面對的情況」。這種情況隨便丟個exception還是秀error之類的,叫它快滾就好了。
如果你很在意user experience,那就去客製化(宣告class去繼承原生exception)各個exception、幫助其他developer(通常就是你自己)在各種exception之下處理各種情況吧。
不過,在Web開發領域,噴exception常是在controller呼叫model函式之時發生。我通常直接在view內用JavaScript提示使用者。他若是進行什麼詭異操作跳過了我的JavaScript協助,那我就用瀏覽器大方的噴個超醜的error message頁面給他。

Q6: 為什麼Q4你要示範兩種寫法?你讓我好困擾、我到底要挑哪種?

把Q2再看一遍。

 

————————————————————————————–

註1:EAFP與LBYL這兩個詞我是在Python社群看到的。這篇用字遣詞可能不甚精確。

註2:文章裡建議的某些處理方式非常隨便。以上僅是我目前的理解。請不斷追尋自己最喜歡的best practice。

(Photo via Elescir, CC License)

Yingjeou_Ma

問題不是服貿好不好,是根本不相信政府說的話。

馬英九總統在今天早上發表了最新的聲明,再次強調服貿的好處、重要性與迫切性。

然後學生團體完全不接受。不管服貿聽起來多有道理、多有好處、多重要又多迫切,大家都聽不進去,因為現在根本沒人相信政府說的話。

這有點像是組織(政府)想推銷一個它認為很棒的產品(服貿),卻怎樣都沒人買帳。

這讓我想到Seth Golding(前Yahoo行銷副總裁)的一篇文章:

當公司產品賣不出去時,最重要的問題何在?

最重要的問題其實不是產品價格是否夠低、不是它是否耐用、不是它的功能是否豐富、不是打廣告的管道是否正確、不是官方網站是否夠酷,也不是公司的產品承諾是否完整。

這些都不是最重要的問題。

把產品賣給從沒買過它的人之前,最重要的問題是:他們是否相信你。

如果他們不相信你,那做再多承諾都沒用。

如果你很清楚自己的產品在幹嘛,但人們之前都沒買過你產品的話,他們很可能根本不相信你說的一切。

如果公司跨足新的領域、做一門新的生意,也會遇到同樣問題。

如果你非常確定你的產品很有價值,但卻怎樣都賣不掉,請先想想「信任」的問題。

取得信任、取得信任、取得信任。然後再去想剩下的事。

現在不管政府講什麼經濟分析、舉什麼競爭力的說明都沒有意義。大家根本不相信政府說的話。

原文出處:
http://sethgodin.typepad.com/seths_blog/2014/02/the-most-important-question.html

(Photo via Wikipedia)

jason_fried

當代領袖怎麼想:員工上班時間摸魚、逛Facebook!

台灣這幾年似乎沒有年輕領袖。檯面上都是郭台銘、張忠謀、施崇棠一類上一輩的企業家。

上一代的企業家持續影響著這一代的所有人:教導大家工作的方式、管理的方式、生活的方式。連創新的方法、網路時代的野心都是他們在談。

我想分享一些國外當代領袖對事情的看法,希望能給大家一些靈感。

————————————————————————-

今天要討論的問題就是「當代領袖怎麼想:員工上班時間摸魚、逛Facebook」,回答者是37Signals、Basecamp、「工作大解放」的作者Jason Fried。

下文摘錄自他在接受Inc網路媒體專訪時,分享他一天如何工作,以及對工作的看法。

Jason Fried:

午餐過後,我在1點到3點之間會覺得很慵懶。我覺得就算工作也沒生產力,所以我通常會上網隨便逛逛。我覺得這很重要。每個人都應該了解一下網路上最近什麼蠢東西很紅、或是看看有什麼新鮮事。

我痛恨把員工當小孩子的公司。他們封鎖Facebook或Youtube,因為他們希望員工一天工作8小時。員工不會因此更有生產力,他們只會很失望。管這種事到底能幹嘛?只要工作能做完,我不在乎大家一整天都做些什麼。

(以上只節錄訪談的一小段,建議有興趣的各位可以去翻閱原文。

順帶一提,Jason Fried的Basecamp是一間獲利「非常驚人」的小型網路公司。)

—————

原文出處

http://www.inc.com/magazine/20091101/the-way-i-work-jason-fried-of-37signals.html

(Photo via Inc)

馬雲

當代領袖怎麼想:當公司快倒了,員工卻不在乎業績,還每天準時下班!

台灣這幾年似乎沒有年輕領袖。檯面上都是郭台銘、張忠謀、施崇棠一類上一輩的企業家。

上一代的企業家持續影響著這一代的所有人:教導大家工作的方式、管理的方式、生活的方式。連創新的方法、網路時代的野心都是他們在談。

我不認為這些人懂當代的管理、當代的經營。我認為他們不懂。不然去這些人手下做事的年輕人,怎麼好像都沒有很滿意?為什麼當國家整體實力低下的時候,是年輕員工的競爭力不足,而不是上一輩企業家整體管理/領導能力的低落?

為什麼勞資關係惡化的時候,都是老闆對員工說「我認為你還沒有準備好當一個員工」,而沒有員工對老闆說「我認為你還沒有準備好當一個老闆」。

這跟學校教育、家庭教育有點關係。大家在唸書的時候,連上課舉手發言都覺得不好意思,離開學校之後又怎麼敢表達自己的意見。

除此之外,我還認為大家普遍對領導人的要求太低了。沒人要求自己的領導人格局要夠大、胸懷要夠寬廣。大家覺得這種事情可遇不可求、下面的人沒有資格要求上面的人。

我想從今天開始,分享一些國外領袖對事情的看法,希望能給大家靈感。

———————————————————————————–

今天要討論的問題就是本篇標題,回答者是阿里巴巴、淘寶網的馬雲。

大家看完問題可以先想想,故事發生在台灣,老闆會怎麼想?當事人員工會怎麼想?台灣員工敢這樣準時走嗎?那準時走是錯的還是對的?事不關己的其他人又會怎麼想?

原文出自阿里巴巴的生意經,是阿里巴巴旗下的一個社群平台。

網友sxf50000提問:

史上最牛業務員,一單不出準點下班。 80後女老闆抓狂了,我這是為員工在打工嗎?

我是一個80後女孩,畢業後工作一年,就自己出來創業了。做時尚時裝的外貿,是專做貿易,利用網絡平台在做。做了半年多了,毫無進展。現在是我和2個外貿業務員在做,自己每個月能出幾個小單,可是業務員一個單都出不了。即使能出小單,也入不敷出。我每天晚上都工作到11點,感覺很辛苦,我的業務員比我還輕鬆,他們每天下午6點準時下班,也不知道如何管理他們,現在是老闆比員工還累,壓力好大,再這樣我覺得我快堅持不下去了。我該怎麼辦?

馬雲:

我覺得這問題出在老闆身上。

第一,是不是招錯人了。

第二,是不是沒有激勵機制。

第三,總共三個人的公司,你要學會替別人多想想,別人就會替你多想想。你做10個小時是應該的,別人做10個小時是不應該的。因為你沒有讓​​他覺得是你的一份子,他憑什麼這麼做?

原文出處

http://baike.1688.com/doc/view-d20798915.html

(photo via wikipedia)

model

MVC,令人搞不懂的Model。

自學web開發兩年,對MVC一直一知半解,常在想怎樣設計系統會最漂亮。整理了一下目前的理解,以Model出發跟各位分享。其中有寫錯的部份也期待各位前輩指教。

Q1: 一個model對應到資料庫一張table對嗎?

不對。雖然大部分framework實作都會將一張table對應到model內一個class,但「一個model對應一張table」比較像是在描述ORM之類幫助簡化資料庫SQL操作的class,而model是「處理跟某張table相關任務的class」。

差別何在?一個model可能會處理到多張table的資料,也可能一張table都不碰,純粹處理一串商業邏輯。舉例來說,可能會有個負責整理行銷資料的model叫Marketing,從多張table撈資料出來,整理之後丟給controller。而資料庫中可能有,也可能沒有「marketing」這張table。

Q2: 可是我寫專案幾乎一個model對應到一張table啊?

因為實務上一個服務常會對應到一個model,而提供這model所需的資料常常就只是一張table。所以許多情況下的確是一張model對應到一張table沒錯。

但也不能說是「一張」table。model內常會去join其他相關的table對吧?所以說「model對應到一張table」或是「model就是一種ORM」都是不精確而誤導的說法。雖然寫起來常會有這種錯覺。

Q3: 那你用一個不會誤導又精確的說法描述model給我看?

model是layer」。不要把MVC中的M跟VC想成平行的分工關係,把VC跟M想成上往下的幾層layer。VC最靠近使用者、下來是M、再下來是database。所以VC要取得資料庫的資料都要透過model,而不同任務有不同model。

Q4: 聽不太懂,談點別的好了。從Rails社群流出一句很流行的MVC原則「fat model, skinny controller」,你對此有什麼想法?

這句話對初學者有幫助,對進階者有害。「fat model, skinny controller」這句話可以提醒初學者盡量把code丟進model別丟進controller。你再看一次我Q3的回答,一個應用程式的最重要部份(商業邏輯、安全性、效能)本來就是跟資料庫互動的那層layer,也就是model,所以把code丟進model別丟進controller可以暗示初學者寫出reusable的code。

但是把太多code寫進model會形成god pattern,也就是某個model跟神一樣負責絕大部分的任務,導致維護起來有困難(想像你初學PHP把全部code寫在同一份檔案的恐怖情況)。

Q5: 又說code要盡量丟進model,又說model內code太多很不好,不然code到底要丟哪?

library以及helper。現在大部分的framework都預設有放library以及helper的地方。不使用framework的話你也可以自己開這兩個資料夾。

多個model常做的同幾件事可以包起來寫成library,而單純的資料格式轉換(甚至是產生相關html/css)之類的任務可以寫成helper。讓model主要專注在商業邏輯(這是最花功夫的地方),其餘的搬到library、helper、或是有些framework稱之為third_party或package的什麼地方都好。別丟進controller!更別丟進view。

Q6: 好吧。可是我開發時在controller跟model內真的會不小心就寫掉大部分任務,這樣寫真的比較快阿。你開發時,上面說的你自己有沒有都做到?

沒有。原因有兩個。一,實務上的確很難在一開始就把架構想清楚,所以一堆任務會丟給model。二,常常越往下寫才發現某些任務是共通的、某些任務一開始只是「簡單」處理資料轉換,寫到後來變成「複雜的」處理資料轉換,我認為這時再把它拉出來成為library或helper即可。重構本來就是不斷在做的事情,時機到了就做*(註1)。

Q7: 我知道library跟helper的地位何在了。但,library跟helper的差別又何在?

軟體開發時,有時會寫幾個function或method解決某些任務,有時候覺得這樣太鬆散,會包成class。library常常是class、helper常常是一組function。以我自己來說,常在controller或view使用helper,在model使用library。

Q8: 關於model,最後有沒有要提醒的?

不要在model裡面用session variable。用下去,那model根本不再reusable(其他controller用之前還要先設定好相關session)。連unit testing都毀了。解決之道是dependency injection。session variable某種程度來說根本就是global variable,濫用global variable是很不好的。

註1:這段話可能很有問題。我在startup工作,開發上極度注重「速度」,常為了拼上線時間而捨棄其他事情。

(Photo via  Alaskan Dude, CC License)

by 阿川先生

有技術問題想要找人討論嗎?

歡迎前往我開發的 Devs.tw 程式設計討論區

一起來討論技術問題、分享技術筆記、文章吧!

前往逛逛:https://devs.tw