0

0

PHP高级特性:反射与工厂设计模式如何结合使用

藏色散人

藏色散人

发布时间:2021-12-17 14:18:05

|

4550人浏览过

|

来源于janrs

转载

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

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

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

反射[Relfection]

e61e2dfc17a926f0bb0ae8dd436b083.png

什么是Reflection

立即学习PHP免费学习笔记(深入)”;

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

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

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:
setAccessible(true);
echo $method->invoke(new Foo);
// echos "7"
?>

工厂设计模式

三种工厂设计模式 [简单工厂模式] [工厂模式] [抽象工厂模式]

简单工厂模式 又称为静态工厂方法模式。简单的说,就是创建对象的方式是通过一个静态方法来实现的。在简单工厂模式中,根据传递的参数来返回不同的类的实例

在PHP中在简单工厂模式中,有一个抽象的产品类【即abstract class Calculate】,这个抽象类可以是接口/抽象类/普通类。这个抽象的产品类可以派生出多个具体的产品类【即class CalculateAdd以及class CalculateSub】。最后再由一个具体的工厂类【即class CalculateFactory】来获取所需要的产品类的实例

7106906b2a91a8c8b2ac08f3de1b101.png

代码实现

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】

8c511ac264c9d2aa8e59086b40f1869.png

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

以下代码需要用到上面的生产抽象类: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】,用来生产具体的工厂

可赞AI
可赞AI

文字一秒可视化,免费AI办公神器

下载

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

92b16f990de562f959ad21e832b785e.png

代码实现

//抽象的产品类
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进行举例

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

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) 抽象出一个产品类来派生出多个角色的权限产品类

2,3) 2,3两个步骤包含在一起。抽象出一个工厂类来派生出多个具体的工厂类,这些工厂类表现为对应要使用权限按钮的场景。其中,setRoleAction方法使用反射来直接生产,也就是替代了每个具体工厂类创建实例的过程

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的权限可以简化成下面的代码:

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

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

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/?p=833

相关文章

PHP速学教程(入门到精通)
PHP速学教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

php

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
拼多多赚钱的5种方法 拼多多赚钱的5种方法
拼多多赚钱的5种方法 拼多多赚钱的5种方法

在拼多多上赚钱主要可以通过无货源模式一件代发、精细化运营特色店铺、参与官方高流量活动、利用拼团机制社交裂变,以及成为多多进宝推广员这5种方法实现。核心策略在于通过低成本、高效率的供应链管理与营销,利用平台社交电商红利实现盈利。

28

2026.01.26

edge浏览器怎样设置主页 edge浏览器自定义设置教程
edge浏览器怎样设置主页 edge浏览器自定义设置教程

在Edge浏览器中设置主页,请依次点击右上角“...”图标 > 设置 > 开始、主页和新建标签页。在“Microsoft Edge 启动时”选择“打开以下页面”,点击“添加新页面”并输入网址。若要使用主页按钮,需在“外观”设置中开启“显示主页按钮”并设定网址。

8

2026.01.26

苹果官方查询网站 苹果手机正品激活查询入口
苹果官方查询网站 苹果手机正品激活查询入口

苹果官方查询网站主要通过 checkcoverage.apple.com/cn/zh/ 进行,可用于查询序列号(SN)对应的保修状态、激活日期及技术支持服务。此外,查找丢失设备请使用 iCloud.com/find,购买信息与物流可访问 Apple (中国大陆) 订单状态页面。

31

2026.01.26

npd人格什么意思 npd人格有什么特征
npd人格什么意思 npd人格有什么特征

NPD(Narcissistic Personality Disorder)即自恋型人格障碍,是一种心理健康问题,特点是极度夸大自我重要性、需要过度赞美与关注,同时极度缺乏共情能力,背后常掩藏着低自尊和不安全感,影响人际关系、工作和生活,通常在青少年时期开始显现,需由专业人士诊断。

3

2026.01.26

windows安全中心怎么关闭 windows安全中心怎么执行操作
windows安全中心怎么关闭 windows安全中心怎么执行操作

关闭Windows安全中心(Windows Defender)可通过系统设置暂时关闭,或使用组策略/注册表永久关闭。最简单的方法是:进入设置 > 隐私和安全性 > Windows安全中心 > 病毒和威胁防护 > 管理设置,将实时保护等选项关闭。

5

2026.01.26

2026年春运抢票攻略大全 春运抢票攻略教你三招手【技巧】
2026年春运抢票攻略大全 春运抢票攻略教你三招手【技巧】

铁路12306提供起售时间查询、起售提醒、购票预填、候补购票及误购限时免费退票五项服务,并强调官方渠道唯一性与信息安全。

35

2026.01.26

个人所得税税率表2026 个人所得税率最新税率表
个人所得税税率表2026 个人所得税率最新税率表

以工资薪金所得为例,应纳税额 = 应纳税所得额 × 税率 - 速算扣除数。应纳税所得额 = 月度收入 - 5000 元 - 专项扣除 - 专项附加扣除 - 依法确定的其他扣除。假设某员工月工资 10000 元,专项扣除 1000 元,专项附加扣除 2000 元,当月应纳税所得额为 10000 - 5000 - 1000 - 2000 = 2000 元,对应税率为 3%,速算扣除数为 0,则当月应纳税额为 2000×3% = 60 元。

12

2026.01.26

oppo云服务官网登录入口 oppo云服务登录手机版
oppo云服务官网登录入口 oppo云服务登录手机版

oppo云服务https://cloud.oppo.com/可以在云端安全存储您的照片、视频、联系人、便签等重要数据。当您的手机数据意外丢失或者需要更换手机时,可以随时将这些存储在云端的数据快速恢复到手机中。

40

2026.01.26

抖币充值官方网站 抖币性价比充值链接地址
抖币充值官方网站 抖币性价比充值链接地址

网页端充值步骤:打开浏览器,输入https://www.douyin.com,登录账号;点击右上角头像,选择“钱包”;进入“充值中心”,操作和APP端一致。注意:切勿通过第三方链接、二维码充值,谨防受骗

7

2026.01.26

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PHP课程
PHP课程

共137课时 | 9.5万人学习

JavaScript ES5基础线上课程教学
JavaScript ES5基础线上课程教学

共6课时 | 11.2万人学习

PHP新手语法线上课程教学
PHP新手语法线上课程教学

共13课时 | 0.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号