當你的團隊在開發一個大型應用時,該應用的不同部分可能以不同的速度前進。比如,設想下面的場景:一個開發熱源被分配 資料層 的backend工作,而另外一個開發人員做front-end和web/controller層面的工作。前端開發人員希望測試他的controller,但是後端的資料層卻進展緩慢。然而,如果兩個開發人員能夠在他們之間的介面上預先達成一個協議(interface),也就是說後端資料層設計人員必須提供以下的資料訪問介面,那麼後端資料層開發即使進展不能和前端開發完全同步,那麼也不會影響前端的開發工作:
interface OrderRepositoryInterface { public function getMostRecent(User $user); }
一旦這個介面規範被制定,即便是這時介面的具體實現還根本沒有,那麼前端工程師就可以測試他的contrller了!這種工作模式就允許應用的不同模組和元件可以以不同的速度向前走,而又能滿足適當的unit test的需求。而且,這種工作方法即便我們徹底改變一個介面的實現模式(可能會有bug),也不會break掉其他我關的元件的工作。記住:這裡無知便是福。我們不想知道也不應該知道我們所依賴的介面是如何實現的,我們只需要知道該介面提供什麼功能即可。所以,既然現在我們有了清晰定義的介面,我們就可以寫我們的controller了:
class OrderController { public function __construct(OrderRepositoryInterface $orders) { $this->orders = $orders; } public function getRecent() { $recent = $this->orders->getMostRecent(Auth::user()): return View::make('orders.recent', compact('recent')); } }
前端工程師甚至可以寫一個"dummy"的介面實現,這時應用的view就可以populate一些假的資料了。
class DummyOrderRepository implements OrderRepositoryInterface { public function getMostRecent(User $user) { return array('Order 1', 'Order 2', 'Order 3'); } }
一旦dummy implementation寫出來後,我們可以bind到我們的ioc容器裡面,這樣我們的整個應用就開始使用這個Dummy implementation了。
App::bind('OrderRepositoryInterface', 'DummyOrderRepository');
經過上述繫結後,一旦一個真實的implmentation被後端工程師開發完成,比如RedisOrderRepository,那麼IoC binding就可以非常方便地切換OrderRepoistoryInterface到這個實際的實現上去了,這時整個應用的功能就完整地開始使用村粗在Redis中的訂單資料功能了!
正因為interface如此重要,因此在一個專案開發開始前,一定要在相關團隊間討論定義清晰的介面,大家隨後可以獨自工作,約定一個時間週期來做integration就好了。這就是一個比較ok的PHP 專案實踐了