

每个类都应该只有单一的职责,并且职责里所有的东西都应该由这个类封装
接下来我们定义一个接口,然后实现该接口
interface OrderRepositoryInterface
{
public function userOrders(User $user);
}
class OrderRepository implements OrderRepositoryInterface
{
public function userOrders(User $user)
{
Order::where('user_id', '=', $user->id)->get();
}
}将接口的实现绑定到Laravel的服务容器中
App::singleton('OrderRepositoryInterface', 'OrderRespository');然后我们将该接口的实现注入我们的控制器
class UserController extends Controller
{
public function __construct(OrderRepositoryInterface $orderRepository)
{
$this->orders = $orderRespository;
}
public function getUserOrders()
{
$orders = $this->orders->userOrders();
return View::make('order.index', compact('orders'));
}
}现在我们的控制器就完全和数据层面无关了。在这里我们的数据可能来自MySQL,MongoDB或者Redis。我们的控制器不知道也不需要知道他们的区别。这样我们就可以独立于数据层来测试Web层了,将来切换存储实现也会很容易。
接口与团队开发
当你的团队在开发大型应用时,不同的部分有着不同的开发速度。
比如一个开发人员在开发数据层,另一个开发人员在做控制器层。
写控制器的开发者想测试他的控制器,不过数据层开发较慢没法同步测试。那如果两个开发者能先以interface的方式达成协议,后台开发的各种类都遵循这种协议。
一旦建立了约定,就算约定还没实现,开发者也可以为这接口写个“假”实现
class DummyOrderRepository implements OrderRepositoryInterface
{
public function userOrders(User $user)
{
return collect(['Order 1', 'Order 2', 'Order 3']);
}
}一旦假实现写好了,就可以被绑定到IoC容器里
App::singleton('OrderRepositoryInterface', 'DummyOrderRepository');然后这个应用的视图就可以用假数据填充了。接下来一旦后台开发者写完了真正的实现代码,比如叫RedisOrderRepository。
那么使用IoC容器切换接口实现,应用就可以轻易地切换到真正的实现上,整个应用就会使用从Redis读出来的数据了。
接口与测试
建立好接口约定后也更有利于我们在测试时进行Mock
public function testIndexActionBindsUsersFromRepository()
{
// Arrange...
$repository = Mockery::mock('OrderRepositoryInterface');
$repository->shouldReceive('userOrders')->once()->andReturn(['order1', 'order2]);
App::instance('OrderRepositoryInterface', $repository);
// Act...
$response = $this->action('GET', 'OrderController@getUserOrders');
// Assert...
$this->assertResponseOk();
$this->assertViewHas('order', ['order1', 'order2']);
}总结
接口在程序设计阶段非常有用,在设计阶段与团队讨论完成功能需要制定哪些接口,然后设计出每个接口具体要实现的方法,方法的入参和返回值这些,每个人就可以按照接口的约定来开发自己的模块,遇到还没实现的接口完全可以先定义接口的假实现等到真正的实现开发完成后再进行切换,这样既降低了软件程序结构中上层对下层的耦合也能保证各部分的开发进度不会过度依赖其他部分的完成情况。
更多laravel框架相关技术文章,请访问laravel教程栏目!
