PHP高级特性-反射以及工厂设计模式的结合使用 [结合 Laravel-Admin 代码实例讲解]
利用反射来实现工厂模式的生产而无需创建特定的工厂类
转载请注明出处:https://janrs.com
转载请注明来源
反射[Relfection]
什么是Reflection
Reflection
,即反射。反射提供给面向对象编程可以自省的能力
这么理解有点太过于概念化,通俗地讲,就是能根据事件的结果反查出原因。在编程中,可以根据一个被实例化的对象,反查出这个对象属于的类以及该类拥有所有属性以及方法,甚至可以读取文档注释。这个反查的过程就叫做反射
PHP
提供了完整的反射 API
,提供了内省类、接口、函数、方法和扩展的能力。此外,反射 API
提供了方法来取出函数、类和方法中的文档注释。详细见PHP官网
PHP反射简介
Reflection
能干什么
在上面讲到的,可以使用反射来获取一个类的所有属性以及方法还有注释文档,甚至可以获取类属性和方法的访问权限[protected/private]
,这些特性使得PHP的使用灵活性得到非常大的提高。例如:
- Laravel
框架的所谓优雅所在,即容器、依赖注入、IOC
控制反转就是依靠这些特性实现的
- Hyperf
框架的注解路由也是根据反射获得注释来实现的
- 生成文档
因为反射可以获取类属性和方法的访问权限,可以扫描整个项目的所有文件再使用反射来生成文档
- 测试驱动开发
利用反射获取该类的所有方法的特性,进行测试驱动开发
- 开发插件
利用反射获取类的内部结构的特性,实现 Hook
功能,例如框架插件的实现
Reflection
的优缺点
优点
反射提供了对类的反解析,从而相比原本面向对象的编程方式获得了极高的灵活性,以及合理的使用能够让代码看起来更加优雅以及简洁。原本在面向对象的编程方式中,使用一个类的实例需要先 new
出一个对象再使用方法,但是使用了反射机制,只需要提供一个该类的方法然后使用反射机制即可使用该对象或者方法。Laravel
框架正是使用了大量的反射才获得了优雅的美誉,Swoole
的 Hyperf
框架的注解路由的实现也是使用了反射
缺点
同时,由于反射是类实例化的反过程,破坏了面向对象的封装性,直接将类的整个内部结构暴露,这就导致了反射一旦滥用,代码将难于管理,整个项目将非常混乱,甚至导致业务执行错乱。尤其在大项目几十人的团队中,试想一下,原本的面向对象,只告诉什么可以用,什么不可以用,CTO写好了底层代码,其他人继承后然后使用就行,内部结构啥的其他人都不知道。一旦用上了反射,如果有一个程序员不小心将原本是 protected
或者是 private
的属性或者方法设置成了可以访问,其他程序员在不知情的情况调用了本该隐藏的数据或者方法,那将导致不可预测的灾难【见下面示例代码】
其次,由于反射的灵活性极高,这导致了无法在 IDE
中通过直接直接点击代码溯源,对于新手真的是很蛋疼,Laravel
和Hyperf
都是如此
在下面的代码中,反射的机制直接将 private
方法设置成外部可访问
#Example:
<?php
class Foo {
private function myPrivateMethod() {
return 7;
}
}
$method = new ReflectionMethod('Foo', 'myPrivateMethod');
//该反射功能直接将原本是private权限的方法设置成可访问
$method->setAccessible(true);
echo $method->invoke(new Foo);
// echos "7"
?>
工厂设计模式
三种工厂设计模式 [简单工厂模式] [工厂模式] [抽象工厂模式]
简单工厂模式
又称为静态工厂方法模式。简单的说,就是创建对象的方式是通过一个静态方法来实现的。在简单工厂模式中,根据传递的参数来返回不同的类的实例
在PHP
中在简单工厂模式中,有一个抽象的产品类【即abstract class Calculate
】,这个抽象类可以是接口/抽象类/普通类
。这个抽象的产品类可以派生出多个具体的产品类【即class CalculateAdd
以及class CalculateSub
】。最后再由一个具体的工厂类【即class CalculateFactory
】来获取所需要的产品类的实例
代码实现
1) 抽象产品生产类:运算抽象类
//生产抽象类
abstract class Calculate{
//数字A
protected $number_a = null;
//数字B
protected $number_b = null;
//设置数字A
public function setNumberA( $number ){
$this->number_a = $number;
}
//设置数字B
public function setNumberB( $number ){
$this->number_b = $number;
}
//获取数字A
public function getNumberA(){
return $this->number_a;
}
//获取数字B
public function getNumberB(){
return $this->number_b;
}
//获取计算结果【获取生产出的产品】
public function getResult(){
return null;
}
}
2) 具体产品生产类:加法运算 / 减法运算 等等
//加法运算
class CalculateAdd extends Calculate{
//获取运算结果【获取具体的产品】
public function getResult(){
return $this->number_a + $this->number_b;
}
}
//减法运算
class CalculateSub extends Calculate{