设计模式实例讲解 - 单一职责
描述
单一职责 (Single Responsibility)
一个类应该仅有一个引起类变化的理由
反面示例
定义一个 SalesReporter
用于显示某个时间段的销售总额
<?php
namespace Acme\Reporting;
use Auth;
use DB;
use Exception;
class SalesReporter
{
/**
* 获取某个时间段的销售总额
*
* @param $startDate
* @param $endDate
*
* @return string
*/
public function between($startDate, $endDate) : string
{
if (! Auth::check()) {
throw new Exception('Authentication required for reporting');
}
$sales = $this->queryDBForSalesBetween($startDate, $endDate);
return $this->format($sales);
}
/**
* 查询销售报表数据
*
* @param $startDate
* @param $endDate
*
* @return float
*/
protected function queryDBForSalesBetween($startDate, $endDate) : float
{
return DB::table('sales')->whereBetween('created_at', [$startDate, $endDate])->sum('charge') / 100;
}
/**
* 数据展示
*
* @param $sales
* @return string
*/
protected function format($sales) : string
{
return "<h1>Sales: $sales</h1>";
}
}
测试
$report = new Acme\Reporting\SalesReporter();
$report->between(
now()->subDays(10),
now()
);
该例子明显违反了单一职责:
- 授权方式发生变化时,如 API 授权,需要改动该类
- 当数据库层发生变化时候,如使用 Redis 时,需要改动该类
- 当展示方式发生变化时,需要改动该类
正面示例
对于上述的例子,应当作出如下的改动:
- 不需要关心用户授权,用户授权与本类的职责无关
- 数据层应当剥离出来
- 展示层应当剥离出来
<?php
// 展示层接口
interface SalesOutputInterface {
public function output();
}
// 展示层实现
class HtmlOutput implements SalesOutputInterface {
public function output($sales)
{
echo "<h1>{$sales}</h1>";
}
}
// 数据层
class SalesRepository {
public function between()
{
return DB::table('sales')->whereBetween('create_at', [$startDate, $endDate])->sum('charge') / 100;
}
}
// 职责类
class SalsReporter {
public $repo;
public function __construct($repo)
{
$this->repo = $repo;
}
public function between($startDate, $endDate, SalesOutputInterface $formater)
{
$sales = $this->repo->between($startDate, $endDate);
$formater->output($sales);
}
}
测试
$report = new SalsReporter(new SalesRepository);
$report->between(
now->subDays(10),
now(),
new HtmlOutput
);
结合 Laravel 的依赖注入,可以进一步简化
class SalsReporter {
public $repo;
public function __construct(SalesRepository $repo)
{
$this->repo = $repo;
}
public function between($startDate, $endDate, SalesOutputInterface $formater)
{
$sales = $this->repo->between($startDate, $endDate);
$formater->output($sales);
}
}
来源:laracasts.com
本作品采用《CC 协议》,转载必须注明作者和本文链接