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本以上。

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

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

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

sun

先寫單元測試的12個好處!

「寫測試的好處是什麼?」

這個問題非常難回答。通常只能得到「只有寫了才知道」這種含糊不清的答案。

我最近找到Tim King在2006年的文章,非常完整的回答這個問題。

看過之後覺得獲益良多,翻譯出來和大家分享。


先寫單元測試的12個好處!

為什麼工程師會討厭寫測試?為什麼他們會拒絕先寫測試?不用解釋了,那些藉口我全聽過。我知道真正的原因為何。

大部份的工程師根本沒認真試過測試先行。不然就是當下的環境不支持他們寫,導致他們不知道自己在幹嘛。前者的情況居多。最後他們就找藉口:「我們沒時間寫單元測試」、「單元測試不能完全保證程式碼品質」。這是在替自己的悲慘找理由,而不是真的覺得不寫比較好。有趣的是,當這種人直接在你面前開發,你常會看到他開發得很不順,進度常常在開倒車。目睹這種事滿有趣的。但他依舊不會收回原本的看法、依舊不願意先寫測試。這種堅持根本有害。

Kent Beck在《Test Driven Development By Example》書中提到,測試先行有3個步驟:

1. Red:寫個能表達你打算如何使用那段code的測試,還有你期待它做什麼。這個測試會失敗。很多介面會用紅色訊息來表示它。
2. Green:寫出足夠的code來讓那個測試成功,但別多寫。如果你想寫更多code,像是檢查某些錯誤的話,那就先另寫一個測試表達它。當下只要寫剛好夠的code去通過測試即可。
3. Refactor:把多餘的code清理一下,然後改善整體設計。之後再跑一次測試,確保沒弄壞什麼地方。

重複這些步驟直到功能做完。這個流程超級簡單。為什麼工程師會畏懼它?因為這會逼他們從根本上改變開發習慣。

我們認為自己在寫code前不需要先去思考,這些code到底要做什麼。

你如何解決一個軟體問題?學校是怎麼教的呢?第一步怎麼走?你大概只想著如何解決問題本身。你心想:「我要寫哪些code來實作出解決方案?」其實你不應該先想「我要寫哪些code」,你要先想「我要怎樣才能確定問題已經被解決了?」

我們直覺認為一段code的正確與否,只要執行一次就知道了,超明顯的,何必寫那種根本廢話的測試?就是這種這種根深蒂固的想法,導致大部份的人改不了開發習慣。

成功跨過那道鴻溝的人,可以感受到下列幾項好處。我全部體會過。不用完全信我,你自己試試看就知道了。

1. 單元測試保證你的code真的能動

這會讓bug減少。當然,單元測試不能取代系統測試跟驗收測試。但單元測試能補足它們的短處。

2. 你會得到一組底層的regression-test suite

這讓你隨時可以回頭去檢查有否哪邊壞掉、bug在哪。很多團隊會每天把整組測試跑一次。這讓你在把程式交給品管部門之前,可以很輕鬆的把bug抓出來。

3. 讓你改善系統設計的時候,不怕弄壞系統

其實就是測試先行3步驟的第3步。通常測試先行寫出來的code不太需要重構。我看過很多超糟糕的系統,就像精神病患一樣,根本無法搞定。如果有準備好單元測試,你就可以對系統裡面最難搞的部份做出有效的重構。

4. 寫測試會讓coding更好玩

你會先搞懂自己的code要做什麼。然後再讓它完成任務。就算系統還沒全做完,你還是能看到code真的動起來,而且真的沒出錯。你會得到一種「我完成了!」的感覺。每分鐘都會不斷感受到喔。只要試試測試先行,你就會整個人high起來、對自己的作品感到驕傲、被激勵去完成更多事情。

5. 它們可靠地展現目前進度

你不用為了等整個系統組裝起來而多等一個月。在系統完成之前你就能展示進度了。不但能說自己寫了code,還能真的跑給別人看。傳統開發有件事搞錯了。「完成」不等於你寫了code然後丟出去。「完成」應該是你的code能在系統裡跑,而且沒bug。寫測試會讓你更接近這點。

6. 單元測試是一種使用範例

我們都碰過那種不知道怎麼用的library。通常我們會先去找範例程式碼。使用範例可算是一種文件。但公司內部的code通常不會有範例可看。所以只好慢慢試、在系統內東找西找了。因為那個同事可能根本離職了,想問他都沒辦法。單元測試可以當作一種文件。當你不知道Foo類別怎麼用,去看一下單元測試怎麼寫的即可。

7. 測試先行會強迫你寫code前先做規劃

先寫測試會逼你在動手開發前把必須完成的事和整體設計想過一遍。不但讓你更專注,還能讓設計更漂亮。

8. 先寫測試能減少bug的成本

越早發現bug越容易修。之後出現的bug通常是改了好幾個地方才出現的,
導致很難抓出哪裡導致了bug。一開始先找出bug在哪,然後要重新回想這段code是怎麼寫的,因為可能是幾個月前寫的。最後才終於弄懂,搞出一套解法。只要能減少抓bug以及修好bug的時間,幾乎都算大賺。如果在成品交給品管部門或是顧客之前,我們只花幾天就找出bug,通常算是很幸運。那幾花幾分鐘就找出bug呢?測試先行就能做到這點。

9. 它比代碼檢查的效果好

有人說事前代碼檢查比事後測試系統更好,因為成本比較低。在系統完成之後才測試系統,要修好bug可說是麻煩多了。越早發現bug,就越簡單、越便宜、越好搞定。代碼檢查的好處就在這:只花幾天就能抓出bug,不需要等幾個月。但是測試先行成本更低。只要幾分鐘就抓出bug,連幾天都不用。

10. 幾乎解決了「開發者瓶頸」(coder’s block)

不知道下一行寫什麼嗎?就跟「作家瓶頸」(writer’s block)一樣,開發者瓶頸很可能是個大問題。測試先行有系統地處理開發上關於結構的部份,讓你能專心在需要創造的部份。你可能會卡在下段code不知道怎麼測、該怎麼通過測試,但你永遠不會因為下一步卡住。通常會有完全相反的結果:你很想在累倒之前休息一下,但因為清楚看到前面的錄了,所以根本不想停下來。

11. 單元測試讓設計更棒

測試一小塊code會強迫你定義清楚那段code負責什麼。如果測起來很簡單,就表示它的責任很明確,cohesion很高。如果一段code能被單元測試,那就表示它很容易就能放進系統之中,就跟它很容易放進測試之中一樣。它跟相關的code只具有loose coupling 。 High cohesion與loose coupling代表了出色、好維護的設計。容易測試的code也很容易維護。

12. 寫測試會讓開發速度更快

不寫單元測試也許會讓速度更快,但無法保證code真的能跑。開發上會花一堆時間在在事後的修bug。測試先行會消除這類的浪費,從一開始就做對、讓bug更好修。

就算好處這麼多,很多工程師還是繼續維持他們的老樣子。如果你在組織裡極度重視流程,你跟他們一部份人會起衝突。我只能祝你好運。記住一件事,人們不會因為一個東西聽起來不錯就買帳。他們只有在極度渴望、超想得到手來品嚐時才會買帳。希望以上幾點可以幫助你說服他們。

不過,如果你是前者,也就是那種頑固的工程師,不在乎好的軟體設計,只在乎堅持己見…。嗯,我覺得你還真可憐。

(Photo via 8#X, CC licensed)

by 阿川先生