2012年1月28日

悼 Elaster Labs

上個禮拜得知 TCloudElaster Labs關閉服務了,雖說我個人一開始就不太看好在臺灣發展 Platform as a Service 雲端服務的前景,但是看到這麼多熱血工程師苦心建構的服務要收掉,還是不勝唏噓,特別為文悼念。


Elaster CAP 這個平台我並沒有用過,從安裝手冊來看,

Elaster CAP 是個可以免費下載裝在自己的叢集上的 PaaS 軟體系統。

它把 OpenLDAPHadoopZooKeeperHBaseSolrNginx 等等開放源碼軟體包括在內,並且可以和 MySQL 連接,再加上自己開發的軟體,組合成一個平台。一般 PaaS 只有 hosted、沒有可下載的平台軟體,Elaster CAP 的模式很不同。而 Elaster Labs 就是讓大家上傳、測試自己的應用的 hosted Elaster CAP 環境。

搭建一個平台牽涉到的問題繁多,如儲存系統的選用、API 的設計、效能與易用之間的取捨、平台的穩定度、多個租戶之間的隔離、資源的調配、負載的平衡... 等等工程問題,還有文件的齊備度、技術支援的速度與深度、移植應用程式的難易度、甚至平台提供者的知名度,在在影響開發者是否採用該平台的決定,因此支撐起一個平台絕對不是簡單的事。GoogleApp Engine 也是花了巨大的力氣,才逐漸成熟脫離 beta。然而一脫離 beta 就因新定價策略遭受批評,也顯示出 PaaS 營運之不易。

因為以下幾點,我對趨勢科技TCloud 的創辦人張明正董事長是很欽佩的:

  • 不論算公司規模、算營收、算知名度,趨勢科技應該都是從臺灣起家最大的軟體公司,這是從無到有、自小變大的能力。
  • 他在雲端運算剛開始受到矚目沒多久,就投資創立 TCloud,大力延攬工程師進入開發,這是大刀濶斧的氣魄。
  • 為了培育人才,他在臺大開闢【雲端計算趨勢學程】,據聯合報 2010/04/05 報導
    台大雲端計算趨勢學程主任、台大資工系主任呂育道說,據他了解,這應該是大學首開的相關學程,該校這個學程是趨勢公司捐贈的學分學程,師資除了數十位台大資工系 (所)及資訊網路與多媒體研究所教授,還有中研院院士孔祥重等人。
    這個學程的師資陣容可是不同凡響。或許有人會說這是企業打廣告、搶人才的手段,但無論如何他真的有實際投入資源來栽培學生,學生畢業後也沒有義務要進入 TCloud。下面這句聽起來像八股,可是臺灣真的太缺乏這種有氣度的企業主。
  • TCloud 工程師辛苦開發出來的 Infrastructure as a Service 系統 "Elaster",據 TCloud 網站宣布,將以 GPL v2 的授權條款將源碼釋出,如此開放的胸襟更是與眾不同。

姑且不論 Elaster 何時會準備妥當釋出源碼,TCloud 的工程師已經對 Elaster 中用到的開放源碼軟體做出了實質貢獻。比方說,Ceph 這個分散式檔案系統裡至少就有 20 個來自 TCloud 工程師的 patch。

你也許會想:「20 個 patch 有什麼了不起?」

比起某些「拿 GPL 軟體來用、不順手的地方就改、改完若無其事」、「被 gpl-violations.org 來信警告下架再手忙腳亂應變」的公司來說,TCloud 這樣從一開始就遵守開放源碼遊戲規則的企業是一定要讚一下的!

更何況,在伺服器端使用 GPL v2 的軟體時,授權條款並不強制要把修改後的源碼公開出來,因此 TCloud 的行為更屬難得,相信是完全了解開放源碼運作模式和成本才會這樣做的。

張明正董事長或許是企圖心旺盛,一開就是 IaaS 和 PaaS 兩條戰線。如今關閉實驗性的 Elaster Labs 服務,不知 Elaster CAP 未來命運如何?TCloud 是否打算把資源聚焦在 IaaS 上?這一兩年來開發 Elaster CAP 和維運 Elaster Labs 的經驗,相信對 IaaS 產品也有助益。雖然創業維艱、全球競爭激烈,仍然期盼 TCloud 能闖出一番名堂來!

2012年1月20日

一人一天一票,送進太空站!

台灣有三組同學入圍 YouTube 的太空實驗室活動,1/18 - 1/25 期間,全世界每人每天對 60 組中的每一組都可以投一票。這段時間台灣正好在放春節,太不利了!為了幫我們台灣的參賽同學推進下一輪,請大家把話傳出去,

一人一天一票,送進太空站!

  • 投票給玩太空清潔劑的【The diffusing phenomenon in microgravity conditions】台南女中-陳昕妤楊宜華
  • 投票給吹太空泡泡的【Can bubbles keep intact in a microgravity condition?】師大附中-蕭維廷成功高中-林秉均
  • 投票給種太空食物的【馬鈴薯種植實驗】政治大學-陳韋佑新竹高工-陳怡欣
今天投好了,
明天再來投!


投這個票有什麼意義?本來還想寫點文介紹這三個實驗的,最後想想,何必呢?在這個沒有對錯只比人氣的事情上,只需要老梗一句:我只知道義氣!台灣人挺台灣人。



真的想知道 YouTube 太空實驗室是什麼活動?就是 YouTube 和美國國家航空暨太空總署NASA)、歐洲太空總署ESA)、日本宇宙航空研究開發機構JAXA)、太空探險旅遊公司 Space Adventures聯想Lenovo)共同舉辦的活動,

  • 第一輪從全球 2,000 組參賽作品中選出 60 組入圍;
  • 第二輪公開由全球網友投票選出 6 組進入決賽; <= 現在在這裡(1/18 - 1/25)
  • 決賽再選出 2 個年齡組別各一組冠軍實驗;
  • 今年夏天 YouTube「太空實驗室」頻道將全球直播總冠軍的太空實驗在國際太空站上的進行實況。

剩下的細節可以參考聯合報蘋果日報的報導,我就不多說了。

投完台灣大選,來投世界大選!再說一次:

一人一天一票,送進太空站!

相關參考連結:

2012年1月16日

網路會是一根稻草嗎?

我這輩子沒寫過政治文,沒記錯的話,這是第一篇。

在人與人因網路而快速連結的時代,在 2012 大選後一天,忽然想寫點東西。

我永遠記得,1995 年在芝加哥費米實驗室,一位同組攻讀博士學位的美國學生滿懷信心地跟我說:"Internet will change China. Internet will force China to be more democratic." 他的論點很簡單。他認為中國之所以沒有實施真正的民主,是因為人民從小就以為共產黨灌輸的思想是正確的真理,資訊又被黨控媒體壟斷。那時 Web 剛剛起飛,有了無國界的 Web,資訊壟斷不再,人民遲早會了解「真正的民主」,而進一步要求民主的。

我搖頭,笑他太過天真,共產黨沒那麼笨。後來 Great Firewall 的出現,證明了我的看法,Web 倒底還是可以有國界的。

然而 17 年後的今天,不知道是 Great Firewall 忘記了還是什麼原因,台灣的民主選舉,透過網路讓許多中國大陸人民看到了原汁原味的影片和文字,從各個陣營的競選短片、政見辯論、到選後的勝選/敗選演講,這些沒被加料、減料、竄改的影像,赤裸裸地出現在許多人的電腦、手機、iPad 螢幕上,人們再利用社交網路把文字和影像傳播出去。

像我下面貼圖的第一則微博,是中國網友 topku 推薦蔡英文在競選總部前對支持者發表的落選演說。

  • 在中國推薦民進黨的演說,這是多麼不可能發生的事!
  • 一天之內有兩萬多人轉貼、五千多人回應,而且絕大多數是持肯定、欽佩的態度,這又是多麼難以想像的事!
  • 這裡面有沒有文化衝擊Culture shock)?這麼多人轉貼和回應,我想多少有所謂蜜月期的幻想的。
  • 會發生什麼效果?這只能留待時間去回答。

我這兩天在新浪微博上看到了許多文字,怕以後會不見,隨手節錄幾則。

「力薦蔡英文的演講,此客家老鄉感染力和氣場太強了,講話如此堅定自信和不卑不亢,絲毫沒有政客的做作:“台灣不能沒有反對的聲音,台灣不能沒有制衡的力量......你可以哭泣,但不要洩氣。你可以悲傷,但是不要放棄。因為明天起來,我們要像過去四年一樣的勇敢,心裡充滿著希望” http://t.cn/z0gKlhh」

------ 中間截掉 -----
topku 發的微博,帖中所貼的蔡英文演講 Flash 影片在新浪這裡
「有比較才有鑑別。這次看了馬英九、蔡英文、宋楚瑜在競選過程中和大選結束後的每場演講,眼界大開,感受到民主制度的魅力。在大陸,還沒有見過這樣生動、實在的演講。台灣,嫉妒死你了。」
地球周末發的微博
「總的說來我等修羅大陸,對岸桃花源雙黨都是攢足眼球,好感倍增,國民黨自不必說,連個一天之前還有絕大部分修羅人恨的咬牙切齒,十數年的敵視的民主進步黨,這次也在修羅大陸攢得口碑無數,一夜間掃去在修羅大陸的負面形象!桃花源人表示很詫異!修羅人說你是飽漢不知餓漢飢,桃花源人說你們是政治漁民」
木峰雲雲的心魔發的微博
「看到票數差距,第一次領會到大陸居委會阿姨們的力量;感悟演講內容,第一次享受政治家的魅力;思考民主進程,第一次認可“兩個國家”是利於台灣人民的好事。// @糖螂 :喜歡!」
沛松的客观世界發的微博
「中國不能沒有反對的聲音,中國不能沒有制衡的力量。有選舉,真好!弱弱的問一句,中國共產黨,我能反對你麼?// @imkket :轉發微博」
Tedcom 發的微博
「中國需要真正的普選。反對民主的人以廣大人民文化水準太低為藉口,企圖拖延民主的實行。選舉的能否進行和能否進行得好,主要關鍵在於人民有沒有發表意見和反對他人意見的權利,在於人民能不能真正無拘束的擁護某個人和反對某個人,至於選舉的技術問題並不是無法解決的。——《新華日報》1946年1月24日」
覃彪喜 發的微博

最後這一則反過來震撼到我。如果是真的,這是共產黨利用黨報在 1946 年向國民黨要求普選的喊話。對照兩岸的現況,恍如隔世。

2012年1月14日

也來聊聊物件導向程式的測試、封裝和相依性注入

今天是 2012 總統大選的日子,一大早就看到朋友 H.C. 貼出來的 Hinet 網頁截圖,實在是太歡樂了!



Hinet 的報票網站,在總統大選投票日 2012/1/14 早上 9:23 被網友 H.C. 抓到的截圖。投票還沒結束,票都開出來了?!

現在是 2012/1/14 傍晚 5:40,票剛開始開,早上 Hinet 就透過時光機看到結果了?還是有人早早「把票數算好了」?

我猜是有人在用上線系統做新元件的測試。

什麼年代了,這麼不專業的事還有人做?

就來聊聊物件導向程式的封裝和相依性注入好了,這和測試有關,也順帶聊一點點單元測試。

封裝和相依性注入

剛學物件導向程式設計時,一定學過要把類別(class)的介面設計好,

  • 找出對的抽象定義(Abstraction),讓使用類別 A 的人很容易理解 A 是做什麼的;
  • 適當地封裝(Encapsulation),讓別的程式不用了解 A 的實作細節就能正確使用 A。就像開車的駕駛,要加速只要會〈看速度表〉和〈踩油門〉兩件事,不用知道油門踩下去時,有什麼訊號傳到了什麼機構、用什麼方式控制了汽缸和噴油嘴的動作、甚至不用知道車子裡有這些機構存在。

等到開始寫有點規模的程式、有許多物件要互相合作時,一定會做單元測試(Unit tests),沒多久就會開始用仿製物件(Mock Objects)來測試程式的行為,總有一天你會聽到人家在說,相依性注入(Dependency Injection)是寫易測程式的重要技巧。

但你發現,相依性注入和封裝是互相矛盾的,怎麼辦?

如果你不知道為何矛盾,我來舉個例子。如果你懂,可以跳到下一節

封裝和相依性注入

假設你在設計一個網站的帳號系統,前台有個登入畫面,後台有個帳號資料庫。登入系統和資料庫顯然都是物件,所以你這樣寫:(我用 C++ 說明,但觀念應該適用在大多數物件導向語言上)
class AccountDB {
 public:
  AccountDB();
  bool GetPassword(const string& name, string* password) const;  // password 不會是明碼吧? XD
 private:
  // ...
};

class LoginSystem {
 public:
  LoginSystem();
  bool Verify(const string& name, const string& password) const;
 private:
  AccountDB* account_db_;
  // …
};

登入系統必需向資料庫要(編碼過的)密碼,才能驗證密碼是否正確。一種典型做法是在 LoginSystem 的 constructor 裡建立和資料庫的連結,把連結記住,之後就可以重複使用這個連結來讀密碼了。

LoginSystem::LoginSystem() {
  account_db_ = new AccountDB();  // account_db_ 是私有資料
  // ...
}

bool LoginSystem::Verify(const string& name, const string& password) const {
  string stored_password;
  if (!account_db_->GetPassword(name, &stored_password)) {
    return false;  // 也可以傳錯誤訊息
  }
  return Encode(password) == stored_password;  // Encode() 是某個編碼函式
}

這種寫法的抽象概念很清楚、封裝很乾淨,問題是 LoginSystem 和 AccountDB 緊緊卡在一起,如果你要測試 LoginSystem::Verify(),你必須在真的帳號資料庫裡塞測試帳號,然後餵測試帳號的資料進 LoginSystem::Verify()。這種做法很容易產生開頭講的問題,勸你千萬不要這樣做。

把測試系統和上線系統分離的一種做法是讓 AccountDB 的 constructor 接受 hostname 引數,就可以找台測試機器,在上面建個測試專用的資料庫,測試時不用碰到使用者的帳號資料庫。為了讓測試程式和上線程式用不同的 hostname 引數,就要讓 LoginSystem::LoginSystem() 也吃這個引數,好從 main() 和測試程式餵進去。

class AccountDB {
 public:
  AccountDB(const sting& db_hostname);
  bool GetPassword(const string& name, string* password) const { /* ... */ }
  // ...
};
class LoginSystem {
 public:
  LoginSystem(const string& db_hostname);
  // …
};

LoginSystem::LoginSystem(const string& db_hostname) {
  account_db_ = new AccountDB(db_hostname);  // account_db_ 是私有資料
  // ...
}

這種寫法程式不難測試,可是犧牲了一點封裝:為什麼登入系統需要吃個資料庫主機名稱?資料庫在哪裡不該是登入系統的實作細節嗎?

這樣的設計,LoginSystem 和 AccountDB 仍然是緊密結合的(Tightly-coupled),如果將來有一天因為現有資料庫不夠快、長不大、或是其他原因要換掉,新資料庫可能會有不太一樣的介面,那你該為它寫個 AccountDB2 類別吧?寫完還要改 LoginSystem 裡和資料庫聊天的程式。

這時你可能會想弄個 AccountDBInterface 介面,讓舊的 AccountDB 和新的 AccountDB2 都實作這個介面,LoginSystem 只需要和 AccountDBInterface 打交道就好了,至於打交道的對象倒底是哪個類別的物件?不重要。打交道的對象是哪個物件?LoginSystem 不必自行決定,由造 LoginSystem 物件的程式決定,再把資料庫物件和登入系統物件「送作堆」就好了。這個作法就是物件導向中很重要的 "Program to an interface, not an implementation" 概念。

所以程式變成這樣:

class AccountDBInterface {
 public:
  virtual bool GetPassword(const string& name, string* password) const = 0;
  // ...
};

class AccountDB2 : public AccountDBInterface {
  AccountDB2(/* 可能有不一樣的引數 */);
  virtual bool GetPassword(const string& name, string* password) const { /* ... */ }
};
// 以上是新的 classes

class AccountDB : public AccountDBInterface {
 public:
  AccountDB(const string& db_hostname);
  virtual bool GetPassword(const string& name, string* password) const { /* ... */ }
  // ...
};


class LoginSystem {
 public:
  LoginSystem(AccountDBInterface* db);
  bool Verify(const string& name, const string& password) const;
 private:
  AccountDBInterface* account_db_;
  // …
};

LoginSystem::LoginSystem(AccountDBInterface* db) {
  account_db_ = db;
  // 或許要檢查 account_db_ 不是 NULL
  // ...
}

寫到這裡,要造 LoginSystem 物件的程式要先造好資料庫物件,再把資料庫物件餵給 LoginSystem 的 constructor,不知不覺寫成相依性注入了。

一旦寫成相依性注入,連帶有個好處:測試時可以用仿製的資料庫物件(Mock database object),也就是個實作 AccountDBInterface 但不真的連到任何資料庫的物件,只要能提供測試用的資料,連測試專用資料庫都不用架設了。

到此功德圓滿,資料庫可以隨意抽換,甚至可以抽換成仿製的資料庫以便測試 LoginSystem 的邏輯。

可是封裝怎麼辦?LoginSystem 依賴 AccountDBInterface 這件事因為放在 constructor 的引數裡而曝露出來了,要使用 LoginSystem 的程式要自己造好帳號資料庫物件再餵給 LoginSystem。如果要用到帳號資料庫的只有 LoginSystem 這 101 個類別(這個例子沒舉好,事實上改密碼系統也要用到帳號資料庫,但不想改例子了,請讀者包涵),以封裝的角度來看,帳號資料庫可以變成 LoginSystem 的私有類別,不必讓任何其他類別看到。要造出和使用 LoginSystem 的程式,不用知道帳號資料庫的存在,LoginSystem 知道就夠了。

這個例子只有一個相依類別,讀者可以想像在較大的軟體系統裡,有的類別會有不少相依類別,造這種物件時,要把相依物件一一造好餵進去。要造這種物件的程式需要知道太多事,封裝性破壞殆盡。

一種解法:工廠法

既然〈相依性注入〉影響到的是造物件時的封裝性,不是使用物件時的封裝性,很自然會想到在造物件的部分用點技巧。

講到造物件,OOP 四人幫的書裡講到五個樣式:工廠法(Factory Method)、造物師(Builder)、抽象工廠(Abstract Factory)、芻型(Prototype)、Singleton(我想譯成「獨支」,不知道誰有更好的譯法?),在這裡可以用工廠法。

工廠法的做法是:提供統一的造物介面,但不同的實作可以造出不同類別的物件。這正好可以用來造用到不同類別資料庫的 LoginSystem:

class LoginSystemFactory {
 public:
  LoginSystemFactory();
  virtual LoginSystem* Build() = 0;
  // ...
};

class LoginSystemWithType1DBFactory : public LoginSystemFactory {
  virtual LoginSystem* Build() {
    return new LoginSystem(new AccountDB("localhost"));  // 參數部分為了方便說明先寫死。
  }
};

class LoginSystemWithMockDBFactory : public LoginSystemFactory {
  virtual LoginSystem* Build() {
    return new LoginSystem(new MockAccountDB());
  }
};
也就是說,把「清楚記錄相依性、一一造好相依物件、再造好登入系統」這件事設計成工廠的責任,不同的工廠可以生產不同的相依物件、和最後的產品物件。這樣子在上線程式中任何需要造登入系統物件的程式不用知道細節,達到封裝的目的,在測試登入系統時也能注入仿製物件。

後話

一個留給讀者的問題:如果你很重視單元測試的覆蓋度(coverage),時時追求 100%,你要怎麼測試這裡的工廠法呢?

【2012/01/15 編輯】謝謝 fr3@k 的提醒,寫太快還真的忘了 virtual,哈哈!

2012年1月5日

The Knack

分享一個最近看到的呆伯特短片。因為沒有字幕,就順手譯了。

如果我的孩子成為工程師,倒底該哭還是該笑呢?



呆媽帶小呆伯特去看醫生。

呆媽:「我擔心小呆伯特,他不像其他小孩。」
醫生:「你的意思是?」
呆媽:「昨天我才放他不管一分鐘,他就拆了電視、時鐘、和音響。」
醫生:「那完全正常,小孩就是會拆東西。」

醫生用小鎚敲了小呆伯特的膝蓋。

小呆伯特:「噢!」

呆媽:「我擔心的是,他拿拆下來的零件組了個火腿無線電。」
醫生:「天哪!」
呆媽:「很糟糕嗎?」
醫生:「我一般會檢查他的腦電波,但是儀器壞了。」

小呆伯特瞬間修好了儀器。

醫生:「這比我害怕的還慘。」
呆媽:「怎麼了?」
醫生:「你兒子恐怕得了 The Knack。」
呆媽:「The Knack?」

醫生翻開厚厚的醫學書,指著文字解釋。

醫生:「The Knack 是種罕見疾病,特徵是對機械和電路有極強的直覺,在社交上是徹底的無能。」
呆媽:「他能過正常的生活嗎?」

「不能,他會成為工程師。」


呆媽崩潰