为什么要使用PHP单例模式及应用实例
首先我们要知道明确单例模式这个概念,那么什么是单例模式呢?
单例模式顾名思义,就是只有一个实例。
作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,
这个类我们称之为单例类。
单例模式的要点有三个:
一是某个类只能有一个实例;
二是它必须自行创建这个实例;
三是它必须自行向整个系统提供这个实例。
下面我们讨论下为什么要使用PHP单例模式?
多数人都是从单例模式的字面上的意思来理解它的用途, 认为这是对系统资源的节省, 可以避免重复实例化, 是一种”计划生育”。而PHP每次执行完页面都是会从内存中清理掉所有的资源. 因而PHP中的单例实际每次运行都是需要重新实例化的, 这样就失去了单例重复实例化的意义了. 单单从这个方面来说, PHP的单例的确有点让各位失望. 但是单例仅仅只有这个功能和应用吗? 答案是否定的,我们一起来看看。
1. php的应用主要在于数据库应用, 所以一个应用中会存在大量的数据库操作, 在使用面向对象的方式开发时(废话), 如果使用单例模式, 则可以避免大量的new 操作消耗的资源。
2. 如果系统中需要有一个类来全局控制某些配置信息, 那么使用单例模式可以很方便的实现. 这个可以参看zend framework的FrontController部分。
3. 在一次页面请求中, 便于进行调试, 因为所有的代码(例如数据库操作类db)都集中在一个类中, 我们可以在类中设置钩子, 输出日志,从而避免到处var_dump, echo。
* 单例模式举例,其要点如下:
*
* 1. $_instance 必须声明为静态的私有变量
* 2. 构造函数和克隆函数必须声明为私有的,这是为了防止外部程序new 类从而失去单例模式的意义
* 3. getInstance()方法必须声明为公有的,必须调用此方法以返回唯一实例的一个引用
* 4. ::操作符只能访问静态变量或静态函数
* 5. PHP的单例模式是相对而言的,因为PHP的解释运行机制使得每个PHP页面被解释执行后,所有的相关资源都会被回收。
* 也就是说,PHP在语言级别上没有办法让某个对象常驻内存。在PHP中,所有的变量都是页面级的,无论是全局变量, 还是类的静态成员,都会在页面执行完毕后被清空,结果会重新建立新的对象,这样也就完全失去了Singleton的意义。
* 不过,在实际应用中同一个页面中可能会存在多个业务逻辑,这时单例模式就起到了很重要的作用,有效的避免了重复 new 对象(注: new 对象会消耗内存资源)这么一个行为,所以我们说PHP的单例模式是相对而言的。
数组排序问题:
js:
<script>
var a=Array(1,2,3,100,200,60,50);
a.sort(function(a,b){
if(a<b){return 1 //a<b return 1从大到小的排序
} else{
return 0
}
})
alert(a);
</script>
PHP:方法一:
<?php
$a=array(1,2,3,100,200,60,50);
usort($a,function($a,$b){
if($a<$b){
return 1; //a<b return 1从大到小的排序
} else{
return 0;
}
});
print_r($a);
?>
PHP方法二:
<?php
$a=array(1,2,3,100,200,60,50);
rsort($a);//rsort从大到小排序 ;sort是从小到大
print_r($a);
?>
2个自定义的PHP in_array 函数,解决大量数据判断in_array的效率问题
大家可能都用过in_array来判断一个数据是否在一个数组中,一般我们的数组可能数据都比较小,对性能没什么影响,所以也就不会太在意。
但是如果数组比较大的时候,性能就会下降,运行的就会久一点,那如果针对在大数组情况下做优化呢,下面说两种方法(都是通过自定义函数来实现):
1.数组key与value翻转,通过isset判断key是否存在于数组中
/**
* in_array is too slow when array is large
*/
function inArray($item, $array) {
$flipArray = array_flip($array);
return isset($flipArray[$item]);
}
大家可能也会问为什么不用 array_key_exists 来做判断二用isset呢? 下面看下array_key_exists() 与 isset() 的对比:
isset()对于数组中为NULL的值不会返回TRUE,而array_key_exists()会。
<?php
$search_array = array('first' => null, 'second' => 4);
// returns false
isset($search_array['first']);
// returns true
array_key_exists('first', $search_array);
?>
2.用implode连接,直接用strpos判断
用implode函数+逗号连起来,直接用strpos判断。php里面字符串取位置速度非常快,尤其是在大数据量的情况下。不过需要注意的是首尾都要加”,” ,这样比较严谨。如: ,user1,user2,user3, 查找的时候,查,user1,。还有strpos要用!== false,因为第一个会返回0。示例如下:
return strpos($str,(string)$item)==false?false:true;**
* in_array is too slow when array is large
*/
function inArray($item, $array) {
$str = implode(',', $array);
return strpos($str,(string)$item)==false?false:true;
}
提示:以上2个函数均有bug!方法一数组翻转中,若原始数组值有null的,是会出问题的;方法二的只能验证字符串类型的。无法验证整形数字。总结:还是in_array($str,$arr,true) 验证方式最安全可靠。虽然性能相对底一点。
php自带的sort排序和用php实现排序算法的性能比较?
- 我今天特地试验了一下两者的性能
php自带的排序函数 100000的数据 排序 平均耗时0.068s microtime()
//php自带排序sort()耗时:0.12000608444214- 返回当前 Unix 时间戳的微秒数。
-
for ($i = 0; $i<100000;$i++){ $arr[] = rand(0,10000); } $t1 = microtime(true); sort($arr); $t2 = microtime(true); echo "php自带排序sort()耗时:".($t2-$t1);
自己写的快速排序 平均耗时1.0s
//快速排序耗时:1.7631008625031
for ($i = 0; $i<100000;$i++){ $arr[] = rand(0,100000); } $t1 = microtime(true); $returnAr = quickSort($arr); $t2 = microtime(true); echo "快速排序耗时:".($t2-$t1); //快速排序 function quickSort($arr) { //先判断是否需要继续进行 $length = count($arr); if($length <= 1) { return $arr; } //选择第一个元素作为基准 $base_num = $arr[0]; //遍历除了标尺外的所有元素,按照大小关系放入两个数组内 //初始化两个数组 $left_array = array(); //小于基准的 $right_array = array(); //大于基准的 for($i=1; $i<$length; $i++) { if($base_num > $arr[$i]) { //放入左边数组 $left_array[] = $arr[$i]; } else { //放入右边 $right_array[] = $arr[$i]; } } //再分别对左边和右边的数组进行相同的排序处理方式递归调用这个函数 $left_array = quickSort($left_array); $right_array = quickSort($right_array); //合并 return array_merge($left_array, array($base_num), $right_array); }
明显是php自带的函数排序速度快很多。
但重点是,为什么还有那么多问题是问 如何用php实现快速排序等算法?
php四排序-冒泡排序
算法和数据结构是一个编程工作人员的内功,技术牛不牛,一般都会看这两点。作为php程序员, 提升技能当然也得学习算法。
下面介绍四种入门级排序算法: 冒泡排序、选择排序、插入排序、快速排序。
一、冒泡排序
原理:对一组数据,比较相邻数据的大小,将值小数据在前面,值大的数据放在后面。 (以下都是升序排列,即从小到大排列)
举例说明: $arr = array(6, 3, 8, 2, 9, 1);
$arr 有6个数据,按照两两比较大小如下,注意 比较轮数 和 每轮比较次数
第一轮排序:
第一次比较 6和3比较 结果:3 6 8 2 9 1
第二次比较 6和8比较 结果:3 6 8 2 9 1
第三次比较 8和2比较 结果:3 6 2 8 9 1
第四次比较 8和9比较 结果:3 6 2 8 9 1
第五次比较 9和1比较 结果:3 6 2 8 1 9
第一轮比较总结:1.排序第1轮、比较5次,没有获得从小到大的排序 2.因为每次比较都是大数往后靠,所以比较完成后,可以确定大数排在最后(9 已经冒泡冒出来了,下轮比较可以不用比较了 )
第二轮排序:
第一次比较 3和6比较 结果:3 6 2 8 1 9
第二次比较 6和2比较 结果:3 2 6 8 1 9
第三次比较 6和8比较 结果:3 2 6 8 1 9
第四次比较 8和1比较 结果:3 2 6 1 8 9
第二轮比较总结:1.排序第2轮、比较4次,没有获得从小到大的排序 2.冒泡出了 8,下轮不用比较8 了
第三轮排序:
第一次比较 3和2比较 结果:2 3 6 1 8 9
第二次比较 3和6比较 结果:2 3 6 1 8 9
第三次比较 6和1比较 结果:2 3 1 6 8 9
第三轮比较总结:1.排序第3轮、比较3次,没有获得从小到大的排序 2.冒泡出了 6,下轮不用比较6 了
第四轮排序:
第一次比较 2和3比较 结果:2 3 1 6 8 9
第二次比较 3和1比较 结果:2 1 3 6 8 9
第四轮比较总结:1.排序第4轮、比较2次,没有获得从小到大的排序 2.冒泡出了 3,下轮不用比较3 了
第五轮排序:
第一次比较 2和1比较 结果:1 2 3 6 8 9
第五轮比较总结:1.排序第5轮、比较1次,没有获得从小到大的排序 2.冒泡出了 2,由于还剩一个1,不用再比较了,至此通过5轮排序,完成整个排序。
通过以上五轮排序,若干次比较,我们有理由推断出一个结论:
对于一个长度为N的数组,我们需要排序 N-1 轮,每 i 轮 要比较 N-i 次。对此我们可以用双重循环语句,外层循环控制循环轮次,内层循环控制每轮的比较次数。
<?php
function getpao($arr)
{
$len=count($arr);
//设置一个空数组 用来接收冒出来的泡
//该层循环控制 需要冒泡的轮数
for($i=1;$i<$len;$i++)
{ //该层循环用来控制每轮 冒出一个数 需要比较的次数
for($k=0;$k<$len-$i;$k++)
{
if($arr[$k]>$arr[$k+1])
{
$tmp=$arr[$k+1];
$arr[$k+1]=$arr[$k];
$arr[$k]=$tmp;
}
}
}
return $arr;
}
$arr= array(6,3,8,2,9,1);
$res = getpao($arr);
print_r($res);
thinkphp5中.htaccess 的定义,通过这个模式,也适用于其他.htaccess定义
<IfModule mod_rewrite.c>
Options +FollowSymlinks -Multiviews
RewriteEngine On
#index.php/admin/users/index.html
RewriteRule ^(.*)list\.html$ $1/admin/users/index\.html
#index.php/admin/users/update/id/1.html
RewriteRule ^(.*)show_([0-9]+)\.html$ $1/admin/users/update/id/$2
#index.php/admin/users/index.html?page=2
RewriteRule ^(.*)p_([0-9]+)\.html$ $1/admin/users/index/page/$2
# http://127.0.0.1/tc/004_oop/tp5/public/p_2.html
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
</IfModule>
tp5分页效果,中间数字化的设置。
核心分页默认文件的路径:tp5\thinkphp\library\think\paginator\driver\Bootstrap.php
/**
* 页码按钮
* @return string
*/
/*
protected function getLinks()
{
}
这个函数是我们要修改替换的!
我这里推荐一个:http://www.thinkphp.cn/code/3000.html
public $rollPage=1;//侧面显示个数
protected function getLinks()
{
if ($this->simple)
return '';
$block = [
'first' => null,
'slider' => null,
'last' => null
];
$side = $this->rollPage;
$window = $side * 2;
if ($this->lastPage < $window +1) {
$block['slider'] = $this->getUrlRange(1, $this->lastPage);
} elseif ($this->currentPage <= $window-1) {
$block['slider'] = $this->getUrlRange(1, $window + 1);
} elseif ($this->currentPage > ($this->lastPage - $window+1)) {
$block['slider'] = $this->getUrlRange($this->lastPage - ($window), $this->lastPage);
} else {
$block['slider'] = $this->getUrlRange($this->currentPage - $side, $this->currentPage + $side);
}
$html = '';
if (is_array($block['first'])) {
$html .= $this->getUrlLinks($block['first']);
}
if (is_array($block['slider'])) {
$html .= $this->getUrlLinks($block['slider']);
}
if (is_array($block['last'])) {
$html .= $this->getUrlLinks($block['last']);
}
return $html;
}
控制器中如何智能化设置呢?注意上面的:public $rollPage=1;//侧面显示个数
我们在控制器中的引用是这样的:
分页栏每页显示的页数进行定义了。
tp5分页效果 上一篇,下一篇的自定义修改。
核心分页默认文件的路径:tp5\thinkphp\library\think\paginator\driver\Bootstrap.php
对应的上一个方法名:
/**
* 上一页按钮
* @param string $text
* @return string
*/
protected function getPreviousButton($text = "«")
{
}
/**
* 下一页按钮
* @param string $text
* @return string
*/
protected function getNextButton($text = '»')
{
详细略
}
提示:你可以直接设置$text="默认值为上一篇/下一篇",但是这样就写死了,我们希望通过视图的方式直接修改上一篇或者下一篇的具体内容。
视图代码:
{$list->render("上一个","下一个")} ;//注意,默认这样传值是不对的,我们还需要按如下流程操作。
在tp5\thinkphp\library\think\paginator\driver\Bootstrap.php 文件中
搜索: public function render()
修改:
public function render($a="",$b="")
{
if ($this->hasPages()) {
if ($this->simple) {
return sprintf(
'<ul class="pager">%s %s</ul>',
$this->getPreviousButton($a),
$this->getNextButton($b)
);
} else {
return sprintf(
'<ul class="pagination">%s %s %s</ul>',
$this->getPreviousButton($a),
$this->getLinks(),
$this->getNextButton($b)
);
}
}
}
php面向对象单例模式、工厂模式;面向对象三大基本特性与五大基本原则
一.单例模式
目的:为了控制对象的数量(只能够有一个,相当于类的计划生育)
做法:
1.在类里面做了一个公有的静态方法来造对象
2.将类的构造方法做成私有的;还要防止子类中重新定义构造方法,所以这里还要加final; final private function __construct(){}
3.防止clone,所以我们还需要定义一个final private function __clone(){}
class Ren
{
public $name;
static public $dx; //静态成员变量用来存储该对象
final private function __construct() //把构造函数设置为私有,类的外部就不能用new造对象了
{
}
final private function __clone() //防止clone复制对象
{
}
static function DuiXiang() //做一个静态的方法,用来在类的内部造对象
{
if(empty(self::$dx)) //判断上面定义的变量是否有值,如果没有,就造一个对象,如果有,就直接输出这个
{
self::$dx = new Ren(); //因为是静态变量,只能用类名调用
}
return self::$dx;
}
}
$r = Ren::DuiXiang();
$r->name = "张三";
$r1 = Ren::DuiXiang();
$r1->name = "李四";
var_dump($r); //由于上面已经造了一个对象$r,所以当再造$r1时,$r1跟$r是一样的对象
二、工厂模式
工厂模式的最大优点在于创建对象上面,就是把创建对象的过程封装起来,这样随时可以产生一个新的对象。
减少代码进行复制粘帖,耦合关系重,牵一发动其他部分代码。
通俗的说,以前创建一个对象要使用new,现在把这个过程封装起来了。
假设不使用工厂模式:那么很多地方调用类a,代码就会这样子创建一个实例:new a(),假设某天需要把a类的名称修改,意味着很多调用的代码都要修改。
工厂模式的优点就在创建对象上。
工厂模式的优点就在创建对象上。建立一个工厂(一个函数或一个类方法)来制造新的对象,它的任务就是把对象的创建过程都封装起来,
创建对象不是使用new的形式了。而是定义一个方法,用于创建对象实例。
abstract class YunSuan
{
public $a;
public $b;
function Suan(){}
}
class Jia extends YunSuan
{
function Suan(){
return $this->a+$this->b;
}
}
class Jian extends YunSuan
{
function Suan(){
return $this->a-$this->b;
}
}
class Cheng extends YunSuan{
function Suan(){
return $this->a*$this->b;
}
}
//做一个工厂类
class GongChang{
static function ShengChan($fuhao){
if($fuhao=="+"){
return new Jia();
}else if($fuhao=="*"){
return new Cheng();
}
}
}
//算加法
$suan = GongChang::ShengChan("+");
$suan->a = 10;
$suan->b = 5;
echo $suan->Suan();
静态方法可以调静态变量,但不能调普通变量
普通方法可以调静态变量,也可以普通变量
面向对象三大基本特性与五大基本原则
三大特性是:封装、继承、多态
所谓封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
封装是面向对象的特征之一,是对象和类概念的主要特性。 简单的说,一个类就是一个封装了数据以及操作这些数据的代码的逻辑实体。在一个对象内部,某些代码或某些数据可以是私有的,不能被外界访问。通过这种方式,对象对内部数据提供了不同级别的保护,以防止程序中无关的部分意外的改变或错误的使用了对象的私有部分。
所谓继承是指可以让某个类型的对象获得另一个类型的对象的属性的方法,它支持按级分类的概念。
继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。 通过继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”、“父类”或“超类”。继承的过程,就是从一般到特殊的过程。要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。继承概念的实现方式有二类:实现继承与接口继承。实现继承是指直接使用基类的属性和方法而无需额外编码的能力;接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力;
所谓多态就是指一个类实例的相同方法在不同情形有不同表现形式。
多态是面向对象的三大特性中除封装和继承之外的另一重要特性。它展现了动态绑定的功能,也称为“同名异式”。多态的功能可让软件在开发和维护时,达到充分的延伸性。事实上,多态最直接的定义是让具有继承关系的不同类对象,可以对相同名称的成员函数调用,产生不同反应效果。【多态性概念】所谓多态性就是指一段程序能够处理多种类型对象的能力。在PHP中,多态一般情况可以理解就是方法的重写【很多资料都是这么解释的,虽然这样不是很严禁!因为php不具有像java那种清晰的多态】。方法重写是指一个子类中可以重新修改父类中的某些方法,使其具有自己的特征。重写要求子类的方法和父类的方法名称相同,这可以通过声明抽象类或是接口来规范。
多态的好处:大大提高程序的扩展,增强系统的灵活性,降低模块间的耦合。
虽然php不具有像java那种清晰的多态;但是严禁上说可以通过编程逻辑实现多态,如下:php体现现了多态。
<?php
abstract class animal{
abstract function fun();
}
class cat extends animal{
function fun(){
echo "cat say miaomiao...";
}
}
class dog extends animal{
function fun(){
echo "dog say wangwang...";
}
}
function work($obj){
if($obj instanceof animal){
$obj -> fun();
}else{
echo "no function";
}
}
work(new dog());
work(new cat());
上面通过一个关键字instanceof来判断,变量指向的对象是否是animal类的一个实例,下面new cat(),new dog()都是animal子类的对象,而输出了“dog say wangwang…”和“cat say miaomiao…”,说明子类对象是父类的一个实例,从而达到了java多态的功能。
上边的类是抽象类,也表明了接口与实现接口的类对象同样可以适用。
至此,得出php虽然多态体现模糊,但还是具有多态特性的。
PHP5中实现多态的两种方法实例分享 https://www.jb51.net/article/49184.htm 浅谈多态以及php的实现方法 https://blog.csdn.net/whd526/article/details/70242918 重载不应归在多态的范畴内 https://blog.csdn.net/whd526/article/details/70240684 方法重写与方法重载的区别 https://www.cnblogs.com/zheting/p/7751787.html 重写: 类的继承关系可以产生一个子类,子类继承父类,它具备了父类所有的特征, 继承了父类所有的方法和变量。 子类可以定义新的特征,当子类需要修改父类的一些方法进行扩展,增大功能, 程序设计者常常把这样的一种操作方法称为重写,也叫称为覆写或覆盖。 重载: 方法重载是让类以统一的方式处理不同类型数据的一种手段。 调用方法时通过传递给它们的不同个数和类型的参数来决定具体使用哪个方法, 这就是多态性。 提示:因为php是弱类型语言,一个类中它也不允许定义相同方法名。 所以针对php,很多资料都把重写和重载看成一个概念【重写】。
五大基本原则
1、单一职责原则SRP(Single Responsibility Principle)
是指一个类的功能要单一,不能包罗万象。如同一个人一样,分配的工作不能太多,否则一天到晚虽然忙忙碌碌的,但效率却高不起来。
2、开放封闭原则OCP(Open-Close Principle)
一个模块在扩展性方面应该是开放的而在更改性方面应该是封闭的。比如:一个网络模块,原来只服务端功能,而现在要加入客户端功能,
那么应当在不用修改服务端功能代码的前提下,就能够增加客户端功能的实现代码,这要求在设计之初,就应当将服务端和客户端分开,公共部分抽象出来。
3、替换原则(the Liskov Substitution Principle LSP)
子类应当可以替换父类并出现在父类能够出现的任何地方。比如:公司搞年度晚会,所有员工可以参加抽奖,那么不管是老员工还是新员工,
也不管是总部员工还是外派员工,都应当可以参加抽奖,否则这公司就不和谐了。
使用里氏替换原则时需要注意,子类的所有方法必须在父类中声明,或子类必须实现父类中声明的所有方法。尽量把父类设计为抽象类或者接口,让子类继承父类或实现父接口,并实现在父类中声明的方法,运行时,子类实例替换父类实例,我们可以很方便地扩展系统的功能。
里氏替换原则是对类继承的一种约束。对里氏替换原则有两种理解:
1. 不能随便去继承不合适的,有多余方法或者属性的类。
2. 子类可以扩展父类的功能,但不能改变父类原有的功能。
4、依赖(倒置)原则(the Dependency Inversion Principle DIP) 具体依赖抽象,上层依赖下层。依赖倒置原则核心一句话:面向接口编程。
“依赖倒置是一种软件设计思想,在传统软件中,上层代码依赖于下层代码,当下层代码有所改动时,上层代码也要相应进行改动,因此维护成本较高。而依赖倒置原则的思想是,上层不应该依赖下层,应依赖接口。意为上层【汽车】代码定义接口,下层代码实现该接口,从而使得下层【具体汽车如宝马,奔驰】依赖于上层接口,降低耦合度,提高系统弹性”。
<?php
// 司机开奔驰,未用依赖倒置原则的写法
class Benz{
public function run(){
return " Benz is runing!!!";
}
}
class Driver{
public function drive(Benz $car){
echo $car -> run();
}
}
class Client{
public static function doing(){
$driver = new Driver();
$driver -> drive( new Benz() );
}
}
Client :: doing();
// 那么如果司机想开宝马呢?,是不是就要修改Driver了,这就违反了开闭原则了,怎么能只在Client添加代码就让宝马车也会开呢?
interface ICar{
//定义一个汽车接口
public function run();
}
class BMW implements ICar{
public function run(){
return "BMW is runing !!!";
}
}
class Benz implements ICar{
public function run(){
return "Benz is runing !!!";
}
}
interface IDriver{
//定义一个司机接口,以防以后有A照,B照,C照的
public function drive(ICar $car);
}
class Driver implements IDriver{
public function drive(ICar $car){
echo "<br>" . $car -> run();
}
}
class Client{
public static function doing(){
$driver = new Driver();
$driver -> drive( new BMW() ); //开宝马
$driver -> drive( new Benz() ); //开奔驰
.
.
.
.
}
}
Client :: doing();
?>
案例解释二:
比如有这么个需求,用户注册完成后要发送一封邮件,然后你有如下代码:
先有邮件类'Email.class.php'
class Mail{
public function send()
{
/*这里是如何发送邮件的代码*/
}
}
然后又注册的类'Register.class.php'
class Register{
private $_emailObj;
public function doRegister()
{
/*这里是如何注册*/
$this->_emailObj = new Mail();
$this->_emailObj->send();//发送邮件
}
}
然后开始注册:
include 'Mail.class.php';
include 'Register.class.php';
$reg = new Register();
$reg->doRegister();
看起来事情很简单,你很快把这个功能上线了,看起来相安无事... xxx天过后,产品人员说发送邮件的不好,要使用发送短信的,然后你说这简单我把'Mail'类改下...
又过了几天,产品人员说发送短信费用太高,还是改用邮件的好... 此时心中一万个草泥马奔腾而过...
这种事情,常常在产品狗身上发生,无可奈何花落去...
以上场景的问题在于,你每次不得不对'Mail'类进行修改,代码复用性很低,高层过度依赖于底层。那么我们就考虑'依赖倒置原则',让底层继承高层制定的接口,高层依赖于接口。
interface Mail
{
public function send();
}
class Email implements Mail()
{
public function send()
{
//发送Email
}
}
class SmsMail implements Mail()
{
public function send()
{
//发送短信
}
}
class Register
{
private $_mailObj;
public function __construct(Mail $mailObj)
{
$this->_mailObj = $mailObj;
}
public function doRegister()
{
/*这里是如何注册*/
$this->_mailObj->send();//发送信息
}
}
下面开始发送信息
/* 此处省略若干行 */
$reg = new Register();
$emailObj = new Email();
$smsObj = new SmsMail();
$reg->doRegister($emailObj);//使用email发送
$reg->doRegister($smsObj);//使用短信发送
/* 你甚至可以发完邮件再发短信 */
上面的代码解决了'Register'对信息发送类的依赖,使用构造函数注入的方法,使得它只依赖于发送短信的接口,只要实现其接口中的'send'方法,不管你怎么发送都可以。上例就使用了"注入"这个思想,就像注射器一样将一个类的实例注入到另一个类的实例中去,需要用什么就注入什么。当然"依赖倒置原则"也始终贯彻在里面。"注入"不仅可以通过构造函数注入,也可以通过属性注入,上面你可以可以通过一个"setter"来动态为"mailObj"这个属性赋值。
https://www.cnblogs.com/painsOnline/p/5138806.html
5、接口分离原则(the Interface Segregation Principle ISP)
模块间要通过抽象接口隔离开,而不是通过具体的类强耦合起来