分類彙整:programming

軟體工程接案技巧:「週薪式兼職」報價

最近接網站開發的案子,特別是新創團隊的案子,我發現用週工時的兼職方式報價,比報固定價格的傳統接案方式好很多。

報價方式會像這樣:

  1. 接案者每週工作 N ~ M 個小時
  2. 每週收費為新台幣 X 元
  3. 預估案件大概 Y 週會做完
  4. 每 Z 週結帳一次

N、M、X、Y、Z 的原理如下:

  • 根據案件的困難程度、急迫程度來決定 N、M、X
  • 要讓案主知道估計的總花費,所以要估一個 Y
  • 接著由雙方的互信與方便程度決定 Z

這種報價方式看似含糊曖昧、對雙方來說都有風險。實際上卻正好相反,它比傳統報價方式符合雙方需求得多,也比較符合現實狀況。

舉例來說,我最近一次替新創團隊做網站的接案對話如下。我先說明報價方式:

111

接著補充說明一些:

222

案主很爽快的答應了,後續也進行得非常順利。這種兼職方式解決了傳統報價方式會遇到的3個問題:

  1. 需求與規格不必在事前定得非常清楚
  2. 消除案主的訂金恐懼、消除接案者的尾款恐懼
  3. 以連續多個較小的承諾培養互信,取代一次性的大承諾

需求與規格不必在事前定得非常清楚

軟體工程幾乎不可能準確規劃規格與細節,因此案件總金額、總工時根本估不準。

按照傳統的方式,雙方只能各憑經驗,討論出一個價格。這價格簡直是兩邊各猜一個數字之後妥協的,一點都不準,價格很少是公平的。常常導致接案者覺得自己被凹了,或是是案主覺得自己被當肥羊宰了。

其實,對很多案件來說,特別是新創團隊的案件,這種固定價格式的接案本來就不合理。

現在創業的商業方法論,講求執行、靈敏。趕快先開始,過幾天有新想法、新功能想做,再調整開發的優先順序。需求與規格本來就會跟一開始不同。

接案者要案主一開始就把規格訂得非常清楚,實在是強人所難。

而這種情況下,案主在一開始就想確認一個絕對數字的預算,也是強人所難。

除此之外,光是為了報價,就必須花一堆時間確認規格、需求。這些開會討論的時間成本很高,卻又不可能跟案主說「我給報價是要收費的」。

於是接案者為了保護自己,會演化出一種習慣:防禦式報價。

那就是把情況都往壞的方向預想,一律只給偏高金額的報價。這對整個接案市場都不是什麼健康的事情。

週薪式兼職不會出現防禦式報價。只要大致溝通一下需求,接案方大概了解情況,馬上就可以開始動工。

消除案主的訂金恐懼、消除接案者的尾款恐懼

對案主來說,什麼都沒拿到就要先付訂金,還經常是總額的1/3到1/2,壓力不小。

對工程師來說,一大部份費用卡在尾款,還有可能一拖再拖,結不了案,同樣可怕。

每 Z 週結帳一次,雙方觀察對方的時間多了許多,不用再賭人品。

以連續多個較小承諾培養互信,取代一次性的大承諾

傳統接案方式,雙方可能從頭到尾都彼此怕怕的,因為兩個沒合作過的人,要在一開始就互相綁定一個大承諾,也不知道案件會不會進行順利、對方人品到底怎麼樣。

而週薪式兼職,對案主來說,最糟的情況就是幾週之後工程師擺爛失聯了,損失幾週的錢。就算這樣也比傳統報價方式搞砸時,損失50%總金額的訂金好。

對工程師來說,最糟的情況就是定期結帳時,案主不付錢失聯了,損失幾週的錢。就算這樣也比傳統報價方式搞砸時,損失50%總金額的尾款好。

這種方式不但可以逐步培養互信,也對溝通很有幫助。

因為雙方每 Z 週必須真實面對彼此一次(付錢/要錢的時候,就是真實面對彼此的時候)。有什麼誤會都會被迫儘早說開,不會拖拖拖好幾個月,到要結案才發現雙方距離彼此期待差超多。屆時的誤解跟憤怒都會高到無法溝通了。

結語與經驗分享

本文一開始提到的接案經驗,雖然規模很小(我1人+案主方2人參與),但是執行得非常順利、非常愉快。連一開始的報價我都只看了一份 UI 的 pdf 檔一眼,5分鐘就給出報價。

我們甚至連合約都沒簽,隔天就開工,只花費 4 週就完成了這個案件。每週見面1-3次,搭配頻繁的線上討論,總金額新台幣 48,000 元。一點浪費時間的感覺都沒有,是一次非常理想的商業合作經驗。

下次接案、發案時,碰到傳統接案方式的困境的話,不妨以這種方式接案、發案,根據個別情況調整一下 N、M、X、Y、Z 參數即可。

傳統合作產生的商業糾紛,或許只是 pricing model 的問題而已。

附註:為什麼工時是區間、不是定額?

跟案主見面開會完之後的閒話家常,算不算工時?
工程師通勤去開會的時間,算不算工時?
工程師吃晚餐、出門散步的時候腦中在想演算法,算不算工時?
案主要求的技術工具太有趣,工程師自行額外看了一堆進階文件,算不算工時?

每件小事都要定義清楚算不算工時,有點不切實際,對雙方來說都很煩,而且斤斤計較這些東西有點傷感情。區間讓案主、接案者雙方都更自在。

不如承認這點曖昧性,兩邊都知道會有彈性,彼此守住一些議價的立場就好了。而且是區間的話,你也不用一直拿手錶計時了。每週抓固定幾個時段工作就差不多了。案主也不用每次開會要一直看手錶了。

除此之外,區間工時可以保護接案者的時薪高低的彈性。未來再接案時,你會有立場在 X/N 到 X/M 附近的範圍報價。

(完)

虛擬貓咪原始碼&智慧合約入門筆記

最近想多了解智慧合約的實際應用狀況,希望能跟區塊鏈上的智慧合約做簡單互動。

於是找了目前最成功的應用之一「虛擬貓咪」來研究。

分享一下目前的研究心得。

在開始之前,可以先閱讀以下兩個連結,對於閱讀本文會非常有幫助:

https://medium.com/loom-network/how-to-code-your-own-cryptokitties-style-game-on-ethereum-7c8ac86a4eb3

https://medium.com/loom-network/your-crypto-kitty-isnt-forever-why-dapps-aren-t-as-decentralized-as-you-think-871d6acfea

智慧合約不是完全公開透明的

以虛擬貓咪為例,智慧合約本身看似可以在此取得:

https://etherscan.io/address/0x06012c8cf97bead5deae237070f9587f8e7a266d#code

但貓咪基因科學的合約是額外佈署的,不但可以由作者動態修改,而且還只有 opcode 可讀。

同樣的道理,您也可以利用這種「設定外部合約」並且不公佈那份外部合約的 Solidity 原始碼,來達成這種保密效果,還讓合約保持可以更新、升級的空間。

智慧合約不是完全去中心化的

除此之外,虛擬貓咪智慧合約定義了 CEO、CFO、COO 三個管理員角色。這三個角色各自有額外權力,甚至可以凍結整個合約的運行。

不僅如此,區塊鏈上的資料看似永恆,但其實除了原始團隊,沒人可以解讀基因編碼。因此一大部份 value 還是來自傳統 web server。所以虛擬貓咪根本沒辦法脫離作者團隊獨立存活。

如何用 web3.js 讀取 address balance?以我與虛擬貓咪為例

要讀取每個地址的以太幣餘額,最簡單的方式就是直接透過 etherscan.io

比如說,我的個人餘額可以在此查看:https://etherscan.io/address/0xcb0418eae76e14c214d79b8305ca34669075cba6

而虛擬貓咪這份合約地址的餘額,可以在此查看:https://etherscan.io/address/0x06012c8cf97bead5deae237070f9587f8e7a266d

以工程師的立場,會希望用程式去跟區塊鏈互動。以在網頁內使用 web3.js 為例,安裝了 MetaMask 來連線到區塊鏈之後,可以用以下程式碼讀取地址餘額:

如何用 web3.js 讀取智慧合約的 public variable?以虛擬貓咪為例

試著讀取智慧合約中三個公開變數的值。最簡單的方式一樣是透過 etherscan.io

可以在這裡看到三個管理員(CEO、COO、CFO)的地址: https://etherscan.io/address/0x06012c8cf97bead5deae237070f9587f8e7a266d#readContract

用 web3.js 來讀取的話,可以先去 etherscan.io 取得合約的 ABI:

https://etherscan.io/address/0x06012c8cf97bead5deae237070f9587f8e7a266d#code

接著像這樣從區塊鏈上讀出值:

如何用 web3.js 呼叫智慧合約的 public function?以虛擬貓咪為例

呼叫公開函式的話,最簡單的方法是透過 etherscan.io。

以虛擬貓咪的 getKitty 函式為例,如果要找出 ID 為 1 的那隻創世貓咪的資料,只要在欄位內輸入送出即可:

https://etherscan.io/address/0x06012c8cf97bead5deae237070f9587f8e7a266d#readContract

用 web3.js 來讀取的話,則可以像這樣來呼叫函式(以分別抓出1、2、3 三隻貓咪為例):

心得感想

像這樣的智慧合約,究竟有多少應用場景?

一個 dapp 該去中心化到什麼程度?智慧合約的未來到底有多少潛力?實在沒人有把握。

還是老話一句,建議有心參與的朋友,保持懷疑精神,花時間了解更多,建立自己的觀點跟看法,不要輕信任何人的說法。

(完)

Ethereum 智慧合約佈署初體驗:數據與成本分享

正式佈署一份智慧合約是什麼感覺?

它的佈署速度、佈署成本又是多少呢?

我親自跑了一次流程。分享一下實際的數據與心得。

發行個人專屬加密貨幣:阿川幣

我直接拿官網的範例程式碼來修改參數,發行阿川幣:

https://ethereum.org/token

官網提供兩份範本,一份只有基本功能,一份具備進階功能。

以太錢包預估了基本合約的佈署成本是 0.000715604 ether(約合新台幣 22 元):

erc20

而進階合約的佈署成本是 0.001010363 ether(約合新台幣 31 元):

advanced

我選擇佈署基本合約就好。輸入參數之後,會發現成本微幅提昇到 0.000777146 ether(約合新台幣 24 元):

with-parameters

按下 DEPLOY 按鈕,會確認密碼,並顯示預估成本 0.000777146 ether 是因為預估耗用 777,146 gas,並且多準備 0.0001 ether 作為緩衝:

gas-usage

送出後,以太錢包會顯示 etherscan.io 網站上的交易連結:

https://etherscan.io/tx/0xcedd9b10deb6ca64b44a547244045383d4563f916849c005308453d939f69c51

大約 6 分鐘後,網頁上的交易狀態顯示為 success:

6-min-success

不過以太錢包內還沒有同步顯示完成。再過 4 分鐘,也就是大約 10 分鐘後,以太錢包上才顯示 created:

10-min-success

進入帳號底下查看,會看到 2,100 萬枚阿川幣正確顯示,代表合約佈署完成了。

我本來有 0.01 ether,現在剩 0.009222854 ether,所以的確是被扣款 0.000777146 ether,以太錢包的預估成本是準確的:

final-balance

並且可以查看合約的地址,合約的原始碼也在裡面:

https://etherscan.io/address/0x03f6a68baf85840a513a71f49f5f8fb1edcf27f6

心得感想

這次初體驗,以個人用戶來說,我認為佈署速度不算太慢(約 6 – 10 分鐘),手續費也還行(約合新台幣 24 元),都還 OK。

所以佈署這種範例程式碼等級的合約,不困難,也不昂貴。

可以以此為基礎,再進一步研發更複雜的智慧合約。

至於智慧合約的未來到底有多少潛力?其實沒人有把握。

還是建議有心參與的朋友,保持懷疑精神,建立自己的觀點跟看法,不要輕信任何人的說法。

(完)

dog

搞懂為何設定 React、JSX、ES2015、Babel、Webpack 的學習筆記

學習 React、JSX、ES2015、Babel、Webpack 等等現代化的前端開發觀念時,會需要安裝很多工具、設定多個檔案、用到許多指令。

大部份教學文章只講「怎麼設定這些」而沒說明「為何要設定這些」。照著做完感覺很不放心。

最近我試著搞懂「為何要設定這些」,跟大家分享一下這個逐步搞懂的過程。

前言

這篇文章假設你想要開始用 React 開發,希望搞懂相關的前端環境與設定。

如果你想知道用 React 到底有什麼好處,可以參閱以下連結:

Introducing JSXThinking in React簡單聊一下 ONE-WAY DATA FLOW、TWO-WAY DATA BINDING 與前端框架

接下來,這篇文章會以試著寫一個陽春的 App 元件作為範例,內含 ChildA 跟 ChildB 兩個元件。

讓我們開始吧!

第一步:直接在 html 檔內開發 React 給瀏覽器執行

我們先用原始的開發方法硬搞看看!

用 script 標籤直接從 CDN 引入 react.js 跟 react-dom.js 檔案。

然後試著寫一個陽春的 App 元件,內含 ChildA 跟 ChildB 兩個元件。

這個 html 檔內容會像這樣:

source code

結果瀏覽器無法執行 React 相關的程式碼!

原因有兩個:

1. 瀏覽器看不懂 ES2015 的語法(class、extends)

2. 瀏覽器看不懂那個像是把 html 寫在 js 裡面的 JSX 語法

該怎麼辦呢?

第二步:用 Babel 讓瀏覽器能看懂 ES2015 與 JSX 語法

我們可以使用 Babel 套件來來編譯 ES2015 與 JSX 的程式碼,讓瀏覽器能夠看懂。

只要在 html 內加上這行:

然後在要編譯的 script 加上 type=’text/babel’ 即可:

就可以順利執行我們的 React 程式了!

source code

您可以用這種方式開發一些小型的 React 程式。

但實務上這些元件會複雜、龐大很多,全部寫在HTML裡面的話,不但很難維護,還有 babel 每次都在瀏覽器內編譯的效能問題。

只有3個元件可能還好,但要是有30個元件呢?

拆開成獨立檔案,並且預先用 babel 編譯過比較好。

第三步:把檔案拆開,並且預先編譯完成

把3個元件各自獨立成檔案,也把啟動這些元件的程式碼獨立成一個檔案:

接著來預先編譯它們。

這次我們不使用上面瀏覽器版本的 babel,我們用命令列介面的 babel-cli 來編譯。

先安裝好 babel-cli:

然後 babel-cli 從版本 6 之後預設不支援 ES2015 與 JSX,需要分別加裝所謂的「preset」才行:

接著就能在命令列使用 babel 並且設定 presets 來編譯了。

我們有4個檔案,所以分別打編譯指令4次:

成功編譯之後,就不需要在瀏覽器內引入 babel 了,直接使用編譯完成的檔案即可:

source code

然而,這樣做卻又產生3個新的問題:

1. 有N個檔案,就要打編譯指令N次,太累了

2. 透過 script 標籤引入N個檔案,會增加伺服器負擔、也會增加用戶端等待時間

3. babel 相關的程式碼都移出瀏覽器了,react.js 跟 react-dom.js 卻還是在瀏覽器內引入

第一個問題還算簡單,可以改成輸入指定資料夾的指令,就可一次編譯:

但是第二三個問題就棘手得多。

有鑑於此,需要找方法一次解決這三個問題:設法一口氣全部編譯、封裝成單一檔案、並且函式庫統一用NPM在伺服器端安裝與管理吧!

第四步:使用 webpack 來解決這些整合問題

webpack 會利用 babel-loader 套件來呼叫核心的 babel-core,所以先分別安裝它們:

我們不會透過命令列去使用 babel 了,既然用不到就先把它移除吧:

我們也不會用 script 標籤引入 react.js 跟 react-dom.js 檔案了。一口氣全部編譯時會用到,所以用NPM安裝它吧:

前面說過 babel 版本 6 以後預設不支援 ES2015 與 JSX,現在不透過命令列設定 presets 的話,就需要建立 .babelrc 檔案去開啟支援:

然後為了讓 webpack 在一口氣全部編譯成一個檔案時,能看懂檔案跟套件之間的相依性,因此需要按照 ES2015 的 import/export 語法寫清楚。

例如原本的 App.jsx 就要改寫成這樣:

src 資料夾內的檔案都要進行改寫。

最後建立 webpack.config.js 來設定檔案路徑,並指定使用 babel-loader 來幫忙編譯:

就可以透過命令列執行 webpack了:

這段指令有點長,可以把它放進 package.json 的 scripts 欄位:

之後就可以用下列指令來進行編譯工作了:

會得到我們要的結果:一個檔案搞定一切!

source code

大功告成!終於搞定我們想要的所有效果!

結語

本篇文章提到的內容,根據不同的套件版本,實務上有多種方式去安裝、設定達到同樣效果。

但是核心觀念是共通的!

可以此為基礎,探索更多的現代前端開發方法,學習更多工具的功能與設定!

(完)

(Photo via 8 Kome, CC licensed.)

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.)