单一职责有两种含义:一是避免相同的职责分散到不同的类中,另一个是避免一个类承担太多职责。遵循单一职责的目的:
(1)可以减少类之间的耦合(模块与模块之间的依赖关系)
减少类之间的耦合,当需求发生变化时,我们只需要修改一个类,从而也就隔离了变化;如果一个类有多个不同职责,它们耦合在一起,当一个职责发生改变时,可能会影响到其他职责。
(2)提高类的复用性
当只需要复用该类的一个职责时,由于与其他类耦合度过多,导致很难分离出。
遵循SRP在代发开发中的应用:
一、工厂模式
工厂模式允许在代码执行时实例化对象。它负责“生产”对象,最简单的工厂就是根据传入的类型名实例化对象。以数据库为例,也许在一个项目中需要不同种类数据库之间的数据交互,我们在之前需要定义多个数据库类,工厂模式的存在是我们可以根据不同的参数类型,实现不同数据库的调用。
首先我们定义一个适配器接口
interface Db_Adapter
{
/*
* 数据库连接
* @param $config 数据库配置
* @return resource
* */
public function connect($config);//接口的方法不能拥有主体
/*
* 执行数据库查询
* @param string $query 数据库查询SQL字符串
* @param mixed $handle 连接对象
* @return resource
* */
public function query($query,$handle);
}
这是一个简化的接口,并没有提供具体的方法,其定义了数据库的操作类,这个类实现了Db_Adapter接口
定义Mysql数据库的操作类
class Db_Adapter_Mysql implements Db_Adapter { private $_dblink;//链接数据库字符串标识 /* * @param $config 数据库配置 * @throw Db_Exception * @return resource * */ public function connect($config) { if($this->_dblink = @mysql_connect($config->HOST.(empty($config->PORT)?'':':'.$config->port),$config->USER,$config->PWD,true)) { if(@mysql_select_db($config->database,$config->_dblink)) { return $this->_dblink; } else{ throw new Db_Exception(@mysql_error($config->_dblink)); } } // TODO: Implement connect() method. }
public function query($query, $handle) { if ($resource = @mysql_query($query,$handle)) {/* * 执行数据库查询 * @param string $query 数据库查询SQL字符串 * @param mixed $handle 连接对象 * @return resource * */
return $resource;
}
// TODO: Implement query() method. }} 下面定义一个SQLite数据库的操作类
现在需要一个数据库操作方法的时候,只需定义一个工厂类class Db_Adapter_SQLite implements Db_Adapter { private $_dblink;//链接数据库字符串标识 /* * @param $config 数据库配置 * @throw Db_Exception * @return resource * */ public function connect($config) { if ($this->_dblink = sqlite_open($config->file,0066,$error)) { return $this->_dblink; } throw new Db_Exception($error); // TODO: Implement connect() method. } public function query($query, $handle) { if ($resource = @sqlite_query($query,$handle)) { return $resource; } // TODO: Implement query() method. } }
class SqlFactory { public static function factory ($type) { if(include_once"Db_Adpater_{$type}.class.php") { $className = 'Db_Adapter_'.$type; return new $className; } else { throw new Exception('Driver not found'); } } }
调用时:我们把创建数据库连接这块单独拿出来,程序中的CRUD就不用关心是什么数据库了,只需要规划是哦那个对应的方法即可。工厂方法让具体的对象解放出来,使其不再依赖具体的类,而是抽象。$db = sqlFactory::factory('Mysql');
二、命令模式
设计模式里面的命令模式也是单一职责的体现,命令模式分离“命令的请求者”和“命令的实现者”方面的职责。比如说在一个餐馆里面,有厨师、服务员和顾客三种角色。作为顾客,只需要列出清单,传给服务员,由服务员通知厨师实现。作为服务员,只需要调用饭菜这个方法,厨师收到炒菜的请求,就立刻去做饭。在这里,命令的请求和实现就完成了解耦。
模拟这个过程,首先,定义厨师角色,处理进行实际的炒菜、做汤的操作。
/* * 厨师,命令的接收者与执行者 * */ class Cook { public function meal () { echo "aaa",PHP_EOL; //做饭的命令 } public function drink () { echo "bbb";//做汤的命令 } } interface Command{ //命令接口 public function execute(); }
接下来模拟服务员和厨师的过程//模拟厨师与服务员的过程 class MealCommand implements Command { private $cook;//绑定命令接收者 public function __construct(Cook $cook) { $this->cook = $cook; } public function execute() { $this->cook->meal(); // TODO: Implement execute() method. } } class DrinkCommand implements Command { private $cook;//绑定命令接收者 public function __construct(Cook $cook) { $this->cook = $cook; } public function execute() { $this->cook->drink(); // TODO: Implement execute() method. } }
模拟顾客和服务员的过程class CookControler { private $mealcommand; private $drinkcommand;//将命令发送者绑定到命令接收者上面(服务员) public function addCommand (Command $mealCommand,Command $drinkCommand) { $this->mealcommand = $mealCommand; $this->drinkcommand = $drinkCommand; } public function callMeal () { $this->mealcommand->execute(); } public function callDrink () { $this->drinkcommand->execute(); } }
实现命令模式:设计模式并非理论的东西,而是来源于生活。在设计模式方面体现的SRP的还有别的(代理模式),SRP是最简单的原则之一,也是最难设计的原则之一。$cook = new Cook(); $mealCommand = new MealCommand($cook); $drinkCommand = new DrinkCommand($cook); $cookController = new CookControler(); $cookController->addCommand($mealCommand,$drinkCommand); $cookController->callDrink(); $cookController->callMeal();