PHP高级特性-反射Reflection以及Factory工厂设计模式的结合使用[代码实例]

简介: # PHP高级特性-反射以及工厂设计模式的结合使用 [结合 Laravel-Admin 代码实例讲解]

PHP高级特性-反射以及工厂设计模式的结合使用 [结合 Laravel-Admin 代码实例讲解]

利用反射来实现工厂模式的生产而无需创建特定的工厂类

本文地址http://janrs.com/?p=833转载无需经过作者本人授权

转载请注明来源


反射[Relfection]

JANRS.COM - PHP Reflection 反射

什么是Reflection

Reflection,即反射。反射提供给面向对象编程可以自省的能力

这么理解有点太过于概念化,通俗地讲,就是能根据事件的结果反查出原因。在编程中,可以根据一个被实例化的对象,反查出这个对象属于的类以及该类拥有所有属性以及方法,甚至可以读取文档注释。这个反查的过程就叫做反射

PHP 提供了完整的反射 API ,提供了内省类、接口、函数、方法和扩展的能力。此外,反射 API 提供了方法来取出函数、类和方法中的文档注释。详细见PHP官网 PHP反射简介

Reflection能干什么

在上面讲到的,可以使用反射来获取一个类的所有属性以及方法还有注释文档,甚至可以获取类属性和方法的访问权限[protected/private],这些特性使得PHP的使用灵活性得到非常大的提高。例如:

- Laravel 框架的所谓优雅所在,即容器、依赖注入、IOC 控制反转就是依靠这些特性实现的
- Hyperf 框架的注解路由也是根据反射获得注释来实现的
- 生成文档 因为反射可以获取类属性和方法的访问权限,可以扫描整个项目的所有文件再使用反射来生成文档
- 测试驱动开发 利用反射获取该类的所有方法的特性,进行测试驱动开发
- 开发插件 利用反射获取类的内部结构的特性,实现 Hook 功能,例如框架插件的实现

Reflection的优缺点

优点 反射提供了对类的反解析,从而相比原本面向对象的编程方式获得了极高的灵活性,以及合理的使用能够让代码看起来更加优雅以及简洁。原本在面向对象的编程方式中,使用一个类的实例需要先 new 出一个对象再使用方法,但是使用了反射机制,只需要提供一个该类的方法然后使用反射机制即可使用该对象或者方法。Laravel 框架正是使用了大量的反射才获得了优雅的美誉,SwooleHyperf 框架的注解路由的实现也是使用了反射

缺点 同时,由于反射是类实例化的反过程,破坏了面向对象的封装性,直接将类的整个内部结构暴露,这就导致了反射一旦滥用,代码将难于管理,整个项目将非常混乱,甚至导致业务执行错乱。尤其在大项目几十人的团队中,试想一下,原本的面向对象,只告诉什么可以用,什么不可以用,CTO写好了底层代码,其他人继承后然后使用就行,内部结构啥的其他人都不知道。一旦用上了反射,如果有一个程序员不小心将原本是 protected 或者是 private 的属性或者方法设置成了可以访问,其他程序员在不知情的情况调用了本该隐藏的数据或者方法,那将导致不可预测的灾难【见下面示例代码】

其次,由于反射的灵活性极高,这导致了无法在 IDE 中通过直接直接点击代码溯源,对于新手真的是很蛋疼,LaravelHyperf 都是如此

在下面的代码中,反射的机制直接将 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】来获取所需要的产品类的实例

![JARNS.COM - 工厂模式[简单工厂UML图]]

代码实现

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{
    
    //获取运算结果【获取具体的产品】
    public function getResult(){
        return $this->number_a - $this->number_b;
    }
}
//乘法 / 除法 等等其他运算【其他产品】
3) 工厂:工厂类。即用一个单独的类来创造实例化的过程,这个类就是工厂。也就是 简单工厂模式
php 中,实现的方式其实就一个 switch 函数或者是 php8 新出的 match 函数来实例化所需要的产品生产类

//根据运算不同实例化不同的对象
//【也就是根据所需产品,实例化对应的产品类进行生产】
//对应的实现其实就是一个switch或者php8函数新出的match函数
//下面用最新的match函数做演示
class CalculateFactory{
    
    public static function setCalculate( $type = null ){
        return match( $type ){
            'add' => (function(){
                return new CalculateAdd();
            })(),
            'sub' => (function(){
                return new CalculateSub();
            })(),
            default => null;
        };
    }
    
}

//具体使用

$calculate = CalculateFactory::setCalculate('add');
$calculate->setNumberA = 1;
$calculate->setNumberB = 2;

//计算
echo $calculate->getResult;//echo 3
总结:简单工厂模式其实就是创建一个基类【abstract】,该类存放所有具体生产产品类的共用的代码,但是没有执行过程,然后具体生产产品的类全部继承基类再实现各自的生产过程。最后创建一个工厂类,该类用来根据传入的参数来获取所需的生产类

工厂方法模式 又称为工厂模式,属于创造型模式。在工厂模式中,工厂类的父类只负责定义公共接口,并不执行实际的生产动作。实际的生产动作则交给工厂的子类来完成。这样做将类的的实例化延迟到了工厂的子类,通过工厂的子类来完成实例化具体的产品,也就是生产

在工厂模式中,跟简单工厂模式不一样的是,有一个抽象的工厂类【即interface CalculateFactory】,可以是接口/抽象类,这个抽象的工厂类可以派生出多个具体的工厂类【即FactoryAdd以及FactorySub

![JARNS.COM - 工厂模式[工厂UML图]]

代码实现【以下代码需要用到上面的生产抽象类】

以下代码需要用到上面的生产抽象类:abstract class Calculate
以及具体的生产类,即:CalculateAdd 以及 CalculateSub。下面不再重复实现


interface CalculateFactory{
    
    public function CreateCalculate();
    
}

class FactoryAdd implements CalculateFactory{
    
    public function CreateCalculate(){
        return new CalculateAdd();
    }
    
}

class FactorySub implements CalculateFactory{
    
    public function CreateCalculate(){
        return new CalculateSub();
    }
    
}

//具体使用

//创建工厂实例
$calculateFactory = new FactoryAdd();
$add = $calculateFactory->CreateCalculate();
$add->setNumberA( 1 );
$add->setNumberB( 2 );

//计算
echo $add->getResult();//echo 3
总结:工厂模式相比于简单工厂模式的区别在于,在简单工厂模式中,只有一个工厂来生产对应的生产对象【即CalculateFactory】。而在工厂模式中,每一个生产产对象都由自己的工厂来生产,并且这些工厂都继承自同一个接口【即 interface CalculateFactory

抽象工厂模式 抽象工厂模式提供创建一系列相关或相互依赖对象的接口,而且无需指定它们具体的类。这么理解很抽象。通俗一点的解释就是,相比于上面的工厂模式来讲,抽象工厂模式在每个不同的工厂之上又有一个超级工厂,这个超级工厂是抽象的接口【interface】,用来生产具体的工厂

在抽象工厂模式中,有多个抽象的产品类【即abstract class Phone以及abstract class Android】,可以是接口/抽象类/普通类,每个抽象产品类可以派生出多个具体产品类【即class IPhone / class MiPhone 以及 class IOS / class Android】。一个抽象的工厂类【即interface AbstractFactory】可以派生出多个具体的工厂类【即class iPhoneFactory以及class MiFactory】,且每个具体的工厂类可以创建多个产品类的实例【即都有createPhonecreateSystem

![JARNS.COM - 抽象工厂模式[工厂UML图]]

代码实现

//抽象的产品类
abstract class Phone{}
abstract class System{}

//具体的产品类
class IPhone extends Phone{}
class MiPhone extends Phone{}

//具体的产品类
class IOS extends System{}
class Android extends System{}

//超级工厂
interface AbstractFactory{
    public function createPhone();
    public function createSystem();
}

//具体的苹果工厂
class iPhoneFactory implements AbstractFactory{
    
    //生产苹果手机
    public function createPhone(){
        return new IPhone();
    }
    
    //生产苹果系统
    public function createSystem(){
        return new IOS();
    }
}

//具体的小米工厂
class MiFactory implements AbstractFactory{
    
    //生产小米手机
    public function createPhone(){
        return new MiPhone();
    }
    
    //生产安卓系统
    public function createSystem(){
        return new Android();
    }
}
总结:抽象工厂模式相比于工厂模式,抽象工厂模式提供了一个接口用来规定所需要生产的产品。每个继承于该接口的工厂都能按照指定的模式进行生产【代码中的AbstarctFactory

以上三种工厂模式,最终都是为了将重复的代码提取出来,并且按照特定的需求场景归纳好,进行解耦和复用,以便在需要的场景中直接使用

三种模式的概括为:

简单工厂:

  • 一个抽象产品类(可以是:接口,抽象类,普通类),可以派生出多个具体产品类
  • 单独一个具体的工厂类
  • 每个具体工厂类只能创建一个具体产品类的实例

工厂模式:

  • 一个抽象产品类(可以是:接口,抽象类,普通类),可以派生出多个具体产品类
  • 一个抽象工厂类(可以是:接口,抽象类),可以派生出多个具体工厂类
  • 每个具体工厂类只能创建一个具体产品类的实例

抽象工厂:

  • 多个抽象产品类(可以是:接口,抽象类,普通类),每个抽象产品类可以派生出多个具体产品类
  • 一个抽象工厂类(可以是:接口,抽象类),可以派生出多个具体工厂类
  • 每个具体工厂类可以创建多个具体产品类的实例

三个模式之间的区别:

  • 简单工厂模式只有一个抽象产品类,只有一个具体的工厂类
  • 工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个抽象产品类
  • 工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个具体产品类的实例

工厂模式与反射的结合使用

可以利用反射的特性来实现工厂模式的生产过程,结合Laravel-admin进行举例

先看下以下的代码,需求背景:需要根据角色不同显示不同的权限按钮

<?php
    
class TaskController extends BaseController
{
    use HasResourceActions;

    /**
     * Make a grid builder.
     *
     * @return Grid
     */
    protected function grid()
    {
            //Grid Columns...
        
            if (Admin::user()->inRoles([AdminUserModel::getAssignmentRole()])) {
                $grid->disableBatchActions();
                $grid->disableEditButton();
                $grid->disableCreateButton();
                $grid->disableDeleteButton();
            } elseif (Admin::user()->inRoles([AdminUserModel::getEvaluatorRole()])) {
                $grid->disableBatchActions();
                $grid->disableEditButton();
                $grid->disableCreateButton();
                $grid->disableDeleteButton();
                $grid->actions(function (Grid\Displayers\Actions $actions) {
                    $actions->append(new ConfirmCloseTaskAction());
                });
            } else {
                $grid->disableCreateButton();
                $grid->disableDeleteButton();
                $grid->disableEditButton();
                $grid->disableBatchActions();
                $grid->disableViewButton();
                $grid->disableActions();
            }
    }
}

以上的代码很明显一看就显得很臃肿。且随着业务的增加【即Controller的增加】以及角色的增加,需要写更多重复的判断以及重复的代码

解决思路:不同的角色需要拥有的不同的权限,每个角色都可以用一个固定的方法来设置权限,这个固定的方法可以为不同的角色设置权限。这些条件刚好满足工厂模式的使用场景:即:

  • 抽象出一个产品类来派生出多个角色的权限产品类
  • 抽象出一个工厂类来派生出多个具体的工厂类,这些工厂类表现为对应要使用权限按钮的场景
  • 每个具体工厂【使用权限按钮的场景】可以创建多个具体产品类【即实例化多个角色的权限产品】

代码如下【在下面的代码中,将使用反射来代替工厂的生产】

1) 抽象出一个产品类来派生出多个角色的权限产品类
<?php

namespace App\GridActionFactory;

use Dcat\Admin\Grid;

/**
 * 工厂接口
 */
interface GridActionInterface
{
    //业务员角色的权限
    function salesmanAction(Grid $grid);
    //分配员角色的权限
    function assignmentAction(Grid $grid);
    //财务角色的权限
    function financeAction(Grid $grid);
    
    //....其他角色的权限
}
2,3) 2,3两个步骤包含在一起。抽象出一个工厂类来派生出多个具体的工厂类,这些工厂类表现为对应要使用权限按钮的场景。其中,setRoleAction方法使用反射来直接生产,也就是替代了每个具体工厂类创建实例的过程
<?php

namespace App\GridActionFactory;

use Dcat\Admin\Admin;
use Dcat\Admin\Grid;

/**
 * 设置Action权限抽象类
 */
abstract class GridActionAbstract
{
    //
    abstract public static function setAction(Grid $grid, string $role);

    /**
     * 过滤角色
     *
     * @param string $role
     * @return bool
     */
    protected static function isInRoles(string $role): bool
    {
        return Admin::user()->inRoles([$role]);
    }

    /**
     * 调用对应的方法
     * [该方法其实就是工厂模式中的工厂,专门来生产的]
     * [多个工厂对应的就是各个需要用到Action权限的Controller控制器]
     * [每个Controller控制器来生产自己的Action权限]
     * [这个生产是通过反射来实现]
     *
     * @param Grid $grid
     * @param string $role
     * @param string $class
     * @throws \ReflectionException
     */
    protected static function setRoleAction(Grid $grid, string $role, string $class)
    {
        $r = new \ReflectionClass($class);

        $methodName = $role . 'Action';
        if (!$r->hasMethod($methodName))
            throw new \Exception('Method Not Found [ method : ' . $methodName . ' ] ');

        $method = $r->getMethod($methodName);
        $method->invoke($r->newInstance(), $grid);
    }
}

根据以上的反射来实现实例化的过程,上面的TaskController的权限可以简化成下面的代码:

<?php

namespace App\GridActionFactory;

use Dcat\Admin\Grid;

class TaskAction extends GridActionAbstract implements GridActionInterface
{
    /**
     * @param Grid $grid
     * @param string $role
     * @throws \ReflectionException
     */
    public static function setAction(Grid $grid, string $role)
    {
        if (!parent::isInRoles($role)) return;
        
        //通过调用父类的setRoleAction直接实现生产的过程
        parent::setRoleAction($grid, $role, self::class);
    }

    //在TaskController下有需要使用权限按钮的角色
    //分配员角色
    public function assignmentAction(Grid $grid)
    {
        //权限按钮
        $grid->showActions();
        $grid->showViewButton();
    }

    //在TaskController下有需要使用权限按钮的角色
    //财务角色
    public function financeAction(Grid $grid)
    {
        $grid->showActions();
        $grid->showViewButton();
    }

    //在TaskController下有需要使用权限按钮的角色
    //业务员角色
    public function salesmanAction(Grid $grid)
    {
    }
    
    //....其他角色
}

经过使用设计模式封装后,上面TaskController中控制权限的代码直接优化成如下:【优雅了不少~

<?php
    
class TaskController extends BaseController
{
    use HasResourceActions;

    /**
     * Make a grid builder.
     *
     * @return Grid
     */
    protected function grid()
    {
            //Grid Columns...
            //财务角色按钮
              TaskAction::setAction($grid, AdminUserModel::getFinanceRole());
            //分配员角色按钮
              TaskAction::setAction($grid, AdminUserModel::getAssignmentRole());
        
            //...其他角色按钮
            /*
            if (Admin::user()->inRoles([AdminUserModel::getAssignmentRole()])) {
                $grid->disableBatchActions();
                $grid->disableEditButton();
                $grid->disableCreateButton();
                $grid->disableDeleteButton();
            } elseif (Admin::user()->inRoles([AdminUserModel::getEvaluatorRole()])) {
                $grid->disableBatchActions();
                $grid->disableEditButton();
                $grid->disableCreateButton();
                $grid->disableDeleteButton();
                $grid->actions(function (Grid\Displayers\Actions $actions) {
                    $actions->append(new ConfirmCloseTaskAction());
                });
            } else {
                $grid->disableCreateButton();
                $grid->disableDeleteButton();
                $grid->disableEditButton();
                $grid->disableBatchActions();
                $grid->disableViewButton();
                $grid->disableActions();
            }
            */
    }
}

总结:设计模式以及反射通常在写框架的时候用的比较多。但是在项目中,适当的使用设计模式以及反射,能够让代码更加健壮以及可扩展,也很优雅~

欢迎来我的博客逛一逛 杨建勇的个人博客http://janrs.com

相关文章
|
12天前
|
IDE PHP 开发工具
PHP中的类型提示与严格模式:提高代码可维护性
随着PHP语言的发展,开发者对代码的可读性、可维护性和可靠性有了更高的要求。PHP中的类型提示(Type Hinting)和严格模式(Strict Mode)为开发者提供了更强的类型检查机制,有助于提升代码质量和减少潜在的错误,尤其是在大型项目中。
|
9天前
|
设计模式 缓存 数据库连接
深入理解PHP的面向对象编程与设计模式####
本文旨在通过实例解析PHP中面向对象编程的核心概念及其在实际开发中的应用,特别是单例模式、工厂模式和观察者模式等常见设计模式。文章将展示这些模式如何帮助开发者构建更加灵活、可维护的代码结构,以及它们在解决具体问题时的优势。 ####
|
11天前
|
PHP 开发者
PHP 7新特性深度解析及其最佳实践
【10月更文挑战第31天】本文将深入探讨PHP 7带来的革新,从性能提升到语法改进,再到错误处理机制的变革。我们将通过实际代码示例,展示如何高效利用这些新特性来编写更加健壮和高效的PHP应用。无论你是PHP新手还是资深开发者,这篇文章都将为你打开一扇窗,让你看到PHP 7的强大之处。
|
11天前
|
安全 编译器 PHP
PHP 8新特性解析与实践应用####
————探索PHP 8的创新功能及其在现代Web开发中的实际应用
|
18天前
|
设计模式 存储 数据库连接
PHP中的设计模式:单例模式的深入理解与应用
【10月更文挑战第22天】 在软件开发中,设计模式是解决特定问题的通用解决方案。本文将通过通俗易懂的语言和实例,深入探讨PHP中单例模式的概念、实现方法及其在实际开发中的应用,帮助读者更好地理解和运用这一重要的设计模式。
14 1
|
21天前
|
PHP 数据安全/隐私保护 开发者
PHP 7新特性解析与实践
【10月更文挑战第20天】本文将深入浅出地介绍PHP 7的新特性,包括性能提升、语法改进等方面。我们将通过实际代码示例,展示如何利用这些新特性优化现有项目,提高开发效率。无论你是PHP新手还是资深开发者,都能从中获得启发和帮助。
|
1月前
|
设计模式 PHP 开发者
PHP中的设计模式:桥接模式的解析与应用
在软件开发的浩瀚海洋中,设计模式如同灯塔一般,为开发者们指引方向。本文将深入探讨PHP中的一种重要设计模式——桥接模式。桥接模式巧妙地将抽象与实现分离,通过封装一个抽象的接口,使得实现和抽象可以独立变化。本文将阐述桥接模式的定义、结构、优缺点及其应用场景,并通过具体的PHP示例代码展示如何在实际项目中灵活运用这一设计模式。让我们一起走进桥接模式的世界,感受它的魅力所在。
|
29天前
|
设计模式 算法 数据库连接
PHP中的设计模式:提高代码的可维护性和扩展性
【10月更文挑战第13天】 本文将探讨PHP中常见的设计模式及其在实际项目中的应用。通过对比传统编程方式,我们将展示设计模式如何有效地提高代码的可维护性和扩展性。无论是单例模式确保类的单一实例,还是观察者模式实现对象间的松耦合,每一种设计模式都为开发者提供了解决特定问题的最佳实践。阅读本文后,读者将能更好地理解和应用这些设计模式,从而提升PHP编程的效率和质量。
|
1月前
|
设计模式 存储 数据库连接
PHP中的设计模式:单例模式的深入解析与实践
在PHP开发中,设计模式是提高代码可维护性、扩展性和复用性的关键技术之一。本文将通过探讨单例模式,一种最常用的设计模式,来揭示其在PHP中的应用及优势。单例模式确保一个类仅有一个实例,并提供一个全局访问点。通过实际案例,我们将展示如何在PHP项目中有效实现单例模式,以及如何利用这一模式优化资源配置和管理。无论是PHP初学者还是经验丰富的开发者,都能从本文中获得有价值的见解和技巧,进而提升自己的编程实践。
|
1月前
|
设计模式 算法 PHP
PHP中的设计模式:策略模式的深入解析与实践
【10月更文挑战第9天】 策略模式是一种行为设计模式,它允许在运行时选择算法的行为。在PHP开发中,通过使用策略模式,我们可以轻松切换算法或逻辑处理方式而无需修改现有代码结构。本文将深入探讨策略模式的定义、结构以及如何在PHP中实现该模式,并通过实际案例展示其应用价值和优势。
28 1