Skip to main content

2014完食的書

· 5 min read

2013 年曬了一下書架,2014 年過去了,終於今年沒讀破百本。(今年共讀 86 本,16052 頁)也許是由於今年做了許多不甚成功的專案,佔用了不少時間,或是因為家中小朋友 1 + 1 的效應開始顯現。但其實也看了不少大部頭的書。值得注意的是看的 86 本中,電子版已佔了 32 本(38%)。

來曬一下今年看過的書,順便從中推薦些好書(由近至遠)

今年剛開始看了 Google/Apple 世紀大格鬥與「什麼都能賣!:貝佐斯如何締造亞馬遜傳奇」的 kindle 簡體版。瞭解各大巨頭間的糾葛與發跡史還蠻有意思的。

「超速習法」對於閱讀前的「待機」心理準備部分歸納的非常完備,後半實際閱讀時的方法相對表達地沒那麼清楚。

Kindle 上看完的「哲學家們都干了些什麼?」。透過一邊介紹有代表性哲學家的故事,將哲學起始到近代的哲學思想都點水過了一遍。對於我這樣的初學者來說還是很有幫助。

過年和元旦依舊是漫畫的領地,「銀之匙」實在是部很有趣的漫畫,身在台灣就能體驗日本的農校生活。

為了稍微瞭解 Android 底層開發以完成「Beyond Web and FirefoxOS」的撰寫,翻譯了「Android BSP 與系統移植開發」一書。

在 kindle 上複習了一遍 yy 小說「冒牌大英雄」,另外也拜讀王伯達先生的新書「再見,世界工廠」,「生產才能創造踏實的經濟成長」等想法在年末看到時寒冰先生的「未來二十年,經濟大趨勢」中也有出現。

數位新時代

在過去十年裡,資訊的獲取方式發生了巨大的變化。可汗學院、維基百科、數以億計的部落格,多如牛毛的網站,只要你有興趣,就可以找到任何資訊,不論你在哪裡。學校的資訊媒介作用已經不再像過去那麼重要,培養孩子「發現資訊的願望」在現在更為重要。

再見世界工廠

這些年可以看到對岸的改變,應進一步認識與了解對岸與東南亞的週邊鄰國發展狀況

我對圖解與量化自我類的書一直沒什麼抵抗力。這本算言之有物,但一年過去了,實踐上還是差了點 /_\。

各種時間管理的入門概念,沒看過科維,GTD 的人從這邊入門也不錯

從年初開始看到年中才看完「Javascript Pattern」,作者很仔細地推導各種 Javascript 模式,相當有意思。

「遊戲化時代」 這本書的作者之前有在 coursera 開 MOOC 課程,雖然我沒上完,但可以感覺到課程內容比書中提到的更多元。這次終於讀完整本。

郝明義先生的工作 DNA 三卷也拜讀完了,值得一觀。小米的「參與感」和 360「我的互聯網方法論」書雖然打廣告意味濃厚,但比起台灣的捧 CEO 類型商業書來,多了些實在的想法。

「一個人的經濟」 前半段講到日本即將面對的現況,不久也會出現在台灣。提早察覺到的趨勢,可以讓我做些什麼哩?

小旅行,繪本,工作,時間管理,哲學,趨勢。我的閱讀依然雜食。

PS: 感覺 Anobii 已經幾乎不更新,許多書已經不是輸入 ISBN 就可以自動出現,所以今年的書單裡還是有些漏書。也許看倌可以建議我將書架搬去哪好?

移民效應,是自幹一套還是拿別人的 Open Source 來用?

· 4 min read

Marissa Mayer 與起死回生的雅虎 一文中,可以看到雅虎工程師對自家 CEO 的讚美。其中一點提到雅虎內部有很多系統設計是與外界完全不一樣的,直到新 CEO 上任才放下使用多年的 YUI 而擁抱其他 Open Source 團隊維護的專案。

從樂觀的角度看:因為當初 Yahoo 工程師夠強(Douglas Crockford),覺得自己做的東西比別家的好,所以乾脆自己做。有這種「愛自幹」的精神,所以才會做出很多跟其他地方不一樣的東西,這是 feature。從反面看則是因為長期跟人家用的工具不一樣,所以沒辦法直接使用別人做的 Open source 工具,這也是 bug。

以前的 Yahoo「不擁抱其他 Open Source 團隊維護的專案」嗎?Yahoo 當然某種程度是支持 Open Source 的公司。就我所知,Yahoo 是間很早開始支援 Hadoop 開源專案的公司。Yahoo 使用 Hadoop,也延攬 Hadoop 開發者進公司,所以「不擁抱其他 Open Source 團隊維護的專案」這個論點也許是存在的。

我們來看看 Google。類似於 YUI,Google 自己也有一套 Closure Library,多數 Google 的產品都建構於其上,雖然開源,但外面專案會使用它的也少之又少。 而 Android 呢,AOSP專案裡也有不少只能在 Android 工具鏈中使用的工具,但從以前基於 Eclipse 的 Android 開發工具到最近釋出的 Android Studio 1.0 是基於 IntelliJ 的開發工具,部分的 Google 似乎較接受擁抱其他 Open Source 團隊維護的專案。

在寫下這兩段話的同時,我比較這兩家公司擁抱其他 Open Source 團隊維護的專案的原因,竟發現兩者改變的相同點在於「併購」。當 Google 併購 Android,就接受了 Android 團隊與他們提出的開發工具。當 Yahoo 開始大採購,許多新創團隊進入 Yahoo 大家庭的同時,也帶進各團隊使用的「其他 Open Source 團隊維護的專案」。也就是說,當新移民團隊進入這個團體時,帶來了改變。

比起大公司而言,新移民團隊在被併購前的資源有限,使用其他 Open Source 團隊維護的專案是很自然的事。團隊被併購後若需使用併購方的技術重新打造產品,將是相當痛苦的事情。因此在 Yahoo 大採購後許多原有規矩被改寫,恐怕背後也有這些新團隊帶來的影響。

用社會學可能可以解釋為什麼是團隊而不是個人?因為改變一直是困難的事。原有的團隊在過去磨合 / 開發的過程中已經凝聚出了一些共識。移民到新團體的過程中,新移民團隊需要參與和適應團體的一些運作方式,而團體則需接納和適應新移民團隊的一些習俗。

回來想想 Mozilla 也做了很多 Open Source 專案,但整套工具常常僅供自己相關的專案使用。 從自身找原因,自家工具的開源專案原本就是為了滿足自己的需求。當外部出現對這套工具的需求時,由於公司人力資源調配,優先要滿足的還是自身專案時程的需求。 當用到的 Open Source 專案在公司裡就有人可以諮詢,當然比去找其他 Open Source 團隊維護的專案來地可靠(?)。許多專案在這樣的思維下就使用自己寫的工具,當意識到的那天,已經回不去了。

若想避免現在的 feature 變成未來的 bug,在決定自幹專案工具前,先想一想(或 search 一下),也許結果會不一樣。

使用 FLUX 架構的概念,漸進改善一般 Javascript 程式碼的組織方式

· 11 min read

前陣子 Facebook 推出一套名為 FLUX 的前端程式架構,期望能幫過去沒有條理,程式多了結構就亂得像一團麵條的 Javascript 程式寫法找到一個理想的組織方法。

FLUX 簡介

視圖(View)-> 操作(Action)-> 分配器(Dispatcher)-> 資料處理器(Store)-> 繪圖者(Renderer)-> 視圖(View) FLUX 的基本原理有別於常用的 MVC(Model/View/Controller)或 MVVM(Model/View/ViewModel)是在 M,V,C(VM)三者之間互相傳遞或修改資料,

MVC (image from fluxxer)

FLUX 重新定義整個組織架構為單向的視圖(View)-> 操作(Action)-> 分配器(Dispatcher)-> 資料處理器(Store)-> 繪圖者(Renderer)->  視圖(View)的運作流程。

FLUX (image from fluxxer)

就我的理解,FLUX 的架構可以拆分為三個重點流程:

  • 跟視圖(View)有關的操作(Action),都透過事件註冊到分配器(Dispatcher)去。
  • 分配器 (Dispatcher)負責將操作(Action)傳遞給需要的資料處理器(Store)。
  • 資料處理器(Store)負責跟資料直接相關的操作。若資料處理器(Store)修改的結果需要反映到視圖(View)上,可以透過發送訊息通知給繪圖者(Renderer)處理。這邊講到了原本 FLUX 概念圖中沒有提到的繪圖者(Renderer)這個角色,在 Facebook 中他們是用 ReactJS 處理。

瞭解其基本架構之後,我發現其實就算不用他們提供的函式庫,用 Javascript 內建的 addEventListener, handleEvent, customEvent 等方法,也可以利用前面所提的三個重點,漸進寫出符合 FLUX 精神的程式。

目前的 JS 組織方式

一個常見的 JS 檔案,一般的架構是

var App = \{    init: function app_init() \{

      //get view       this.view1 = document.getElementById ('xxx1');       this.view2 = document.getElementById ('xxx2');             //do stuff       this.view1.addEventListener ('click', function () {         //do something       });       this.view2.addEventListener ('keyup', function () {         //do something       });     } }; 若我們想要將視圖(View)的操作從 init 分離開來,大部分的人會這樣做

 var App = {     init: function app_init() {       // get view       this.view1 = document.getElementById('xxx1');       this.view2 = document.getElementById('xxx2');             // do stuff       this.view1.addEventListener('click', this.clickView1);       this.view2.addEventListener('keyup', this.keyupView2);     },

    clickView1: function app_clickView1() {        // do something     },

    keyupView2: function app_keyupView2() {        // do something     }

}; 如果在 clickView1 或 keyupView2 中要呼叫到 App 裡的參數或方法,那麼我們需要在 addEventListener 時使用 bind (this)

 var App = {     init: function app_init() {       // get view       this.view1 = document.getElementById('xxx1');       this.view2 = document.getElementById('xxx2');             // do stuff       this.view1.addEventListener('click', this.clickView1**.bind(this));       this.view2.addEventListener('keyup', this.keyupView2.bind(this)**);     },

    clickView1: function app_clickView1() {        // do something     },

    keyupView2: function app_keyupView2() {        // do something        this.clickView1();     }

}; 大多數書籍的範例大概就停在這裡,沒有再進一步探討程式的組織架構了。即使是龐大的 Javascript 專案如 Gaia,不少部分的程式碼組織方式也是如此。在這種組織方式裡,若有很多的視圖(View)需要操作或修改,我們的程式碼就會開始亂起來。

下面來試著將以上程式漸進改為 FLUX 架構。

改進建議一:將 handleEvent 當作 Dispatcher 來使用

跟視圖(View)有關的操作(Action),都透過事件註冊到分配器(Dispatcher)去。 (溫馨提示:IE 9 以上版本才有支援 handleEvent 方法,在之前版本上使用要加 polyfill)

我們先來想想看視圖(View)跟操作(Action)在前端 Javascript 程式中分別代表著什麼。 視圖(View)很明顯,就是透過 getElementById 等方法,從 HTML 中取得代表對應節點(Node)的元素(element)。

若想要套用 FLUX 架構,我們可以將附加在各個元素(element)上的事件行為分離,將事件註冊到一個統一的地方(分配器),在這個地方對不同的事件進行操作。

 Javascript 內建的分配器叫做 handleEvent,它可以拿來處理任何事件 Event,寫法如下。

 var App = {     init: function app_init() {       // get view       this.view1 = document.getElementById('xxx1');       this.view2 = document.getElementById('xxx2');             // do stuff       this.view1.addEventListener('click', this);       this.view2.addEventListener('keyup', this);     },

    handleEvent: app_handleEvent(evt) {        switch (evt.type) {          case 'click':            switch (evt.target) {               case this.view1:                 this.clickView1();                 break;            }            break;          case 'keyup':             switch (evt.target) {               case this.view2:                 this.keyupView2();                 break;             }        }     },

    clickView1: function app_clickView1() {        // do something     },

    keyupView2: function app_keyupView2() {        // do something        this.clickView1();     }

}; 這麼做帶來的明顯好處是所有的呼叫都統一在 handleEvent 中,可以更容易地追查到。

這麼寫也可以在 addEventListener/removeEventListener 時不用使用 bind (this),而 bind (this) 經常有些 side effect 需要特別留意。

例如假使我們想要反註冊 view1 上的 click 方法,使用以下寫法

 this.view1.removeEventListener ('click', this.clickView1.bind (this)); 其實並沒有將第一個 event 移除。因為使用了 **.bind (this) ** 後,回傳的其實是一個新的 instance...。 正確的寫法是 this.bindClickView1 = this.clickView1.bind(this)

this.view1.addEventListener('click', this.bindClickView1);

...

this.view1.removeEventListener('click', this.bindClickView1);用 handleEvent 可以省點事,要反註冊時也傳入 this 即可。 
this.view1.removeEventListener('click', this);

改進建議二:將資料處理的部分分離,使用自訂事件來改變 Store 狀態

分配器 (Dispatcher)負責將操作(Action)傳遞給需要的資料處理器(Store) 資料處理器(Store)負責跟資料直接相關的操作。在稍大的 Web App 中,我們可以另外定一個 Object 來處理資料相關的事宜。一般我們的寫法會是

// Store.js

function Store() \{

  this._data: 0; };

Store.prototype = \{

  getSomething: function s_getSomething() {     return this._data;   },

  doSomething: function s_doSomething() {     this._data += 1;   },

  setSomething: function s_setSomething(val) {     this._data = val;   } };

// App.js

var App = \{

  this.store = new Store ();   this.store.init ();   this.store.getSomething ();   this.store.doSomething ();   this.store.setSomething (2); }; 若想要套用 FLUX 架構,首先我們要避免從資料處理器(Store)外部直接改變資料處理器(Store)。我們可以透過在呼叫端使用 window.DispatchEvent 發送自訂事件(CustomEvent),並在資料處理器(Store)中接收自訂事件來做到。

如此一來,資料處理器(Store)將只留下 get 方法來讓外部取得 Store 想提供的資料。

另外如果程式碼改善進入到下一個重點,在操作(Action)時應該不需要再呼叫 Store.getSomething 了,我們將資料處理器(Store)的 getSomething 方法留著給繪圖者(Renderer)使用 。

// Store.js

function Store() \{

  this._data: 0; };

Store.prototype = \{

  init: function s_init() {      window.addEventListener('store_do', this);      window.addEventListener('store_set', this);   },

  handleEvent: s_handleEvent(evt) {     switch(evt.type) {       case 'store_do':         this.doSomething();         break;       case 'store_set':         this.setSomething(evt.detail.val);         break;     }   },

  getSomething: function s_getSomething() {     return this._data;   },

  _doSomething: function s_doSomething() {     this._data += 1;   },

  _setSomething: function s_setSomething(val) {     this._data = val;   } };

// App.js

var App = \{

  this.store = new Store();   this.store.init();   //this.store.getSomething();   **window..dispatchEvent(new CustomEvent('store_do'));   window..dispatchEvent(new CustomEvent('store_set',     {'detail':{'val':2}}   )); ** }; 這麼做帶來的明顯好處是測試時可以簡單地將 Store 與 App 分開來測試,這對大型 App 是很重要的。

改進建議三:讓 Renderer 來處理視圖

若資料處理器(Store)修改的結果需要反映到視圖(View)上,可以透過發送訊息通知給繪圖者(Renderer)處理 > //  Renderer.js

var ClickRenderer = \{

  init: function s_init(element, Store) {      this.element = element;      this.store = Store;      window.addEventListener('render_view1', this);   },

  handleEvent: s_handleEvent(evt) {     switch(evt.type) {       case 'render_view1':         this.element.textConent = this.store.getSomething();         break;     }   }};

// Store.js

function Store() \{

  this._data: 0; };

Store.prototype = \{

  init: function s_init() {      window.addEventListener('store_do', this);      window.addEventListener('store_set', this);   },

  handleEvent: s_handleEvent(evt) {     switch(evt.type) {       case 'store_do':         this.doSomething();         break;       case 'store_set':         this.setSomething(evt.detail.val);         break;     }   },

  getSomething: function s_getSomething() {     return this._data;   },

  _doSomething: function s_doSomething() {     this._data += 1;     window..dispatchEvent(new CustomEvent('render_view1'));   },

  _setSomething: function s_setSomething(val) {     this._data = val;   } };

// App.js

var App = \{

  init: function a_init() {     // get view     this.view1 = document.getElementById('xxx1');     this.view2 = document.getElementById('xxx2');     this.store = new Store();     this.store.init();     **ClickRenderer.init(this.view1, **this.store);   },

    handleEvent: a_handleEvent (evt) {     window..dispatchEvent (new CustomEvent ('store_do'));     //Store.setSomething (2)     window..dispatchEvent (new CustomEvent ('store_set',       {'detail':{'val':2}}     )); }; 上段程式在 App 中註冊了 ClickRenderer,並傳入 Store 與 所需的 View 元件。所有的介面更新全交由 ClickRenderer 處理。

(另一個方法是讓繪圖者(Renderer)監看資料處理器(Store)的狀態,然後去改變視圖(View))  http://fluxxor.com/documentation/store-watch-mixin.html

總結

整理完後,一般 javascript 套用 flux 架構的運作流程如下:

簡而言之,上面的各種建議是鼓勵大家多使用 Javascript 內建的 addEventListener, handleEvent, customEvent 等方法。透過大量使用 event,我們可以改善 Javascript 程式邏輯,資料,與介面元件之間的關聯程度。

將 FLUX 架構拆分為三個重點流程來理解或實踐的好處,是我們能漸進地遵循其中一些方法來改善我們現有的程式架構。

以上是我關於如何使用 FLUX 架構在一般 Javascript 組織方式的第三個版本,可能有些錯謬之處,還迎大家討論或給予建議。

FoxBox and SummerOfCode14

· 2 min read

FoxBox is the project that intent to provide a battery included Firefox OS build environment.

The goal of foxbox is to try any approach that make new user can do as less as possible to start the FirefoxOS development

Our first take is use vagrant with virtualbox to make major platform users can try FirefoxOS dev in VM.

It will be great to setup the current version of foxbox in your desktop environment

And record obstacles you encountered here https://github.com/gasolin/foxbox/issues?state=open. There are some issues (but not the limit) that might be worth to do in the future version of foxbox.

Note that you require a desktop with INTEL VT-x/AMD-V hardware virtualization support(Windows8 or Mac already enabled it), at least 4GB RAM and about 10~40GB disk space(for gaia or full B2G development).

FoxBox has been approved by the Google Summer of Code administrator http://wiki.mozilla.org/Community:SummerOfCode14 , so its perfect time to step up, try FoxBox, fix issues that every others will encounter, save everybody's time and start make your own Firefox OS phone.

If you'd like to contribute FoxBox for SummerOfCode14. We expect you could find out the interesting topic you want to contribute or any other way that can better achieve FoxBox's goal.  

Android Wear 周邊

· One min read

Android Wear 不是作業系統,基本上是拿來開發 Android 周邊所使用的協定。它是把原本需要拿出手機查訊息,變成透過 Notification API 傳給手錶。透過 Google Now 手錶可以收到推播來的相關訊息或透過語音辨識來控制手機應用做事情。

因為 Android Wear + Google Now 帶來的便利性,Google 可以再進一步加強圍繞著 Play Service/Google Now 的生態圈。