PHP面向对象五大原则之单一职责原则(SRP)详解


Posted in PHP onApril 04, 2018

本文实例讲述了PHP面向对象五大原则之单一职责原则(SRP)。分享给大家供大家参考,具体如下:

单一职责原则(Single Pesponsibility Principle, SRP)

单一职责有两个含义: 一个是避免相同的职责分散到不同的类中, 别一个是避免一个类承担太多职责

为什么要遵守SRP呢?

(1)可以减少类之间的耦合

如果减少类之间的耦合,当需求变化时,只修改一个类,从而也就隔离了变化;如果一个类有多个不同职责,它们耦合在一起,当一个职责发生变化时,可能会影响到其他职责。

(2)提高类的复用性

修改电脑比修理电视机简单多了。主要原因就在于电视机各个部件之间的耦合性太高,而电脑则不同,电脑的内存、硬盘、声卡、网卡、键盘灯等部件都可以很容易地单独拆卸和组装。某个部件坏了,换上新的即可。上面的例子就体现了单一职责的优势。由于使用了单一职责,使得‘组件'可以方便地‘拆卸'和‘组装'。

不遵守SRP会影响对类的复用性。当只需要用该类的某一个职责时,由于它和其他的职责耦合在一起,也就很难分离出。

遵守SRP在实际代码开发中有没有什么应用?有的。以数据持久层为例,所谓的数据持久层主要指的是数据库操作,当然,还包括缓存管理等。这时就需要数据持久层支持多种数据库。应该怎么做?定义多个数据库操作类?想法已经很接近了,再进一步,就是使用工厂模式。

工厂模式(Faction)允许你在代码执行时实例化对象。它之所以被称为工厂模式是因为它负责‘生产对象'。以数据库为例,工厂需要的就是根据不同的参数,生成不同的实例化对象。最简单的工厂就是根据传入的类型名实例化对象,如传入MySQL,就调用MySQL类并实例化,如果是SQLite,则调用 SQLite的类并实例化,甚至还可以处理TXT、Execl等‘类数据库'。

工厂类也就是这样的一个类,它只负责生产对象,而不负责对象的具体内容。

以下是示例

定义一个适配器的接口

interface Db_Adpater
{
  /**
   * 数据库连接
   * @param $config 数据库配置
   * @return mixed resource
   */
  public function connect($config);
  /**
   * 执行数据库查询
   * @param $query 数据库查询的SQL字符串
   * @param $handle 连接对象
   * @return mixed
   */
  public function query($query,$handle);
}

定义一个实现了DB_Adpater接口的MySQL数据库操作类

class Db_Adapter_Mysql implements Db_Adpater
{
  private $_dbLink;  //数据库连接字符串标识
  /**
   * 数据库连接函数
   * @param $config 数据库配置
   * @return resource
   * @throws Db_Exception
   */
  public function connect($config)
  {
    if($this->_dbLink = @mysql_connect($config->host . (empty($config->port) ? '' : ':' . $config->prot) ,$config->user, $config->password, true))
    {
      if(@mysql_select_db($config->database, $this->_dbLink))
      {
        if($config->charset)
        {
          mysql_query("SET NAME '{$config->charset}'", $this->_dbLink);
        }
        return $this->_dbLink;
      }
    }
    throw new Db_Exception(@mysql_error($this->_dbLink));
  }
  /**
   * 执行数据库查询
   * @param $query 数据库查询SQL字符串
   * @param $handle 连接对象
   * @return resource
   */
  public function query($query,$handle)
  {
    if($resource = @mysql_query($query,$handle))
      return $resource;
  }
}

定义一个实现了DB_Adpater接口的SQLite数据库操作类

class Db_Adapter_sqlite implements Db_Adpater
{
  private $_dbLink;  //数据库连接字符串标识
  public function connect($config)
  {
    if($this->_dbLink = sqlite_open($config->file, 0666, $error))
    {
      return $this->_dbLink;
    }
    throw new Db_Exception($error);
  }
  public function query($query, $handle)
  {
    if($resource = @sqlite_query($query,$handle))
    {
      return $resource;
    }
  }
}

现在如果需要一个数据库操作的方法怎么做,只需定义一个工厂类,根据传入不同的生成需要的类即可

class sqlFactory
{
  public static function factory($type)
  {
    if(include_once 'Drivers/' . $type . '.php')
    {
      $classname = 'Db_Adapter_'.$type;
      return new $classname;
    }
    else
      throw new Exception('Driver not found');
  }
}

调用时,就可以这么写

$db = sqlFactory::factory('MySQL');
$db = sqlFactory::factory('SQLite');

我们把创建数据库连接这块程序单独拿出来,程序中的CURD就不用关心什么数据库了,只要按照规范使用对应的方法即可。

工厂方法让具体的对象解脱出来,使其不再依赖具体的类,而是抽象。

设计模式里面的命令模式也是SRP的体现,命令模式分离“命令的请求者”和“命令的实现者”方面的职责。举一个很好理解的例子,就是你去餐馆订餐吃饭,餐馆存在顾客、服务员、厨师三个角色。作为顾客,你要列出菜单,传给服务员,由服务员通知厨师去实现。作为服务员,只需要调用准备饭菜这个方法(对厨师喊“该炒菜了”),厨师听到要炒菜的请求,就立即去做饭。在这里,命令的请求和实现就完成了解耦。

模拟这个过程,首先定义厨师角色,厨师进行实际做饭、烧汤的工作。

以下是示例

/**
 * 厨师类,命令接受者与执行者
 * Class cook
 */
class cook
{
  public function meal()
  {
    echo '番茄炒鸡蛋',PHP_EOL;
  }
  public function drink()
  {
    echo '紫菜蛋花汤',PHP_EOL;
  }
  public function ok()
  {
    echo '完毕',PHP_EOL;
  }
}
//然后是命令接口
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();//把消息传给厨师,让厨师做饭,下同
  }
}
class DrinkCommand implements Command
{
  private $cook;
  //绑定命令接受者
  public function __construct(cook $cook)
  {
    $this->cook = $cook;
  }
  public function execute()
  {
    $this->cook->drink();
  }
}

现在顾客可以按照菜单叫服务员了

class cookControl
{
  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();
  }
}

好了,现在完成整个过程

$control = new cookControl;
$cook = new cook;
$mealcommand = new MealCommand($cook);
$drinkcommand = new DrinkCommand($cook);
$control->addCommand($mealcommand,$drinkcommand);
$control->callmeal();
$control->calldrink();

从上面的例子可以看出,原来设计模式并非纯理论的东西,而是来源于实际生活,就连普通的餐馆老板都懂设计模式这门看似高深的学问。其实,在经济和管理活动中对流程的优化就是对各种设计模式的摸索和实践。所以,设计模式并非计算机编程中的专利。事实上,设计模式的起源并不是计算机,而是源于建筑学。

在设计模式方面,不仅以上这两种体现了SRP,还有别的(比如代理模式)也体现了SRP。SRP不只是对类设计有意义,对以模块、子系统为单位的系统架构设计同样有意义。

模块、子系统也应该仅有一个引起它变化的原因,如MVC所倡导的各个层之间的相互分离就是SRP在系统总体设计中的应用。

SRP是最简单的原则之一,也是最难做好的原则之一。我们会很自然地将职责连接在一起。找到并且分离这些职责是软件设计需要达到的目的

一些简单的应用遵循的做法如下:

根据业务流程,把业务对象提炼出来。如果业务的流程的链路太复杂,就把这个业务对象分离为多个单一业务对象。当业务链标准化后,对业务对象的内部情况做进一步处理,把第一次标准化视为最高层抽象,第二次视为次高层抽象,以此类推,直到“恰如其分”的设计层次

职责的分类需要注意。有业务职责,还要有脱离业务的抽象职责,从认识业务到抽象算法是一个层层递进的过程。就好比命令模式中的顾客,服务员和厨师的职责,作为老板(即设计师)的你需要规划好各自的职责范围,即要防止越俎代庖,也要防止互相推诿。

希望本文所述对大家PHP程序设计有所帮助。

PHP 相关文章推荐
PHP GD 图像处理组件的常用函数总结
Apr 28 PHP
PHP原理之异常机制深入分析
Aug 08 PHP
PHP下利用shell后台运行PHP脚本,并获取该脚本的Process ID的代码
Sep 19 PHP
PHP文章采集URL补全函数(FormatUrl)
Aug 02 PHP
thinkphp验证码显示不出来的解决方法
Mar 29 PHP
typecho插件编写教程(三):保存配置
May 28 PHP
typecho插件编写教程(五):核心代码
May 28 PHP
php使用CURL不依赖COOKIEJAR获取COOKIE的方法
Jun 17 PHP
php简单实现sql防注入的方法
Apr 22 PHP
PHP中header用法小结
May 23 PHP
Windows平台实现PHP连接SQL Server2008的方法
Jul 26 PHP
phpstudy2018升级MySQL5.5为5.7教程(图文)
Oct 24 PHP
PHP基于面向对象实现的留言本功能实例
Apr 04 #PHP
PHP设计模式之工厂模式定义与用法详解
Apr 03 #PHP
PHP设计模式之原型模式定义与用法详解
Apr 03 #PHP
thinkPHP框架自动填充原理与用法分析
Apr 03 #PHP
PHP设计模式之适配器模式定义与用法详解
Apr 03 #PHP
PHP延迟静态绑定的深入讲解
Apr 02 #PHP
PHP设计模式之装饰器模式定义与用法详解
Apr 02 #PHP
You might like
php中$this->含义分析
2009/11/29 PHP
php导出word格式数据的代码实例
2013/11/25 PHP
php实现的支持imagemagick及gd库两种处理的缩略图生成类
2014/09/23 PHP
php中socket的用法详解
2014/10/24 PHP
PHP时间日期增减操作示例【date strtotime实现加一天、加一月等操作】
2018/12/21 PHP
解决php用mysql方式连接数据库出现Deprecated报错问题
2019/12/25 PHP
extjs form textfield的隐藏方法
2008/12/29 Javascript
基于jquery用于查询操作的实现代码
2010/05/10 Javascript
JQuery优缺点分析说明
2011/04/10 Javascript
javascript 兼容所有浏览器的DOM扩展功能
2012/08/01 Javascript
javascript 构造函数强制调用经验总结
2012/12/02 Javascript
jQuery中hide()方法用法实例
2014/12/24 Javascript
浅谈webpack打包过程中因为图片的路径导致的问题
2018/02/21 Javascript
vue-cli 3.x 配置Axios(proxyTable)跨域代理方法
2018/09/19 Javascript
手把手带你封装一个vue component第三方库
2019/02/14 Javascript
nodejs实现UDP组播示例方法
2019/11/04 NodeJs
从零学Python之入门(三)序列
2014/05/25 Python
在Python中使用模块的教程
2015/04/27 Python
图文详解WinPE下安装Python
2016/05/17 Python
Python中的日期时间处理详解
2016/11/17 Python
Python利用matplotlib生成图片背景及图例透明的效果
2017/04/27 Python
Python re 模块findall() 函数返回值展现方式解析
2019/08/09 Python
使用OpenCV circle函数图像上画圆的示例代码
2019/12/27 Python
python打开音乐文件的实例方法
2020/07/21 Python
简述python Scrapy框架
2020/08/17 Python
纯CSS实现的大小渐变、渐远效果
2014/04/15 HTML / CSS
联想台湾官网:Lenovo TW
2018/05/09 全球购物
有机婴儿毛毯和衣服:Monica + Andy
2020/03/01 全球购物
简述数组与指针的区别
2014/01/02 面试题
酒店开业策划方案
2014/06/02 职场文书
员工三分钟演讲稿
2014/08/19 职场文书
如何写辞职书
2015/02/26 职场文书
亮剑精神观后感
2015/06/05 职场文书
爱护环境建议书
2015/09/14 职场文书
字典算法实现及操作 --python(实用)
2021/03/31 Python
python编程学习使用管道Pipe编写优化代码
2021/11/20 Python