PHP面向对象五大原则之接口隔离原则(ISP)详解


Posted in PHP onApril 04, 2018

本文实例讲述了PHP面向对象五大原则之接口隔离原则(ISP)。分享给大家供大家参考,具体如下:

设计应用程序的时候,如果一个模块包含多个子模块,那么我们应该小心对模块做出抽象。设想该模块由一个类实现,我们可以把系统抽象成一个接口。但是要添加一个新的模块扩展程序时,如果要添加的模块只包含原系统中的一些子模块,那么系统就会强迫我们实现接口中的所有方法,并且清寒要编写一些哑方法。这样的接口被称为肚胖接口或者被污染的接口,使用这样的接口将会给系统引入一些不当的行为,这些不当的行为可能导致不正确的结果,也可能导入资源浪费。

1.接口隔离

接口隔离原则(Interface Segregation Principle, ISP)表明客户端不应该被强迫实现一些他们不会使用的接口,应该把胖接口中的方法分组,然后用多个接口替代它,每个接口服务于一个子模块。简单地说,就是使用多个专门的接口比使用单个接口要好很多。

ISP的主要观点如下:

1)一个类对另外一个类的依赖性应当是建立在最小的接口上的。

ISP可以达到不强迫客户(接口的使用方法)依赖于他们不用的方法,接口的实现类应该只呈现为单一职责的角色(遵循SRP原则)

ISP还可以降低客户之间的相互影响---当某个客户要求提供新的职责(需要变化)而迫使接口发生改变时,影响到其他客户程序的可能性最小。

2)客户端程序不应该依赖它不需要的接口方法(功能)。

客户端程序就应该依赖于它不需要的接口方法(功能),那依赖于什么?依赖它所需要的接口。客户端需要什么接口就是提供什么接口,把不需要的接口剔除,这就要求对接口进行细化,保证其纯洁性。

比如在继承时,由于子类将继承父类中的所有可用方法;而父类中的某些方法,在子类中可能并不需要。例如,普通员工和经理都继承自雇员这个接口,员工需要每天写工作日志,而经理不需要。因此不能用工作日志来卡经理,也就是经理不应该依赖于提交工作日志这个方法。

可以看出,ISP和SRP在概念上是有一定交叉的。事实上,很多设计模式在概念上都有交叉,甚至你很难判断一段代码属于哪一种设计模式。

ISP强调的是接口对客户端的承诺越少越好,并且要做到专一。当某个客户程序的要求发生变化,而迫使接口发生改变时,影响到其他客户程序的可能性小。这实际上就是接口污染的问题。

2.对接口的污染

过于臃肿的接口设计是对接口的污染。所谓的接口污染就是为接口添加不必要的职责,如果开发人员在接口中增加一个新功能的目的只是减少接口实现类的数目,则此设计将导致接口被不断地“污染”并“变胖”。

“接口隔离”其实就是定制化服务设计的原则。使用接口的多重继承实现对不同的接口的组合,从而对外提供组合功能---达到“按需提供服务”。

接口即要拆,但也不能拆得太细,这就得有个标准,这就是高内聚。接口应该具备一些基本的功能,能独一完成一个基本的任务。

在实际应用中,会遇到如下问题:比如,我需要一个能适配多种类型数据库的DAO实现,那么首先应实现一个数据库操作的接口,其中规定一些数据库操作的基本方法,比如连接数据库、增删改查、关闭数据库等。这是一个最少功能的接口。对于一些MySQL中特有的而其他数据库里并不存在的或性质不同的方法,如PHP里可能用到的MySQL的pconnect方法,其他数据库里并不存在和这个方法相同的概念,这个方法也就不应该出现在这个基本的接口里,那这个基本的接口应该有哪些基本的方法呢?PDO已经告诉你了。

PDO是一个抽象的数据库接口层,它告诉我们一个基本的数据库操作接口应该实现哪些基本的方法。接口是一个高层次的抽象,所以接口里的方法都应该是通用的、基本的、不易变化的。

还有一个问题,那些特有的方法应该怎么实现?根据ISP原则,这些方法可以在别一个接口中存在,让这个“异类”同时实现这两个接口。

对于接口的污染,可以考虑这两条处理方式:

利用委托分离接口。

利用多继承分离接口。

委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理,如策略模式、代理模式等中都应用到了委托的概念。

再来看一下实例说明

你是否遇到过非常“胖”的接口呢?

举个例子来说吧:有一个跟动物有关的接口,代码如下:

<?php
interface Animal{
  public function walk();
  public function speak();
}

狗是这个接口的一个具体实现:

<?php
require_once "animal.php";
class Dog implements Animal{
  public function walk(){
    echo "dogs can walk";
  }
  public function speak(){
    echo "dogs can speak";
  }
}

ok,现在我们想创建一个鱼类,它会游泳,怎么办呢?我们必须要修改接口,还会影响到dog类的实现,而fish也需要实现walk和speak方法,如下代码所示:

Animal接口类:

<?php
interface Animal{
  public function walk();
  public function speak();
  public function swim();
}

dog类:

<?php
require_once "animal.php";
class Dog implements Animal{
  public function walk(){
    echo "dogs can walk";
  }
  public function speak(){
    echo "dogs can speak";
  }
  public function swim(){
  }
}

fish类:

<?php
require_once "animal.php";
class Fish implements Animal{
  public function walk(){
  }
  public function speak(){
  }
  public function swim(){
    echo "fish can swim";
  }
}

这时Animal接口类就呈现出了”胖“接口的特征了。所谓胖接口其实就是接口中定义了不是所有实现类都需要的方法,就像Animal接口类,有些动物是不会游泳的,有些动物是不会行走的,还有些动物是不会飞的。如果将这些方法都写在一个Animal接口类中,那么后期的扩展和维护简直就是一场灾难。

那么,怎么解决以上问题呢?

很简单,接口细化即可,将Animal接口类拆分成三个接口类:

animalCanWalk接口类:

<?php
interface animalCanSpeak{
  public function speak();
}

AnimalCanSwim接口类:

<?php
interface AnimalCanSwim{
  public function swim();
}

animalCanSpeak接口类:

<?php
interface animalCanSpeak{
  public function speak();
}

定义好这几个接口类之后,dog和fish的实现就容易多了,

<?php
require_once "animalCanSpeak.php";
require_once "animalCanWalk.php";
class Dog implements animalCanSpeak,animalCanWalk{
  public function walk(){
    echo "dogs can walk";
  }
  public function speak(){
    echo "dogs can speak";
  }
}
<?php
require_once "animalCanSwim.php";
class Fish implements AnimalCanSwim{
  public function swim(){
    echo "fish can swim";
  }
}

总结一下:

接口隔离原则(Interface  Segregation Principle, ISP)的概念:使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。

在使用接口隔离原则时,我们需要注意控制接口的粒度,接口不能太小,如果太小会导致系统中接口泛滥,不利于维护;接口也不能太大,太大的接口将违背接口隔离原则,灵活性较差,使用起来很不方便。一般而言,接口中仅包含为某一类用户定制的方法即可,不应该强迫客户依赖于那些它们不用的方法。

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

PHP 相关文章推荐
PHP 实现多服务器共享 SESSION 数据
Aug 15 PHP
PHP的SQL注入实现(测试代码安全不错)
Feb 27 PHP
初学PHP的朋友 经常问的一些问题。不断更新
Aug 11 PHP
利用php递归实现无限分类 格式化数组的详解
Jun 08 PHP
Drupal读取Excel并导入数据库实例
Mar 02 PHP
PHP使用imagick读取PDF生成png缩略图的两种方法
Mar 20 PHP
PHP网页游戏学习之Xnova(ogame)源码解读(十五)
Jun 30 PHP
PHP实现补齐关闭的HTML标签
Mar 22 PHP
php日期操作技巧小结
Jun 25 PHP
Laravel框架实现定时发布任务的方法
Aug 16 PHP
PHP实现浏览器格式化显示XML的方法示例
Jan 22 PHP
laravel实现查询最后执行的一条sql语句的方法
Oct 09 PHP
PHP面向对象五大原则之开放-封闭原则(OCP)详解
Apr 04 #PHP
PHP面向对象五大原则之单一职责原则(SRP)详解
Apr 04 #PHP
PHP基于面向对象实现的留言本功能实例
Apr 04 #PHP
PHP设计模式之工厂模式定义与用法详解
Apr 03 #PHP
PHP设计模式之原型模式定义与用法详解
Apr 03 #PHP
thinkPHP框架自动填充原理与用法分析
Apr 03 #PHP
PHP设计模式之适配器模式定义与用法详解
Apr 03 #PHP
You might like
php tp验证表单与自动填充函数代码
2012/02/22 PHP
PHP怎么实现网站保存快捷方式方便用户随时浏览
2013/08/15 PHP
php使用str_replace实现输入框回车替换br的方法
2014/11/24 PHP
PHP严重致命错误处理:php Fatal error: Cannot redeclare class or function
2017/02/05 PHP
PHP创建单例后台进程的方法示例
2017/05/23 PHP
php实现的中秋博饼游戏之掷骰子并输出结果功能详解
2017/11/06 PHP
关于ThinkPHP中的异常处理详解
2018/05/11 PHP
从sohu弄下来的flash中展示图片的代码
2007/04/27 Javascript
jQuery插件 tabBox实现代码
2010/02/09 Javascript
javascript 保存文件到本地实现方法
2012/11/29 Javascript
jquery写个checkbox——类似邮箱全选功能
2013/03/19 Javascript
Javascript缓存API
2016/06/14 Javascript
基于JavaScript实现点击页面任何位置返回
2016/08/31 Javascript
利用vue写todolist单页应用
2016/12/15 Javascript
Angular工具方法学习
2016/12/26 Javascript
Bootstrap响应式导航由768px变成992px的实现代码
2017/06/15 Javascript
node.js+express+mySQL+ejs+bootstrop实现网站登录注册功能
2018/01/12 Javascript
vue 实现复制内容到粘贴板clipboard的方法
2018/03/17 Javascript
JS实现获取进今年第几天是周几的方法分析
2018/06/27 Javascript
详解angular分页插件tm.pagination二次触发问题解决方案
2018/07/20 Javascript
nodejs之koa2请求示例(GET,POST)
2018/08/07 NodeJs
vue下载excel的实现代码后台用post方法
2019/05/10 Javascript
微信小程序动态添加和删除组件的现实
2020/02/28 Javascript
python中文编码问题小结
2014/09/28 Python
Python中urllib2模块的8个使用细节分享
2015/01/01 Python
Python面向对象class类属性及子类用法分析
2018/02/02 Python
Python脚本导出为exe程序的方法
2020/03/25 Python
通过python-pptx模块操作ppt文件的方法
2020/12/26 Python
班主任个人工作反思
2014/04/28 职场文书
2015年南京大屠杀纪念日活动总结
2015/03/24 职场文书
Nginx搭建rtmp直播服务器实现代码
2021/03/31 Servers
前端学习——JavaScript原生实现购物车案例
2021/03/31 Javascript
Python pygame实现中国象棋单机版源码
2021/06/20 Python
MySQL慢查询优化解决问题
2022/03/17 MySQL
Mysql事务索引知识汇总
2022/03/17 MySQL
Fluentd搭建日志收集服务
2022/09/23 Servers