Laravel-–-a-beautiful-PHP-framework

Laravel 的 routing 是如何支援開發者使用 method injection?

前陣子參加線上讀書會,聊到 Laravel 5 的 controller 允許開發者使用 method injection

好奇是如何做到的?

今天 trace 了一下,發現此功能寫在 Illuminate\Routing\RouteDependencyResolverTrait 裡面

然後 Illuminate\Routing\ControllerDispatcher 使用了此 trait

並且在此處呼叫

這就是 Laravel 5 controller 提供 method injection 的地方。

順帶一提,直接用 closure 的 routing 寫法的話,一樣支援 method injection

它在 Illuminate\Routing\Route 使用了此 trait

並且在此處呼叫

Laravel-–-a-beautiful-PHP-framework

讀 source code 研究 Laravel IoC Container 實作的一點心得

前陣子在SO回答了一個問題

http://stackoverflow.com/questions/27341595/contracts-in-laravel-5/29812568

這問題不難,官方文件就有寫。

過幾天,下面的comments接著有網友 amosmos 提問 IoC Container 實作細節的問題。

他的問題考倒我了,完全無法回答。

於是花了幾天讀原始碼,最後終於搞懂了。

跟大家分享一下這個心得,也順便分享下我在過程中,讀原始碼的整套思路。

正文開始

簡單來說,網友 amosmos 的問題如下:

在 Illuminate/Foundation/Application.php 檔,會在 function registerCoreContainerAliases 看到這些:

讓我們先稱呼’app’、’auth’這些字串為 IoC Container 內的 「key」 好了。

乍看之下,大部份的 service 都在這時候被綁定到 IoC Container內,對應到某個 key。

看起來非常合理,也很漂亮,對吧?Laravel會用到的service都在這時候統一登記好、綁進這IoC Container。

問題來了,在 config/app.php 內,有這個陣列:

你如果熟悉 Service Provider (下文以SP代稱) 的話,就會知道每個 SP 內必定有 function register,負責綁定service到container。

以CacheServiceProvider來說,會出現這幾行:

問題來了,在上面的 Application.php,已經有這個了:

所以這CacheManager,到底是何時被綁定到 ‘cache’ 這個key上的?

是Application在建構式呼叫registerCoreContainerAliases時綁定的嗎?

還是在某處讀取 config/app.php 的providers時綁定的?

這邊出現了我們的問題一:

Q1: 難道重複綁定了兩次嗎?CacheManager被初始化了兩次?所以是 Laravel 原始碼的瑕疵?

讓我們做個實驗吧,把Application.php裡面這行comment掉:

接著在cmd輸入

php artisan tinker

很好,跑起來了,至少laravel沒有炸掉。

接著試試cache功能是否還活著?

看起來沒問題!難道真的是Laravel的瑕疵?

接著把 Application.php恢復原狀,然後把CacheServiceProvider的綁定給comment掉:

接著在cmd輸入

php artisan tinker

[ReflectionException]
Class cache does not exist

Laravel炸掉了!根本不能跑!

所以不算是重複綁兩次?

那就出現了我們的問題二:

Q2: 為什麼 Application.php 內的key給comment掉,功能還是正常運作?

反覆翻閱一下 Application.php、Illuminate\Container\Container.php,會發現 function registerCoreContainerAliases 似乎將整串陣列存在container的成員變數$aliases上?

接著尋找讀取config\app.php的地方,會找到 Illuminate\Foundation\Bootstrap\LoadConfiguration.php這檔案,然後發現Illuminate\Foundation\Bootstrap\RegisterProviders.php這檔案,翻閱Application.php和 Illuminate\Foundation\ProviderRepository.php之後,會發現config\app.php內的providers似乎會存在container的成員變數$bindings上?

有一個很簡單的方法去驗證這件事情。

將 Illuminate\Container\Container.php 的 $aliases 與 $bindings 成員變數改成 public,接著 php artisan tinker

會發現的確是這樣沒錯!

事到如今,我們會發現Q1的問題解決了:Application.php是存在aliases內,而config\app.php是存在bindings內。並沒有重複綁定兩次的問題!而是container實作細節沒搞清楚的問題!

所以真正的問題應該是這個:

Q3: IoC Container內的$aliases跟$bindings分別是什麼?兩者如何互動?

要解決這個問題,必須去讀container最核心的function make部份程式碼。

看到這邊,我們幾乎可以確定$aliases跟$bindings的關係為何了:

我們在使用 App::make($abstract) 時,輸入的abstract,實際上主要是去bindings找對應並叫出來。

而aliases只是紀錄abstract的其他別名而已,舉例如下:

還可以再做進一步的測試,將Container的function alias改成public,然後做以下測試:

所以正式回答Q3的問題:

Service Provider的function register內會去登記到IoC Container的abstract,實際上是存在$bindings或$instances陣列。

而App::make($abstract)實際上也是從這兩個陣列找東西出來用。

$aliases陣列只是存abstract的其他別名,讓 Binding Interfaces To Implementations 成為可能,也允許直接打類別全名進去。

結論

讀Laravel原始碼有幾個小技巧

1. 多用php artisan tinker去跟laravel互動

2. 把一些source code的變數、函數改成public,在tinker硬倒出來觀察

3. 有些東西很難在一天內搞清楚,睡個覺醒來再看通常很有幫助

(完)

quality

開發網站時,寫測試的3個簡單方法

許多想要學寫測試的朋友,常常不知道怎麼開始。

其實寫測試比大多數人以為的還要簡單。

就算是小型的網站、簡單的購物網站,也可以利用軟體測試加強軟體品質。

今天跟大家分享3個簡單的導入測試開發方法。

(本文使用 Laravel + PHPUnit 示範,但相關概念通用在任何語言/工具上。)

第一招、controller轉移到entity

開發網站時,大部份商業邏輯都在對entity(例:產品、訂單、使用者…etc)操作。

相關的code通常會散在controller裡面。

舉例來說,假設我們在寫一個購物網站,那麼把產品加進訂單、更新訂單金額的code會像這樣:

寫測試的目的是為了增加開發者的信心。

像上面這樣的code,有些人會覺得打開瀏覽器按個幾次,看看最後訂單金額是否正確就好。

反覆按個幾次,就算不寫測試,也對程式品質很有把握、相信它沒有bug。

但如果程式再複雜一點呢?例如增加不同運費的邏輯:

有些人依然覺得這不複雜,在瀏覽器上多操作幾次,然後看看金額、運費是否都正確即可。

但如果你像我一樣,採取比較嚴謹的立場,預設自己寫的所有code都有bug,那麼可以先把code從controller搬到entity:

然後可以這樣寫測試:

像這樣的測試寫好之後,就不用每次改動一點商業邏輯,就打開瀏覽器重複做一堆人工測試了。

如果運費的算法更複雜,根據訂單金額而有4、5種運費金額的話,

光是要測試運費邏輯,就要人工去操作下單流程好幾次。

這種時候,寫測試方便多了。

這種開發流程,我稱為「controller轉移到entity」。

一開始邏輯單純的時候,直接寫在controller即可。

複雜起來後,再把code轉移進entity,然後補寫測試即可。

第二招、轉移到service

接續上面的例子,來看看更複雜的例子。

現在產品賣掉的時候,產品本身的庫存數量要減一。

以原本的寫法來說,需要在entity加上這一行:

然後在測試內多加幾行:

看起來沒多大問題。邏輯放在Order內還算合理,該測的也都測到了。

但如果邏輯再複雜一點呢?

假設有人氣指數的功能,在賣出去的同時,商品的人氣指數要加上10分:

這行code還是放在Order類別內嗎?

為什麼一定是$order->addProduct($product),而不能是$product->addToOrder($order)呢?

隨著Product相關邏輯變多,商品加入訂單的功能不再完全像是訂單的事,也很像是Product的事。

像這種不知道要放在Order內還是Product內的時候,可以獨立出來成Service類別:

測試的時候,改寫成這樣即可:

跟第一招一樣,可以在程式變大之後、有必要的時候才獨立成service類別並補寫測試。

轉移的成本是很低的,幾乎只是把code從這邊剪下貼上到那邊而已。

第三招、轉移到專門的POPO

POPO(Plain Old PHP Object)是指單純的全手寫類別,不去繼承Eloquent之類的華麗類別。

雖然是最基本的OOP用法,卻常常被人們所忽略。

現在假設訂單進一步變複雜,運費的邏輯會根據消費者的所在地區而有不同:

再加上金額的變化,光是運費本身的計算就很複雜、需要寫很多測試才安心。

與其在entity或service內計算,不如弄一台運費計算機出來,會更分工明確:

接著就能替計算機寫許多測試,測到安心為止:

本來的service就可以改寫成,吃計算機當作參數:

POPO常常被人們忘記,因為太習慣把code全都寫進entity或是controller裡面。

其實,在程式複雜起來之後,最好把意義上獨立的部份各自獨立成類別。

然後分別替這些POPO寫各式各樣的測試,就能大幅增加軟體的品質與穩定性了。

結論

寫測試的目的純粹是為了增加安全感,讓工程師晚上安心入睡而已。

不需要撲天蓋地般地狂寫測試,適量的測試就非常足夠。

以中大型專案來說,寫測試可以幫助省下「非常大量的時間」。

實務上,在一開始商業邏輯不多的時候,寫在controller通常沒什麼問題,把幾個變數存進資料庫而已,不太需要寫測試。

等到邏輯慢慢變複雜,發現不寫測試會不敢上線的時候,再拉出來並且補寫測試即可。

希望你之後的專案開發,可以試試看寫測試帶來的好處與美妙!


歡迎訂閱轉個彎日誌的粉絲專頁,我很樂意和你分享各種心得。

(Photo via Elizabeth Hahn, CC licensed.)

baby

不要浪費開發時間:給新創公司的6個軟體開發建議

新創公司因為產品定位還不明確,常常花一堆時間做好功能,卻發現沒人要用,浪費了寶貴時間。

我在2014年底加入一元翻譯,一個已有穩定客源與翻譯師的翻譯團隊,負責開發系統來協助公司處理與日俱增的文件。

原以為這套系統的定位明確、連使用者都已經有了,因此容易開發,結果初版的系統開發還是犯了一些錯誤。

踩雷幾次之後,我們慢慢整理出一些比較有效的開發方法。

這些方法幫我們省下很多開發時間,今天跟大家分享一下!

一、別在一開始就想要流程全自動

常常聽到正要創業的團隊擔心「要是我們太受歡迎怎麼辦?」、「要是我們太紅怎麼辦?」

直覺的想法會是「當然要先做好準備再上線!」、「把系統做到全自動,讓工作人員不需要介入,整個流程就能順利跑完!」

我們也犯過類似錯誤:既然都已經有穩定客源了,請他們改用更方便的全自動系統應該不難吧?

結果系統上線之後,舊有客戶大多繼續用 Email 和電話與我們來往。即使我們提供額外折扣,他們一時之間還是不習慣新的下單方法。

所以我們後來設計新功能、新產品的時候,便不再一開始就把所有流程自動化。

客戶習慣的部份,不要輕易做大幅度調整。先針對很花時間、重複性很高的部份做自動化,比較有效率。

其餘的部份,快速寫出一個「半自動」的系統,然後直接上線。

收集Feedback系統?用個Google表單嵌入在網頁裡面就很像了。

自動寄出電子發票系統?做個「寄出發票」按鈕,給客服人員自己找時間去按就好了。

合作夥伴註冊系統?用個Google表單嵌入蒐集資料,然後自己用 Email 一個一個聯絡就好了。

商品具有多種狀態的全自動物流系統?其實在資料庫用不同整數代表各種狀態,給物流人員手動調整所有商品的所有狀態就夠用了。

做出幾個功能按鈕,讓負責人員自己判斷什麼時候去按那些按鈕就好了。

情境判斷與自動化的程式碼都先別寫了。需求量大到負責人員忙不過來再寫就可以了。

二、信任你的內部人員

不信任的成本是很高的。

公司常常需要針對管理員、工程師、合作廠商、實習生等等不同角色,開發權限系統。

真的要針對每種角色,在後台管理面板寫一套大企業等級、能夠做完整內控的權限管理系統嗎?

底層不作嚴格檢查、只針對各角色顯示不同資訊,真的不夠用嗎?

相關的內部人員真的會惡意到去測試系統漏洞、亂改不屬於他們的資料嗎?

真的發生這種事,在算帳時會發現不了嗎?被影響到的人不會來反應嗎?

我們也曾因為預計之後會有獨立的PM權限,於是在目前的ADMIN權限之外多寫了PM權限。

結果因此浪費很多開發時間,而且根本沒那麼快招募專門的PM人員。

完整的權限系統會需要在controller或是更底層的layer寫一堆檢查的程式碼。

更糟糕的是,各個角色的職責跟功能也變動得很快,複雜的權限系統會讓各種功能改起來更慢。

先實作一套簡單陽春的後台權限檢查,通常會夠用好一陣子。

 三、把功能拆分成階段上線

這是所有建議裡面,我們付出最多代價,才學到的教訓。

不要在一開始就把功能做到豐富完整。

大部份的功能,都可以在討論過後,拆分成階段上線。

討論出功能最基本的長相(Phase 1),試著在幾天到幾週內開發然後上線。

Phase 1上線後會碰到各式各樣的問題,像是使用者不想用、不會用、用了卻不爽…等等。

不如讓 phase 1 先上線,再根據結果,思考phase 2要做哪些事,或是乾脆擱置這個功能不做下去。

這種作法能讓服務快速上線、團隊的下一步明確、省下不必要的開發時間。

以我們一元翻譯為例,公司的營運有兩段流程:客戶送文件給我們、我們送文件給翻譯師。

這兩段流程原本都是在Email上進行,也就是很傳統的作法。

為了讓這個流程自動化,需要開發一個類似購物網站的電子商務系統,讓客戶透過網站下單、翻譯師透過網站收到文件。

直覺上會從客戶下單介面開始,接著做內部管理面板、翻譯師工作面板,把整個系統寫完。

但如果真的一口氣把系統寫完,風險會非常巨大。

要是客戶都拒絕透過網站下單、堅持繼續用Email寄檔案給我們怎麼辦?

要是翻譯師不喜歡用工作面板接案,習慣用Email跟我們互動怎麼辦?

那原本預期的流程就完全行不通了。

為了避免這種「寫出一整套solution,上線後卻行不通」的慘劇,我們  phase 1 只開發「送文件給翻譯師」系統。

也就是客戶繼續用 Email 給我們檔案,我們的PM手動把文件上傳到系統,接著系統通知翻譯師,翻譯師透過網站接案與交稿,PM最後再把成品用Email寄給客戶。

對客戶來說,流程跟原本一模一樣。

upload

(圖一、PM用這個面板把檔案丟進系統)

projects

(圖二、PM跟翻譯師在這個面板瀏覽文件、翻譯文件)

Phase 1 上線後,我們蒐集翻譯師的意見、不斷改善系統、改到翻譯師覺得系統好用為止。

然後才去開發「客戶送文件給我們」的系統。

但因為怕客戶「堅持只用Email與我們互動」、「操作網站會很沒安全感」,我們的 phase 2 從提供一個確認報價的連結開始:

email

(圖三、收件後繼續由PM寄Email給對方。但要求他至少打開一個網頁。)

confirm

(圖四、報價資訊只寫在網頁內,不寫在Email內,鼓勵客戶去按那個大按鈕。如果客戶拒絕去按,而寧願寫Email回信,那這個 phase 2 就算是失敗。)

結果大部份的客戶都願意去按「同意報價」按鈕!只有少數客戶選擇用Email回信。

確定客戶們至少「願意在Email以外與我們互動」,我們才動手把圖一的PM下單面板做成公開版本的 phase 3,讓客戶能夠自行下單。

如果沒有分階段開發,這個系統可能會開發非常久才上線。

四、盡量去追蹤網站的使用情況

網站到底哪些功能常被使用?哪些地方客戶逛了但是沒去用?哪些地方客戶根本逛都不逛?

除非你站在客戶旁邊看他操作系統,否則很難得到答案。

網站的下一步要怎麼改善,團隊裡的每個人都有不同想法,這種時候,最好能用數據來說話。

因此,應該要盡可能地去紀錄每個按鈕、每個連結、每個頁面的使用數據。

安裝Google Analytics是基本的。

除此之外,記得替幾個你覺得重要的按鈕加上Google Analytics的Event Tracking功能;

有在網路上公開的文章、貼文,可以用Bitly之類的工具紀錄網址使用情況。

再不行的話,就在系統本身建幾張資料表,直接用程式碼去紀錄某些功能的使用數據吧。

五、工程師做做看客服,做做看業務

這點跟開發沒有直接相關,但還是會影響開發速度。

負責客服的同事可能多次向工程師反應過某個頁面很難懂、按鈕很難用。

工程師常常會去忽略這些反應,心想「那個頁面最好是有這麼複雜」、「你跟那些客戶多解釋一下就好了」。

然後就會導致每個人對於接下來什麼事最重要有不同看法。

這種時候,如果工程師花點時間去做客服,通常會有很大幫助。

他可能會驚覺「居然這麼多客戶反應同樣問題」,於是充滿鬥志的把功能改好。

也可能會發現「同事沒講清楚,但其實加點字、改改按鈕顏色就解決了」。

同樣的,如果工程師花一些時間做業務,可能會突然理解為何負責業務的同事會一直要求某個功能。

工程師偶爾換一下角色,做點其他事情,不但能讓團隊溝通更有效率,開發起來也會更有士氣。

六、先讓介面簡單易用

我們在設計第一版網站的時候,想把網頁弄得漂漂亮亮、希望它很有質感。

後來發現如果它的功能本身不受歡迎,那再漂亮也沒用。

在不確定會多受歡迎的情況下,不如先做到使用者知道系統怎麼用就好。

所以JavaScript預設的alert、confirm、prompt函數,其實依然很好用。

一元翻譯來說,我們把報價頁面做好之後,客戶卻多次反應「不知道怎麼確認報價」。

因為不想花時間去大幅調整設計,我們於是直接加上又大又紅的提示文字,再搭配一個大大的綠色按鈕:

red

(又大又紅的提示文字)

green

(48px 超巨大綠色確認按鈕)

雖然不太好看,但這個介面開發時間很短,而且上線之後,再也沒有客戶抱怨找不到確認按鈕了。


以上六點就是我們花了許多開發時間後,整理出來的一些建議。

說穿了其實就是:盡可能地將開發時間花在確定有價值的事情上面。

但也不需要太怕犯錯,初次開發產品多少都會遇到類似問題。

最重要的是有明確方向、小步驟地實驗與驗證,同時又保持足夠彈性來根據實際使用狀況做調整。

如果您有任何想法或是其他開發建議和大家分享,歡迎在下方留言!

 

工商服務時間:

本篇文章的內容,大多是來自我在一元翻譯開發系統的心得。

需要專業翻譯服務的朋友,歡迎來一元翻譯的官網逛一逛!

文中提到的客戶下單系統,可以在這裡看到它的真面目!

也歡迎到一元快報看看我們翻譯的各類優質文章!

(Photo via Sano Rin, CC licensed.)

learn

給OOP初學者的建議:先搞懂「資料跟行為在一起」就好,其它的慢慢來

初學者接觸OOP,幾乎都會有以下疑惑:

我到底為什麼要學OOP?OOP解決了什麼問題?書上這些範例就算不用OOP也寫得出來吧?

然後覺得「繼承」、「多型」、「介面」、「抽象類別」等等的名詞很難,覺得OOP很難。

其實這些名詞雖然重要,但對新手來說,本來就很難在一開始就搞懂。

建議先搞懂「資料跟行為在一起」是什麼,以及它的好處在哪,就可以了,其它的慢慢來。

什麼叫做「資料跟行為在一起」?

假設我們在開發一個「中英文互助學習網」,鼓勵中文人士與英語人士登入討論。

這個系統的貼文、留言功能會顯示「發文日期」。

發文日期要根據使用者的註冊身份(台灣人、英語人士)顯示不同格式(台灣格式、西方格式)。

下面就以這個日期格式的功能舉例說明「資料跟行為在一起」是什麼意思。

作法一:直接硬寫(不OOP、資料跟行為混在一起)

初學者通常會用最簡單、也最直覺的作法,直接硬寫出來,像這樣:

這種寫法的資料(日期)跟行為(轉換成各種格式)混在一起。

它的優點是寫起來很簡單,缺點則有兩個:

* 日期格式的邏輯會重複出現在很多地方,整段code會到處重複出現
* 整段code大概會塞在<div>或是<span>的裡面,導致它跟HTML混在一起,很亂

作法二:自訂函數(不OOP、資料跟行為沒混在一起)

為了解決作法一遇到的問題,聰明的初學者很快就想到可以用「自訂函數」!就像這樣:

這種寫法將行為(轉換成各種格式)用自訂函數給獨立出來,也大幅改善了作法一遇到的問題。

對小型的網頁程式來說,這招非常好用,不但開發快速、簡單,還漂亮地將資料跟行為拆開。

但是程式規模變大之後,為了將各種行為拆出來,會寫出很多自訂函數,類似這樣:

於是又衍生出三個問題:

1. 像localFormat、englishFormat這樣的函數名稱意義模糊,看不出是處理日期、人名,還是什麼東西的格式

2. 這些自訂函數各有不同的行為,全部放在一起顯得很亂,應該要想辦法分類、整理這些函數

3. 像localFormat、englishFormat這樣的函數,只吃特定格式的參數,最好能跟某種資料的形式綁在一起,以後要改程式時,能讓相關的資料跟行為一起被看到

問題1很好解決,只要替函數名稱加前綴字變成dateLocalFormat、dateEnglishFormat就行了。

問題2也很好解決,只要多開幾個檔案,把相關的函數放進同一個檔案就行了。

問題3就很棘手,資料跟行為拆開之後,如何在概念上又找方法整理在一起?

作法三:使用class(OOP、資料跟行為在一起)

正是這些處理資料、整理行為的問題,導致了OOP的誕生:

OOP的寫法,一次解決了前述三個問題:

問題1 => 現在從類別名稱就可以知道底下方法的意義了

問題2 => 現在相關的函數都整理進同一個類別底下成為方法了

問題3 => 現在資料的形式都統一在constructor處理一次,之後不管新增多少方法都不用處理資料了

這就是所謂的「資料跟行為在一起」,也正是OOP的核心概念。

利用這種方式整理程式碼、寫出一個又一個的類別,可以大幅提昇程式碼的品質。

結論

上述的作法一跟作法二並沒那麼糟糕,但確實會帶來一些問題。

對於小型的網頁程式來說,可能還算夠用。

但是隨著程式規模變大,如果將概念上相關的資料跟行為整理在一起,會很有幫助。

實務上也可以先從作法二開始寫起,直到發現某些資料跟行為關係密切,再拉出來整理成類別即可。

至於很多OOP教學會提到的「繼承」、「多型」、「介面」、「抽象類別」等等名詞,一時搞不懂沒有關係,你可能實務上也暫時用不到。之後找時間慢慢搞懂它們的用途就好。

光是知道「將資料跟行為放在一起」的技巧,就能夠開始寫OOP程式碼了。

(註:本篇文章的程式碼純屬教學用途。實務上PHP已經有DateTime類別可以使用,或是用更漂亮的Carbon類別。)

Q&A

Q1:我常常設計一些類別,只有資料沒有行為,聽起來OK嗎?

不OK,這很不OOP,而且沒意義。

乾脆直接用關聯式陣列去表示那些資料就好。

Q2:我常常設計一些類別,只有行為沒有資料,聽起來OK嗎?

這個要看情況,不一定。

但唯一可以確定的是,這種作法很不OOP。

因為OOP的核心是「資料跟行為在一起」。

這也是為什麼你會看到有人明明寫了類別、用了物件,別人卻說「這不夠OOP」。

然後你又會看到像JavaScript這樣連「類別」關鍵字都沒有(ES5以前),卻能夠寫出很OOP程式碼的關係。

判定的標準都是一樣的,而且也就只有這麼一個標準:資料跟行為有沒有在一起。

Q3:一個類別包含的概念是越大越好,還是越小越好?

不一定。不過我們從作法一到作法三的過程,有一個明確目的:希望讓程式碼更好懂。

如果聲稱一個類別包含的概念很大(例:設計LanguageHelpWebsite類別,用來代表「中英文互助學習網」需要的所有功能),那會把幾乎整個網站的所有行為跟資料都放進去,成為所謂的God object。它可沒有讓程式碼更好懂。

相反地,如果聲稱一個類別包含的概念很小(例:分別設計LocalDate、EnglishDate類別),雖然意義可能更精準了,但用一整個Date類別的概念去思考,程式碼會更容易理解,也就是所謂的內聚性(Cohesion)更高。

所以要替OOP就是「資料跟行為在一起」加個但書:

要以方便理解程式為前提,將資料和行為放在一起。

(完)

(Photo via brando.n, CC licensed.)

jason_fried

[翻譯] Basecamp共同創辦人:不必擇你所愛,也不必愛你所選

如果你常參加創業聚會,或是常去聽那種企業家的勵志演講,你會很常聽到這種論調:你必須擇你所愛,愛你所選!如果你沒有的話,那你乾脆別出來混了。最著名的例子是Steve Jobs 2005年在Standford畢業典禮上的演講:「想要有出色成就,唯一方法就是做你熱愛的事。如果你還沒找到,那就繼續找下去,不要停下來。」

我完全不信這套。

熱愛自己的工作當然沒有錯。但我不覺得這是創業或實現抱負的先決條件,也跟什麼出色成就無關。說真的,事業有成的人吹噓自己對工作的熱愛實在很虛偽。就跟富有的人說自己不在乎財富一樣虛偽。人們常常會浪漫化自己的動機跟過去。他們會把自己現在所在乎的東西想得很重要,而忘記他們一開始在乎哪些東西。人類天性如此,很容易就會這樣。

根據我的觀察,許多出色企業跟重要創新,根本是源自於挫折,甚至是厭惡。Uber的共同創辦人 Travis Kalanick 和 Garrett Camp哪是因為熱愛運輸與物流才創業的。他們創業是因為舊金山很難叫計程車,搞得他們一肚子火。Kalanick現在大概滿喜歡經營Uber的,但他之前真的超痛恨叫不到車回家。巴黎一個腦力激盪的夜晚,讓那份挫折轉變為一棵幼苗,孕育出一間市值數十億美金的公司。

我常常跟其他企業家聊天,很多人開公司都是基於類似的理由:他們想要的東西市面上沒有,或是想用更好的方法去改變舊的做事方式。至於是否熱愛則未必重要。但是對於現有選項的厭惡、對於事物運作方法的強烈意見則影響極大。能否成功也和它比較相關。

我的職業生涯也是這樣。大概在90年代的時候,我想找一個能幫我紀錄音樂播放清單的小工具,但市面上的軟體都很肥大,而且過度複雜。這兩件事都讓我痛恨。所以我就自己想辦法做了一個工具,最後命名為 Audiofile丟到市面上。我並不熱愛音樂蒐集,也不熱愛開發軟體(當時才剛學而已)。我也沒有開一間軟體公司的願望。我就只是看到一個需求,然後設法滿足它,謹此而已。這沒什麼不對。之後也基於類似的情況,開了現在這間公司Basecamp。

老實說,就算到了今天,我也沒說總是很愛我的工作。那些文書工作,那些報告,伴隨公司成長所帶來與日俱增的責任與瑣事。這些事都讓我覺得很煩。不過經營Basecamp對我來說還是比做其他事好。我覺得我做得不錯。每天都要作一些需要創意、很有挑戰性的工作。我一直覺得讓專案管理工具越變越好,是一個值得、能有所回報的理由。每天都能跟這些厲害的同事一起工作也真的很棒。

如果要我上講台發表什麼勵志演說,我會說,如果你想成功、想對世界有貢獻,你需要對你在做的事情有某種內在動機。你必須樂於把時間花在那上面。對於它的喜愛有可能在日後增加。如果真的那樣,那很棒。但不需要一開始就熱愛它。光是渴望一個還不存在的東西,就足以讓你成功。

(本文翻譯自 Do you have to love what you do?,得到 Jason Fried 親自授權。)

provoke

批評 Active Record 的13個論點:最好用也最危險的 Anti-pattern

現在很多框架都內建active record pattern來幫助開發者建立application。

乍看之下很自然,實際上它卻違反了一大堆傳統的軟體開發知識。

這會讓工程師在學習時陷入下列困惑:

為甚麼一堆書上的原則(principles)、模式(patterns)、手法(practices),都無法在實務上應用?

接著導致一個更嚴重的認知失調:

是我的開發方法不對嗎?還是這些傳統知識全部過時了?

事實上,active record pattern還真的跟傳統開發知識充滿衝突。

為了點出那些衝突,這篇文章會從傳統開發知識的角度,對active record pattern提出多項批評。

什麼是Active record pattern?

從Rails的ActiveRecord,到Laravel的Eloquent,許多框架都內建active record pattern。

各框架對active record pattern的實作都很華麗。除了直接操作資料庫、充當domain object的功能之外,還提供relationships、自動更新created_at/updated_at欄位等等多種功能。

一般認為active record pattern這個名字由Martin Fowler所發明。

根據他的定義,一旦類別同時處理兩件事情:

* data access logic(處理資料庫相關操作)
* domain logic(處理app的商業邏輯)

就算是實作了active record pattern(下文簡稱AR)。其他華麗的功能都是框架額外提供的。

AR會讓物件同時「提供資料給別人用」與「提供行為給別人呼叫」。

正是因為這點,就傳統開發知識來看,至少可以對AR做出13種批評。

下面會將這些批評分成3種層面來看,然後一一列舉它們:

* 原則層面(Principles)
* 模式層面(Patterns)
* 手法層面(Practices)

(注意:許多批評其實是指同一件事,只是換個層面,或是換個方法描述而已。)


原則層面 (Principles)

1. 違反物件導向(OOP)精神

物件應該將資料對外隱藏,將行為對外開放(hidden data, expose behavior)。

而承載資料的資料結構會將資料對外開放,並且不具有行為(expose data, no behavior)。

AR物件因為同時身兼兩者,導致你從根本上面臨矛盾。

你永遠抓不清分際何在:該讓它所有properties都對外隱藏嗎?還是讓它一個method都沒有?分別該做到什麼程度呢?

2. 違反Single Responsibility Principle

AR負責了data access logic,導致資料庫schema一改,程式碼必須跟著修改。

AR負責了domain logic,導致app的商業邏輯一改,程式碼必須跟著修改。

OOP知名五大原則SOLID中的SRP被AR直接打破:一個類別應該因為且只因為一個理由而修改。

3. 違反「Tell, don’t ask.」原則

「Tell, don’t ask.」原則指引大家OOP開發應該「告訴物件去做什麼」而不應該「跟物件拿資料出來操作」。

偏偏AR物件是如此方便易用,你幾乎一定會從外部直接取用它的資料。

4. 導致Database Oriented Development

應該圍繞著產品本身領域知識去開發的domain driven/business oriented開發方法,變成以database為中心在開發。

開發者在開發過程中,不再對領域知識念茲在茲,而是滿腦子想著database schema。

如果使用的Active Record Pattern甚至實作了toJson這類的函式來幫你把entity內容轉成JSON格式的話,下場就更誇張了:連front-end的開發人員都得跟著滿腦子database schema。前後端分離程度更低。

5. 導致測試時碰到資料庫

傳統上會說測試時應該只測商業邏輯,別碰資料庫,因為碰到資料庫會跑很慢。而且兩者應該分開來測。

但是用了AR會讓測試很難不碰到資料庫。

框架甚至直接提供fixturesModel Factories來協助這件事,毫不避諱。


模式層面(Patterns)

6. 無法用Constructor Injection

你無法實作這個dependency injection模式來設計類別:

因為你的建構式被拿來處理data access logic,因而放棄domain logic了。

7. 導致God object

建出來的entity,會很容易把code一直往裡面放,最後變成上千行、負責一堆功能的God object:

也就是Rails社群流傳的「Fat model, skinny controller」。

8. 導致「貧血的領域模型」(Anemic Domain Model )

如果你為了避免entity內含過多商業邏輯而將code抽離到別的地方(例如:做了一堆service objects),那很容易變成相反的狀況:entity本身幾乎不含什麼code。

例如這樣:

9. 做不出Rich Domain Model

理想狀況是隨著app的商業邏輯越變越複雜,你的domain model會因為各種分工(由多個物件來對外負責各種behavior)而越變越豐富,最後成為漂亮的rich domain model。

非常遺憾的,因為使用了AR的關係,類別的設計被綁死在schema上,因此你很難做出rich domain model。

你要嘛會做出God object,要嘛會做出Anemic domain model。

也許會有人嘗試將data access logic與domain logic分離來克服…

奉勸千萬別這麼做:根本是放棄AR的優點去硬搞,最後變成四不像。

10. 質變的Repository Pattern

將CRUD操作(persistence logic、query logic)封裝的經典模式repository pattern,因為AR而產生質變。

舉例來說,你可能看過有人這樣設計repository class:

接著將repository用dependency injection放進controller或是service object內。

乍看之下沒問題:確實因此可以在測試時抽換repository object成測試專用的object。

但應該「負責persistence logic」的repository,實際上居然只是叫參數物件自己save自己。

這可稱不上優雅。

質變後的repository退化成只負責查詢功能的query object。

11. 質變的Domain Driven Design

Eric Evans寫的Domain-Driven Design一書教導了一種圍繞領域知識的軟體開發方法。

很悲慘的,如果你用了AR,這本書你將很難看得懂,也很難應用。

DDD從最基本的Repository Pattern就已因AR導致質變(參考10.),再加上entity間的關係通常會被AR直接根據schema建好,Aggregate也變得無法封住內部物件,更不用說domain model根本建不好了(參考9.)。


手法層面(Practices)

12. 濫用public properties

OOP課本翻沒幾頁就會提到getter/setter的觀念,並且反對直接把properties設成public。

AR因為同時負責data access logic,很自然會讓人直接對properties操作:

13. Atomic operation困境

為了確保Database內的資料正確性,有時你需要用到lock機制或是transaction機制。

試問下列這兩行code該放哪呢?

放在entity的話,代表Order/Item/User其中一個類別會直接操作其餘兩個類別 => 容易導致god object。

不放在entity的話,就必須放在外部(例如service object或controller) => SQL語法不再被封裝在底層,也不被封裝在entity內,而外洩到更上層了。

結論

Active Record Pattern方便、快速開發的特性,讓它具有非常高的商業價值。

這也是Rails之後一直到Laravel等等各大框架都實作它的原因。

愛用Active record pattern跟反對active record pattern的人常常有所爭論,幾乎成了現代開發vs傳統開發方法的戰爭(例一例二)。

使用它來開發沒什麼問題,但千萬要留心它與哪些傳統開發知識有衝突。

享受它的優點,理解它的缺點,才能在現代和傳統開發知識的衝突上,找到屬於自己的平衡點。

(完)



下面是我在公司新開發的一個討論plugin。歡迎用它來反映您對AR的看法。

順帶一提,server-side是用Laravel 5所寫成:背後的Eloquent,正是實作active record pattern。

(Photo via Don McCullough, CC licensed.)

fancy

簡單聊一下 One-way data flow、Two-way data binding 與前端框架

新手在學完JavaScript基本知識、離開新手村之後,很快就必須面對前端框架。

這些框架常會號稱是one-way data flow或是two-way data binding。

這兩個名詞究竟代表什麼呢?一定要有框架才能做到嗎?一個框架只能是one-way或是two-way嗎?

這篇文章會一次回答這些觀念問題,並且分別檢視一次Backbone、Angular、React三套框架。

用詞澄清

在開始之前,先針對用詞做兩點澄清。

澄清一:「data flow」跟「data binding」指的是同一件事。

換句話說,one-way data flow就是one-way data binding;two-way data binding就是two-way data flow。

這件事其實Facebook的React官方文件就有提到:

React’s one-way data flow (also called one-way binding) keeps everything modular and fast.

澄清二:這兩個詞是指稱某種行為,而不是指稱具備某種功能。

舉例來說,下面這種句子是不嚴謹、充滿誤會的:

* 某某框架「是」一個 one-way data binding 框架。 → 不嚴謹的句型,盡量少用。

正確的描述方法是這樣:

* 某某框架讓人「很容易做到」one-way data binding。 → 正確句型。

溝通上應該避免用「是」句型;用「很容易做到」句型才正確。

使用正確的句型有助於理解這兩個詞。這部份後面會再解釋。

Data model

data binding指的是UI元件和data model之間的互動。

在談data binding之前,我們先從data model的觀念開始談起。

沒有data model觀念的JavaScript/jQuery寫法

新手最直覺的作法,就是用JavaScript/jQuery不斷操作HTML元件,讓畫面上顯示他要的效果。

不同元件之間需要溝通時,就每次都去觀察HTML元件的內容,將需要的資料找到,接著處理。

舉例來說,做一個TODO list時,就用ul元件包住一堆li元件,每個li元件代表一個待辦事項。

需要增加/減少/修改事項時,就用JavaScript/jQuery想辦法找到目標li元件,然後處理它。

那麼要把所有事項內容用alert跳出來,或是用HTTP POST丟給後端怎麼辦?

那就用JavaScript/jQuery把li的內容分別抓出來,整理成字串丟給alert顯示,或是另組成form元件submit出去(用成Ajax丟出也可以)。

簡單來說,就是data本身存在於UI元件(HTML元件)之中。每次需要data就去分析一次UI元件。

這種作法簡單、直覺,在UI單純時可以這樣寫,但當頁面上的UI元件多、互動又複雜時,程式碼很快就會變成一團混亂。

有data model觀念的JavaScript/jQuery寫法

有經驗的工程師很快就會想到separation of concerns原則,將data本身獨立成自己的模型(本文稱為data model),每次顯示就根據那個data model去render出UI元件即可。

例如說像這樣(todos陣列就是data model):

然後在button元件或是li元件的onclick事件裡面操作todos陣列,就算是操作data model了。

此外,記得在所有關於data model的操作內呼叫renderTodoList函式。

像這樣任何操作都從data model出發,最後再render的作法,可以讓data model跟UI元件保持某種對應關係,讓程式碼更好維護。

有經驗的前端工程師,光靠這種技巧,再搭配Handlebars之類的模板系統,就可以寫出十分漂亮的程式碼。

例如這份:jQuery TodoMVC source code

可以反過來寫嗎?

這個時候,就有人疑惑了:要讓data model跟UI保持對應關係,可不只這種方法。

為何要從data model出發再render出UI?何不從UI出發再generate出data model?

每次變動過HTML都去更新data model不可以嗎?

其實可以,那就會像這樣:

而清空列表的程式碼會變這樣:

任何操作都從HTML(UI)出發,最後再generateDataModel的作法,一樣可以讓data model跟UI元件保持某種對應關係。

Data binding

有了data model獨立於UI的觀念之後,我們會發現上面兩種作法代表兩種資料的流向(data flow):

* 從data model出發,每次更新就同步更新UI(甲方向)

* 從UI出發,每次更新就同步更新data model(乙方向)

前端框架與data binding的關係,就是框架本身能否讓人很容易就做出甲方向或是乙方向的效果。

能輕易做出其中一個方向,就算是達成one-way data binding。

能輕易同時做出兩方向,就算是達成two-way data binding。

以前面Todo list的兩個範例來說,其實都做到one-way data binding了,但是我需要去多寫一個陣列、幾個函式、用了onclick事件、手動去loop過每個待辦事項、還要記得每次去呼叫函式,花了點功夫(共做5件事)才做到one-way data binding。

所以我們不會說JavaScript本身有data binding機制,也不會說jQuery是一個讓人很容易做到one-way data binding的套件。

這也是最前面「澄清二」提到的避免使用「是」句型的原因:

* 說「jQuery是一個提供one-way data binding的套件」是錯誤的,因為做起來不太容易。

* 說「jQuery不是一個提供one-way data binding的套件」也是錯誤的,因為還是做得到。

* 說「jQuery要做到one-way data binding不太容易」就沒問題,邏輯上正確。

如果每次更新data model就call renderTodoList,再加上,每次更新UI就call generateDataModel,那甚至做到了two-way data binding。(實務上大概不會有人這麼做。)

說明完這兩個詞為什麼是在指稱行為而非特性,然後解釋了句型上的正確用法之後,讓我們來解讀各大前端框架的特性,以及用正確的句子評論它們。

Backbone.js

參考這份程式碼:http://jsfiddle.net/Xm5eH/9/

甲方向data binding

設定data model更新時要call render函式:

運用Underscore.js提供的模板系統:

在render函式用jQuery正式將模板系統的輸出塞進HTML裡面;

官方提供的幾個機制就達成data binding了。

這也是為什麼很多人說「Backbone.js是一個one-way data binding框架」的原因。

(其實正確說法應該是:「Backbone.js是一個讓人很容易做到 one-way data binding的框架」。)

乙方向data binding

先去監聽UI元件的變化,一變化就call update函式:

在update函式去更新data model:

評論

乙方向data binding很有爭議,也造成Backbone.js社群的一些誤會與爭論。

在某些人看來,這樣夠輕易就達成乙方向data binding了,因此「Backbone.js是一個two-way data binding框架」。

在另外某些人看來,這只是去監聽UI內容的變化然後手動更新data model而已,因此「Backbone.js不是一個two-way data binding框架」。

在最後某些人看來,上面甲乙兩個方向都是一堆手工設定達成,需要更優雅的去綁定兩邊才算數,所以又做了Epoxy.js這樣的套件。

這也再次說明了為什麼要避免使用「是」句型:它帶給人們混亂、誤會與雞同鴨講。

就說你認為「Backbone.js容不容易做到one/two-way data binding」就好了。

因為「容不容易」是一個主觀問題,聽的人各自判斷,誰也不需要說服誰。

Angular

參考這份程式碼:http://plnkr.co/edit/GxqBiOoNFuECn55R4uJZ?p=preview

甲方向data binding

把資料設在$scope底下:

使用{{}}的模板syntax:

乙方向data binding

利用ng-model這directive就完成了:

評論

甲乙兩方向都很容易做到,Angular也一直以此為賣點。

這就是一談到two-way data binding,人們就想到Angular的原因。

話雖如此,還是老話一句:不要說「Angular是一個two-way data binding的框架」。

說「Angular讓人很容易做到two-way data binding」比較好。

React

參考這份程式碼:https://jsfiddle.net/reactjs/n47gckhr/

甲方向data binding

用props的方式設定資料:

或是用state的方式設定資料:

然後在render函式裡面寫獨特又優雅的jsx語法:

一律由state或是props一層一層將data往子元件傳,超級優雅的甲方向one-way data binding寫法。

這就是一談到one-way data flow,人們就想到React的原因。

乙方向data binding

寫一個監聽變化的函式,一監聽到就去更新data model:

然後設定好onChange這個prop即可:

評論

乙方向data binding做得到,官方甚至提供Helpers使用,還用Two-Way Binding Helpers描述它。

但是實務上通常會採取某種程度的Flux架構(永遠經手action和store去更新UI),因此不會使用這種Helpers。

所以大家都說「React是one-way data flow」而不會說「React是two-way data binding」。

話雖如此,換個方式,說「React會強迫做到one-way data flow」比較好。

至於React跟two-way data binding的關係就不重要了。

不過就是個做得到但實務上不常用的手法。你覺得是就是,你覺得容易就容易吧。

結論

透過這篇文章的脈絡,你會發現隨便套個模板系統,然後在每次data model更新時用下面任一方式重新render出UI,都算是甲方向data binding:

* 全手動(jQuery範例的呼叫render)
* 半手動(Backbone範例的設定模板與model on事件)
* 自動(Angular與React)

至於乙方向data binding就更多元了:有的看起來像是全手動更新,有的看起來像是半手動監聽事件然後update model,有的看起來像是宣告bindings即可,有的寫個ng-model的directive就完成。

光是甲或乙方向data binding的定義就如此鬆散,自然one-way或是two-way的爭論會應運而生。

其實,在挑選框架的時候,不管是標榜one-way或two-way的框架,背後終究都只是資料傳過來傳過去而已,差別不過在於框架本身替你做掉多少事情。

然而,框架替你做多做少都不是重點,畢竟做多本身也代表入門越困難、convention越多。

只要整體用起來順手、好理解、好維護,那麼任何一種寫法都是最好的寫法。

(完)


參考資料

http://stackoverflow.com/questions/13504906/what-is-two-way-binding

http://stackoverflow.com/questions/30590170/simple-practical-example-for-two-way-data-binding-in-angularjs

(Photo via TheGiantVermin, CC licensed.)

learning

給網站初學者的建議:用 Ruby on Rails 非常辛苦,用 PHP 非常舒服

Rails是新手入門學寫網站最辛苦的選擇之一。

常常有文章或是課程建議新手用Rails入門寫網站,我認為這種建議十分危險。

Rails並不適合初學者使用。推薦沒有資訊背景的人去學Rails,很可能害他事倍功半。

新手用Rails入門的問題在哪?

Rails最大的問題就在於:它從一開始就不是設計給新手用的。

它是設計給工程師快速開發用的。

軟體工程沒有萬靈丹。適合老手的,對新手來說一定太難;適合新手的,對老手來說一定太囉唆。

Rails為了替工程師節省反覆設定(configuration)的時間,而預設了許多立場(convention)。

新手連web application的基本知識、環境設定都不知道,

直接去學前人的framework和convention,絕對不是好事,也非常辛苦。

我們從Rails的官方入門導覽來舉例吧:

http://guides.rubyonrails.org/getting_started.html

對新手來說,這份導覽真是充滿了災難。

它就跟大部份的rails教材一樣,會帶新手去學很沒必要的4件事情:

1. 新手沒必要學框架的檔案結構
2. 新手沒必要學框架的command line指令
3. 新手沒必要學helper
4. 新手沒必要學migration

1. 新手沒必要學框架的檔案結構

新手打開導覽之後,映入眼簾的,是這個介紹檔案結構的嚇人表格:

File/Folder Purpose
app/ Contains the controllers, models, views, helpers, mailers and assets for your application. You’ll focus on this folder for the remainder of this guide.
bin/ Contains the rails script that starts your app and can contain other scripts you use to setup, deploy or run your application.
config/ Configure your application’s routes, database, and more. This is covered in more detail in Configuring Rails Applications.
config.ru Rack configuration for Rack based servers used to start the application.
db/ Contains your current database schema, as well as the database migrations.
Gemfile
Gemfile.lock
These files allow you to specify what gem dependencies are needed for your Rails application. These files are used by the Bundler gem. For more information about Bundler, see the Bundler website.
lib/ Extended modules for your application.
log/ Application log files.
public/ The only folder seen by the world as-is. Contains static files and compiled assets.
Rakefile This file locates and loads tasks that can be run from the command line. The task definitions are defined throughout the components of Rails. Rather than changing Rakefile, you should add your own tasks by adding files to the lib/tasks directory of your application.
README.rdoc This is a brief instruction manual for your application. You should edit this file to tell others what your application does, how to set it up, and so on.
test/ Unit tests, fixtures, and other test apparatus. These are covered in Testing Rails Applications.
tmp/ Temporary files (like cache, pid, and session files).
vendor/ A place for all third-party code. In a typical Rails application this includes vendored gems.

初學者一開始只想做個部落格或是個人網頁而已,真的有必要去面對這麼多檔案嗎?

這種架構是框架替工程師的軟體專案長遠著想,而詳細分工的結果。

新手並沒有要開發大型專案,做出幾個能跑能動的頁面就夠了。

結構或是程式碼醜一點無所謂,好上手就好,實在沒必要一開始就碰這麼複雜的。

2. 新手沒必要學框架的 command line指令

官方導覽介紹了產生controller相關檔案的指令:

然後rails會顯示以下訊息:

只不過想做個簡單頁面,居然跑出了好幾個檔案。對新手來說,這非常不友善。

新手想要的,是在資料夾裡面一次建立一個檔案,然後在裡面寫一點東西。

接著看看程式有什麼反應、跑不跑得動。這樣他才知道自己在做什麼、在學什麼。

像這種打一個指令產生一堆檔案的學習方法,就算真的「快速做出了一點東西」,

之後大概連怎麼修改程式都不知道。如此黑箱,很容易帶來挫折感。

3. 新手沒必要學helper

官方導覽提到了Form的寫法如下:

這完全是災難一場。

新手該學的不是這種ERB語法,而是原始HTML的Form element語法:

Form element的語法一點都不難,網路上到處找都有

新手學HTML基本知識就夠了。接著再學一些HTTP的基本知識,了解瀏覽器如何將表單的資料丟給後端程式互動。

學這些基本知識才踏實、才有成就感。

helper是工程師為了少寫HTML而使用的進階工具。

初學者連基本的HTML都不會,學什麼helper?徒然增加挫折感而已。

4. 新手沒必要學migration

migration是工程師方便管理資料庫結構的版本,以及在團隊成員間同步化的工具。

初學者根本沒有這種管理資料庫的需求。

新手如果要學資料庫操作,應該去學資料庫軟體(譬如說MySQL)本身的語法,這樣才會有學到資料庫操作的扎實感覺。

如果是一開始不想學資料庫語法的新手,那更不應該去學migration。

他應該去安裝一款有圖形化介面的資料庫管理軟體,用滑鼠按一按把資料表做出來就可以了。

初學者最舒服的學習路徑:PHP + 懶人包

初學者對於開發網站的想像,不外乎就是「寫幾行程式碼,做出幾個小頁面」。

他一開始對於環境安裝與調整沒有興趣,也負荷不來。

這個時候,使用PHP搭配懶人包開發會是最舒服的路徑。

學寫網站至少要碰幾個東西:

  • HTML
  • CSS
  • client-side programming (JavaScript)
  • server-side programming (ex: Ruby on Rails 或是 PHP)
  • 資料庫
  • 架server

我對新手學習的建議是這樣的:

1. HTML和CSS幾乎沒有入門門檻,線上隨便找教材都有。
2. JavaScript主要是用來做瀏覽器上的一些動態效果,非必要。一開始先不學。
3. 資料庫跟架server一開始超出新手想像,可以先安裝懶人包帶過。
4. server-side programming才是新手原本理解的「學寫網站」。

基於以上四點建議,新手一開始把心力放在1、4即可,然後從以下懶人包擇一安裝:

這些懶人包會直接幫你裝好server、資料庫與PHP。

資料庫相關的操作可以先透過phpMyAdmin之類的軟體用滑鼠操作。

接著在線上找PHP教學之類的東西,以「一個檔案就可以做出一個網頁」的方式開始學習。

在懶人包的協助下,按照興趣慢慢摸索HTML, CSS和PHP的知識。

之後再根據目標,從以下三個方向更進一步學習:

  • 急著讓網站上線者,可以把程式碼交給工程師朋友,請他幫忙買網址、架起來。
  • 對server side工程有興趣者,可以補強資料庫、伺服器等等知識。試著不依靠懶人包就獨立搞定環境、挑一個框架學習之類的。
  • 對client side工程有興趣者,可以補強JavaScript的知識,學習前端相關知識。

入門最重要的是循序漸進,先想辦法做出一個能動的網站,

之後再根據需求,把缺少的技能一個一個補起來。

在「實作出成品」與「學習新知識」之間來回進行,反覆累積成就感與能力。

一口氣全部一起學,只會非常茫然、倍感挫折而已。況且根本沒有必要。

用Rails就更慘了,在一開始就得學一些工程師用的進階手法。

總結起來,我給網站初學者的建議就是:用 Ruby on Rails 非常辛苦,用 PHP 非常舒服。

(Photo via Per Gosche, CC licensed.)

big

[推薦]學寫程式的幾本書籍

常常被朋友問到,說他工作上有某某需求,想學某某語言,有沒有適合他的書本?

有的,我的確有推薦的入門書籍。

這裡寫文章一次回答這些問題。

Q: 我沒有任何基礎,想入門程式設計,希望能叫電腦替我做些事情?

我推薦「深入淺出程式設計」。
programming
Q: 我沒有任何基礎,想入門程式設計,學架blog、網站、個人網頁?

我推薦「深入淺出 PHP 與 MySQL」。
php-mysql
Q: 我不想學寫程式,只想學設計相關的HTML&CSS?

我推薦「深入淺出HTML&CSS」。
html-css
Q: 我有一點基礎,但想更了解Python的初級、中級觀念?

我推薦「深入淺出 Python」。
python
Q: 我有一點基礎,但想更了解Java的初級、中級觀念?

我推薦「深入淺出 Java 程式設計」。
java
Q: 我有一點基礎,但想更了解JavaScript的初級、中級觀念?

我推薦「深入淺出 JavaScript」。
javascript

你可能會覺得奇怪,怎麼全是同一套系列的書?

這也沒辦法,因為深入淺出系列就是如此出色。

整套系列,我買入手的、曾經去圖書館借來看的,就在10本以上。

歐萊禮的這系列書籍,跟一般的電腦書籍非常不同,

它會兼顧你學習、應用、樂趣、知識,多個入門最需要的面向。

試了很多本電腦書籍,卻依然一頭霧水的朋友,不妨試試看深入淺出系列。