提高React component重用性:Composition

這陣子寫React的components,常常很快就完成了期待的效果。
但是總覺得沒有重用性、無法從application獨立出來。

我想將元件中可重用的部份獨立出來,成為基本元件,接著針對application另寫元件去擴充這些基本元件。
有辦法讓一個component去繼承另一個component嗎?似乎不行。

我首先在開發React-Lightbox時遇到了上述問題。

我想製作一個Lightbox效果的plugin,提供Lightbox、LightboxTrigger、LightboxModal三個component,讓developer自訂觸發Lightbox的按鈕、Lightbox框框內的內容。

用法類似這樣:

        React.renderComponent(
            
                
                    
                
                
                    
                
            ,                
            document.getElementById('react-canvas')
        );

範例中的ToggleButton跟MyPanel是由developer自訂。

該怎麼達成這個效果呢?

毫無頭緒之下,我寫出像這樣的code:

        for (j in this.props){
            if (j !== 'children'){
                this.props.children.props[j] = this.props[j];                
            }
        }
        for(i in this.props.children){
            this.props.children[i].props.openLightbox = this.openLightbox;
            this.props.children[i].props.closeLightbox = this.closeLightbox;
            this.props.children[i].props.setLightboxState = this.setLightboxState;
            for (j in this.state){
                this.props.children[i].props[j] = this.state[j];                
            }
        }

簡單地說,就是硬著頭皮傳遞某些props、達到類似「繼承」的「擴充」效果。

眼前的問題解決了,但是tomchentw指出這樣寫法的問題所在(感謝指點):

‘At my first glance it looks pretty magic.’

該怎麼辦呢?

我最後終於在這篇SO的問答找到了一線希望:

Extending React.js components

由component去wrap住基本component來達到擴充的效果,然後利用JSX Spread Attributes的幫助來傳遞props。

利用這些,我開發出了第二個React的plugin:Tag Manager

所謂「使用者自訂component去擴充基本component」的code,大概長這樣:

/** @jsx React.DOM */

var MyCustomTagManager = React.createClass({

    addTagCallback: function(tagName, setStateCallback){
        alert('Add a tag! Maybe you should send an ajax!');
        setStateCallback({id: 99, name: tagName});
    },

    removeTagCallback: function(tag){
        alert('Remove a tag! Maybe you should send an ajax!');
    },

    render: function(){
        return(
            
        )
    }

});            

反省

再看一眼,發現前面的寫法跟後面的寫法其實都是「用component去包住component」,意義上其實類似。
(都是叫composition?我理論沒學好,對專有名詞不熟@@”)

後面的寫法看起來更explicit了,maintainability跟readability應該也更高了吧?
您覺得呢?

別急著用那些新奇玩意兒

一個正要用PHP開始工作的朋友焦慮地問我:同事說我應該要用NetBeans。它是一種叫IDE的東西?我查了資料,發現它提供很多功能:字詞自動補完、全文搜索…等等。但我裝了之後覺得好難用,壓力好大!

我看了看他的電腦:他用Ubuntu當作業系統、Sublime當編輯器、git做版本管理、Apache當伺服器。這些工具夠他解決幾乎所有問題了。

我問他是否看得懂那些文章提到的「優點」在說什麼?「看不太懂。」
我問他同事有否解釋要拿NetBeans來做什麼?「好像可以設定連線、好像很方便、好像歐洲人都用NetBeans。」

這讓我回想起自己踏入這行後困惑很久的煩惱:為了一個別人大力提倡、但是自己看不出來有什麼用的東西而感到焦慮。

對於這種看到新工具、新觀念、新知識而產生的焦慮,我最後養成了一種態度:隨便看看、知道有這個東西存在就好了。之後遇到瓶頸、困難時再回頭,看看它們能否解決我的麻煩。

別浪費時間煩惱這個東西到底能解決哪個問題了;也別為了這種困擾感到挫折、覺得自己很笨。

課本上的一個名詞、一個Design Pattern、一個時髦的工具、一個軟體開發方法、一個軟體測試技巧、一個抽象化思考方法、一個程式語言的某個語法…。看不出來有什麼用,就別急著用。

時間一久,你還可能發現一件更難相信的事情:你從頭到尾都是對的。

那些新奇的狗屁也許根本就有某些缺點。你第一眼就看到了,但是所有人都不去提。

國王裸體在街上走!你的眼睛說不定從來沒看錯。

我不是在說學習沒有價值,我想說的是這兩件事:

* 碰到麻煩再去學習進階的,很多時候這樣才學得透徹。

* 所有東西都可能有缺點、或是只在某些情況適用,也許你只是很早就察覺缺點。

我再換句話說吧:

當你為了新奇玩意兒感到焦慮的時候,

或許,學習它的時候未到;

又或許,那東西真的沒什麼屁用。

(Photo via Alessandra, CC licensed.)

PHP這個程式語言

PHP是web領域的知名程式語言,沒有資訊背景的人可能也聽過這個名字。

不像其他語言在設計上有所堅持,PHP只堅持找到解決web問題的最短路徑

這個語言的內容雜亂,會飽受批評完全可以理解。它除了專心「把事情搞定」之外,幾乎什麼都不管。

我從事PHP開發以來,發現它門檻低、使用者多、允許多種寫法、允許不同程度的人用自己的方式開發。

也因為社群龐大,PHP產出的程式碼平均品質低、開發者程度參差不齊。

那麼PHP到底解決了多少人的web問題呢?

關於PHP的缺點、批評,都是事實,但PHP社群知道自己在幹什麼。

正準備開始,不知去哪找人討論嗎?到批踢踢的PHP版、Facebook的PHP台灣發問吧!

學習了一陣子,覺得很多文章過時、想參考業界流行的開發慣例嗎?看看PHP: The Right Way吧!

對英文能力有自信,想跟全球的網友一起討論嗎?來Reddit的PHP版吧!

工作一段時間,想找些高品質的函式庫參考嗎?逛逛the PHP League吧!

好奇才華洋溢的PHP工程師到底有多少生產力嗎?看看symfony的Fabien Potencier的commits吧!

想自學寫網站、親手搞定某個web的問題嗎?試試看PHP吧!

軟體測試:我不在新創公司用mocks

為了確保軟體品質,多少會寫點測試。

常會看到文章提倡使用mocking的手法,說是要獨立測試程式的各個部份比較好。

我觀察這個手法很久了,遲遲不願意真正應用。

我總覺得mocking讓測試的code變得很冗長、不好讀。

最近終於發現,其實不少人跟我抱持同樣看法。

這邊舉Testing on the Toilet: Don’t Overuse Mocks一文中提到的例子。

不使用mocks、寫出有相依性的test長這樣:

public void testCreditCardIsCharged() {
  paymentProcessor = new PaymentProcessor(creditCardServer);
  paymentProcessor.processPayment(creditCard, Money.dollars(500));
  assertEquals(500, creditCardServer.getMostRecentCharge(creditCard));
}

使用mocking達到測試環境獨立的test長這樣:

public void testCreditCardIsCharged() {
  paymentProcessor = new PaymentProcessor(mockCreditCardServer);
  when(mockCreditCardServer.isServerAvailable()).thenReturn(true);
  when(mockCreditCardServer.beginTransaction()).thenReturn(mockTransactionManager);
  when(mockTransactionManager.getTransaction()).thenReturn(transaction);
  when(mockCreditCardServer.pay(transaction, creditCard, 500)).thenReturn(mockPayment);
  when(mockPayment.isOverMaxBalance()).thenReturn(false);
  paymentProcessor.processPayment(creditCard, Money.dollars(500));
  verify(mockCreditCardServer).pay(transaction, creditCard, 500);
}

不但可讀性下降、還得多花時間撰寫mocks。

在新創公司,我不寫mocks的原因有兩個:

一、新創公司,軟體越快上線越好。花時間寫mocks,太不划算了。
二、既然軟體架構不大,反正任何一個測試fail,整個application都會壞掉。tests無論如何都得全部pass才行。

想來想去,只有在用到第三方library或API、不方便在tests中真正呼叫時,才會想用mocks。

話雖如此,有看法認為應該用interface再加上一層薄layer來解決test第三方library的問題:

Test Smell: Everything is mocked

如果是在開發framework就另當別論了。要確實分別測試各個部份才行。

Laravel的tests

話雖如此,也許更資深、接觸的軟體架構更大之後,我會改變想法也說不定。

工程師的缺德行為:叫朋友去學C/C++

常常看到非資訊背景的朋友問工程師:我最近想學寫程式!該怎麼入門?

通常他們會得到很多糟糕的答案:「先了解演算法」、「先弄懂資料結構」、「先認識物件導向」。

這些答案都夠糟糕了,但最糟糕的答案莫過於「從C/C++開始入門」。

(我看過最扯的建議是「去找C++ Primer Plus來看」。這本磚頭書絕對不適合一般人自學)

照著以上建議去做的朋友,大概會在兩週之內就得到滿滿的挫折感,然後認為自己「果然不適合寫程式」,也不好意思再去跟工程師講自己的狀況,最終再也不想踏入programming領域。

這些建議真是害人不淺。

資訊相關科系,是因為要接續到之後的資料結構、作業系統,所以大一從C/C++開始入門會比較連貫、才能完整學一次電腦的基本知識。

而這些想學coding的朋友,大部分只是這兩種情況:

A. 最近覺得寫網站好酷,想試試看自己能否寫個blog、或是個人網頁
B. 對工作上某些人工流程不滿意,想試試看自己能否學寫程式、用電腦解決問題

他們的願望僅此而已,並沒有打算成為電腦專家、駭客,實在沒有必要從C/C++入門。

就像點火一樣,先從零星的火苗開始、小心保護不要讓它熄滅,接著慢慢加東西進去、讓火焰慢慢成為大火。學習,除了知識/技能之外,培養成就感與熱情也是很重要的。而從C/C++入門,會讓正要萌芽的小火苗直接消滅殆盡。

我對程式設計入門的建議很簡單。
如果你是狀況A,去學PHP&MySQL,先弄一個醜醜網頁出來就可以了。
如果你是狀況B,去學Python,先弄一個能執行、能顯示幾個文字的程式就可以了。

一般人想到程式通常只想到「圖形化介面」,如果能寫出一個有介面的小玩意會非常有成就感。
那樣的話,學PHP來寫個網站、直接就是介面,絕對有趣;學Python很快就能把玩內建的tkinter函式庫、做出按鈕做出選單,樂趣無窮。

話雖如此,你到市面上找PHP或是Python的書,大概還是會落得「滿滿的挫折感然後正式放棄」的下場。

別擔心,這不是你的錯,是那些書寫得不夠好。沒有一本是真正的程式設計入門書。
那些書只想著給你知識/技能,沒有想到要培養你的成就感、保護你的熱情。

要找書的話,我只建議兩本。

狀況A,去找歐萊禮的「深入淺出 PHP 與 MySQL」(Head First PHP & MySQL)。
狀況B,去找歐萊禮的「深入淺出程式設計」(Head First Programming)。

已經出社會了嗎?去台灣大學資訊系統訓練班逛逛吧,看看時間跟課程費用能否負擔(我有經濟系背景的朋友去上過幾堂課程,學得滿開心)。

政府也有推動青年就業讚的課程,滿足某些條件還能申請補助(不過,我有朋友上過其中一堂PHP課程,印象很糟糕、覺得是業者要賺政府補助款隨便開設的課程。但畢竟是單一經驗,我還是列出來給各位參考。)

國外的線上開放課程也可以考慮。

Coursera的

人人都懂的編程課(Python)

學習編程:基礎

麻省理工的

Introduction to Computer Science and Programming

A Gentle Introduction to Programming Using Python

全部都是用Python入門。

我對程式設計入門的建議就只有這樣。你不需要知道什麼演算法、資料結構、物件導向。
什麼狗屁名詞都不需要知道。那大多只是工程師下意識地想讓你覺得他們很厲害而已。

你只要想辦法寫出一個會動的程式就可以了。所有偉大的東西都是這樣開始的。


Q1: 但是狀況A還要面對HTML/CSS耶.. 好像很難?
每個技能深入下去沒有不難的。但是HTML/CSS要入門實在不怎麼難。
就從隨便寫幾個表單或按鈕、弄個醜醜的靜態網頁出來開始吧。

Q2: 我是工程師,我能怎麼幫助朋友?
幫他們搞定環境設定吧!幫他們把最基礎卻又最給新手挫折感的環境給設定好!讓他們能用記事本跟幾個小工具就開始寫code!給他們成就感!

Q3: 那寫手機app呢?你怎麼沒提到?
我覺得手機app還滿難入門的,實在給不出有建設性的建議。不知道。不過,上面的連結也許有一些手機相關課程,不妨試試?

(Photo via Sano Rin, CC licensed.)

我對Rails之父的觀察

Rails之父,網路代號DHH,本名David Heinemeier Hansson。
我不認識他本人,但是從他自己的blog、他的合夥人Jason Fried的描述、媒體的採訪,我發現幾件事和想像中的不一樣。撰文和各位分享。


他本來是個有商業天賦的PHP工程師

DHH的合夥人Jason Fried在Hacker News親自寫道

我遇見DHH時他是個PHP工程師。我寫過一些PHP,能夠稍微評估他的能力。除此之外,我還喜歡他的商業素養以及平常做事的方法—都跟我很合。(*1)

他本來不覺得自己適合當程式設計師

他接受Big Think訪問時,其中這段提到:

說來有點好笑。我以前寫PHP跟Java的時候,常常花時間去摸其他程式語言。到處摸看看其他程式語言…隨便什麼都好。寫PHP跟Java實在太悶了,我需要用這種方式讓自己暫時抽離。(*2)

還有這段:

我以前寫PHP跟Java的時候,完全不覺得自己之後會當程式設計師。(*3)

閱讀程式設計的教材、範本給他很多苦悶,他無法那樣學習

他在網誌中透漏,自己沒有對電腦熱愛到能夠純粹為了學習而學習。
請參閱我之前的翻譯:Rails之父:我怎麼學會寫程式的?

他不是資工系畢業的

看他在signalvnoise部落格下方的評論,網友Ben提到:

@DHH
你沒有資訊工程學位。這說明了超多事情。

DHH在下面回應:

我其實有半個資工學位(我們系是一半商業管理、一半電腦科學)。但我沒在學校真的學到怎麼寫程式。(*4)

翻閱維基百科的介紹

從Copenhagen商學院拿到商管與電腦科學學位後,他從丹麥搬到美國。(*5)

他拿的學位等於台灣的「資訊管理學系」。

他一開始只是接案,不是決心創業

DHH以Basecamp(以前叫37signals)公司合夥人的身份跟Jason Fried合出過幾本書。
大家都知道Rails是開發Basecamp的過程中,從code中抽離出來而成的框架。
那麼一開始他是如何定位自己呢?
我寫信給Jason Fried本人,問他股權的事情。他的答覆如下:

Hi Tony-

我們最一開始沒有談股權的事。

我一開始是發案給他,之後僱用成員工。我們一起工作了好幾年才談到公司所有權的事。

-Jason(*6)


*1
I’ve done some PHP programming in the past, and I met DHH when he was a PHP programmer, so I was able to evaluate his talent at a very basic level. Beyond that, however, I liked his business mind and general approach to things – they were closely aligned with my own.

*2
It’s kind of funny; when I used to develop in PHP or the stuff I did in Java, I was always looking for something else. I was always looking for another programming language, another… just something else, in part just to distract me from being bored in the languages I was in.

*3
I was absolutely not convinced that I was going to be a programmer when I was working with PHP and Java.

*4
I actually got half a comp sci degree (my bachelor was in biz admin/comp sci), but that wasn’t where I truly learned to program.

*5
After graduating from the Copenhagen Business School and receiving his bachelor’s degree in Computer Science and Business Administration, he moved from Denmark to Chicago, Illinois, U.S. in November 2005.

*6
Hi Tony-

We didn’t talk about stock at the very beginning.

I hired David as a contractor first, then an employee. We worked
together for a few years before there was any talk about ownership.

-Jason

by 阿川先生