遵循协变/逆变原则,避免重复代码的最佳实践

遵循协变/逆变原则,避免重复代码的最佳实践

本文探讨了如何在PHP中遵循协变/逆变原则的同时,避免编写重复代码。通过移除`BaseBarClass::getFooBase()`方法的返回类型声明,或者在PHP8及以上版本中使用`: mixed`,可以解决子类`getFoo()`方法违反协变/逆变原则的问题,同时保持代码的简洁性和可维护性。

面向对象编程中,协变和逆变是类型系统中的重要概念,它们描述了方法参数和返回值类型在继承关系中的变化规则。在PHP中,严格遵循这些规则可以提高代码的健壮性和可维护性。然而,在某些情况下,为了避免代码重复,我们可能会遇到违反这些规则的情况。本文将探讨一种解决此类问题的实用方法。

考虑以下场景:我们有一个基类BaseFooClass和多个子类ChildFooClass1、ChildFooClass2等。还有一个基类BaseBarClass,它负责创建BaseFooClass及其子类的实例。BaseBarClass有一个受保护的方法getFooBase(),用于从远程数据源获取数据并创建BaseFooClass的实例。每个ChildBarClass都有一个getFoo()方法,该方法调用getFooBase()来获取特定类型的ChildFooClass实例。

以下是代码示例:

keys as $key => $value) {            $this->map[$key] = $keyValuePairs[$key] ?? null;        }    }}class ChildFooClass1 extends BaseFooClass {    protected $keys = ['foo1_a', 'foo1_b'];}class ChildFooClass2 extends BaseFooClass {    protected $keys = ['foo2_a', 'foo2_b', 'foo2_c'];}abstract class BaseBarClass {    protected $classIndex;    protected function getFooBase(int $dataIndex) //: ?BaseFooClass  Remove the return type or use : mixed    {        // GetRemoteData is assumed to be a global function, the important thing here is the retrieved data depends on classIndex and dataIndex        // If $classIndex is 1, the $keyValuePairs will look like ['foo1_a' => value1, 'foo1_b' => value2] where value1 and value2 depend on $dataIndex        $keyValuePairs = GetRemoteData($this->classIndex, $dataIndex);        if (checkDataIntegrity($keyValuePairs)) {            $class = "ChildFooClass" . $this->classIndex;            return new $class($keyValuePairs);        }        return null;    }}class ChildBarClass1 extends BaseBarClass {    protected $classIndex=1;    public function getFoo(int $dataIndex) : ?ChildFooClass1    {        return $this->getFooBase($dataIndex);    }}class ChildBarClass2 extends BaseBarClass {    protected $classIndex=2;    // input to getFoo in each BarClass can be different    public function getFoo($someInput) : ?ChildFooClass2    {        $dataIndex = $this->calculateDataIndex($someInput);        return $this->getFooBase($dataIndex);    }}// 假设的全局函数function GetRemoteData(int $classIndex, int $dataIndex): array {    // 模拟从远程数据源获取数据    $data = [];    if ($classIndex == 1) {        $data = ['foo1_a' => 'value1_' . $dataIndex, 'foo1_b' => 'value2_' . $dataIndex];    } elseif ($classIndex == 2) {        $data = ['foo2_a' => 'value3_' . $dataIndex, 'foo2_b' => 'value4_' . $dataIndex, 'foo2_c' => 'value5_' . $dataIndex];    }    return $data;}function checkDataIntegrity(array $data): bool {    // 模拟数据完整性检查    return !empty($data);}// 示例用法$childBar1 = new ChildBarClass1();$foo1 = $childBar1->getFoo(1);if ($foo1 instanceof ChildFooClass1) {    echo "ChildBarClass1::getFoo() 返回了 ChildFooClass1 实例n";} else {    echo "ChildBarClass1::getFoo() 没有返回 ChildFooClass1 实例n";}$childBar2 = new ChildBarClass2();$foo2 = $childBar2->getFoo('someInput');if ($foo2 instanceof ChildFooClass2) {    echo "ChildBarClass2::getFoo() 返回了 ChildFooClass2 实例n";} else {    echo "ChildBarClass2::getFoo() 没有返回 ChildFooClass2 实例n";}?>

最初的代码中,ChildBarClass1::getFoo()和ChildBarClass2::getFoo()方法违反了协变/逆变原则,因为它们试图返回更具体的类型(例如ChildFooClass1),而BaseBarClass::getFooBase()方法返回的是更通用的类型BaseFooClass。

解决方案:移除getFooBase()的返回类型声明

最简单的解决方案是从BaseBarClass::getFooBase()方法中移除返回类型声明。这样,该方法将不再强制返回特定类型,从而避免了协变/逆变冲突。由于getFooBase()是受保护的方法,不属于公共API,因此移除返回类型声明不会对代码的外部接口产生影响。

或者,在PHP8及以上版本中,可以使用: mixed作为getFooBase()的返回类型声明。mixed类型可以接受任何类型的值,因此也可以避免协变/逆变冲突。

修改后的代码如下:

abstract class BaseBarClass {    protected $classIndex;    protected function getFooBase(int $dataIndex) //: ?BaseFooClass  Remove the return type or use : mixed    {        // ...    }}

优点:

避免了代码重复:无需在每个ChildBarClass::getFoo()方法中重复调用GetRemoteData()和checkDataIntegrity()等逻辑。遵循协变/逆变原则:通过移除或放宽getFooBase()的返回类型声明,避免了类型冲突。保持类型安全:由于ChildBarClass::getFoo()方法仍然具有具体的返回类型声明,因此可以在编译时或运行时检测到类型错误。

注意事项:

确保GetRemoteData()函数返回的数据与ChildFooClass的构造函数兼容。如果GetRemoteData()函数返回的数据不完整或无效,checkDataIntegrity()函数应返回false,以避免创建无效的ChildFooClass实例。

总结:

通过移除或放宽基类方法的返回类型声明,可以在遵循协变/逆变原则的同时,避免编写重复代码。这种方法简单有效,可以提高代码的可维护性和可读性。在实际开发中,应根据具体情况选择合适的解决方案,并仔细考虑类型安全和代码可维护性之间的平衡。

以上就是遵循协变/逆变原则,避免重复代码的最佳实践的详细内容,更多请关注php中文网其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1329275.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月12日 15:29:47
下一篇 2025年12月12日 15:29:57

相关推荐

  • PHP str_pad 数字格式化与反向解析:从定长字符串恢复原始浮点数

    本教程详细介绍了在php中如何将浮点数转换为特定长度的零填充字符串,以满足api接口等固定格式要求。文章将首先展示转换过程,随后重点阐述如何高效、准确地将这些格式化后的字符串反向解析回原始的浮点数值,同时提供示例代码和关键注意事项,确保数据在转换与逆转换过程中的完整性与精度。 1. 引言:API数据…

    2025年12月13日
    000
  • php源码怎么设置密码_php源码设置访问密码与权限法【技巧】

    答案:可通过HTTP基本认证、Session会话控制、IP白名单及数据库验证四种方式实现PHP文件的访问保护。首先使用HTTP基本认证弹出登录框并校验用户名密码;其次通过Session记录登录状态,避免重复验证;再结合客户端IP地址判断是否在允许列表中,拒绝非法IP访问;最后可对接数据库动态验证用户…

    2025年12月13日
    000
  • HTML表单实现客户端邮件发送:mailto:协议详解与局限性分析

    本教程详细介绍了如何利用html表单的mailto:协议功能,在用户提交表单后,自动打开其默认邮件客户端并预填充邮件内容。文章将提供示例代码,并深入探讨mailto:协议的使用方法、可配置参数,以及作为客户端解决方案的固有局限性,帮助开发者理解其适用场景与替代方案。 1. mailto:协议简介:实…

    2025年12月13日
    000
  • Respect/Validation 进阶:基于条件动态添加验证规则

    本文深入探讨了如何使用 respect/validation 库基于特定条件动态扩展验证规则集。文章揭示了一个常见陷阱,即在验证链中过早调用终端操作(如 `validate()`)会导致后续规则添加失败。通过对比错误示例与正确实践,重点介绍了如何确保验证器对象始终保持可链式调用状态,并推荐使用 `k…

    2025年12月13日
    000
  • Nginx错误页面定制:实现外部重定向与内部通知机制

    本文详细阐述了如何在nginx中配置自定义错误页面,使其在触发特定http错误(如404、500)时,能够自动重定向到指定的外部网站,并同时在服务器端触发一个php脚本来执行诸如邮件通知等额外操作。教程涵盖了nginx的`error_page`指令配置、php重定向与通知脚本的编写,以及使用`cur…

    2025年12月13日
    000
  • PHP 4 函数引用参数兼容性指南:解决默认值语法错误

    本教程旨在解决 php 4 中因函数引用参数设置默认值而导致的语法错误。php 4 不支持为引用参数指定默认值,这与新版本 php 的行为不同。文章将详细解释这一限制,并提供针对 php 4 环境的正确函数定义和调用方法,确保代码兼容性与功能正常。 引言 在 PHP 开发中,不同版本之间的语法和特性…

    2025年12月13日
    000
  • PHP动态生成Bootstrap表格样式丢失问题解析与优化实践

    在使用php从mysql数据库动态生成bootstrap表格时,常见的问题是表格样式(如`.table-striped`)无法正常显示。这通常是由于html结构不正确,特别是重复创建` `标签导致的。本文将详细解释此问题的原因,并提供一个优化的php代码示例,确保生成的表格能够正确应用bootstr…

    2025年12月13日
    000
  • DEFLATE数据流手动解析指南:位序陷阱与RFC1951规范解读

    本教程深入探讨DEFLATE压缩数据流的手动解析过程,重点揭示了RFC1951规范中关于字节内位序(Least Significant Bit优先)这一常见陷阱。通过具体示例,文章演示了如何正确解读DEFLATE数据块的头部信息,如BFINAL和BTYPE,并强调了严格遵循官方规范的重要性,以避免解…

    2025年12月13日
    000
  • php源码怎么添加加密_用加密算法给PHP源码加密封装教程【技巧】

    一、使用Zend Guard可将PHP源码编译为加密字节码,需安装工具、导入项目、启用混淆与加密并配置服务器环境后生成加密文件。 如果您希望保护PHP源码不被轻易查看或修改,可以通过加密算法对源码进行封装处理。以下是实现PHP源码加密的几种常用方法: 一、使用Zend Guard进行源码加密 Zen…

    2025年12月13日
    000
  • 在 Laravel 中处理动态日期范围参数并传递给打印功能

    本文旨在解决 laravel 应用中,从表单获取动态日期范围(start_date 和 end_date)并将其正确传递给独立打印路由的问题。通过将“打印”按钮改造为表单提交类型,并在控制器中判断是哪个提交按钮被点击,我们能够确保动态日期参数在用户点击打印时被正确捕获和处理,避免了直接使用链接导致参…

    2025年12月13日
    000
  • php后台源码怎么安装_php后台源码安装配置与访问法【教程】

    首先确保服务器环境正确配置,安装XAMPP等集成环境并启动Apache和MySQL服务;将PHP源码放入htdocs目录,在phpMyAdmin中创建数据库并导入SQL文件;修改config.php等配置文件中的数据库连接信息为本地参数;查看文档获取默认管理员账号或通过数据库修改密码;最后访问htt…

    2025年12月13日
    000
  • PHP XPath 处理非间断空格(NBSP)的策略与实践

    在php中使用xpath进行html解析时,处理非间断空格(nbsp,如` `或` `)是一个常见挑战。本文将深入探讨html源文件中的字符引用与dom树中实际unicode字符的区别,并提供在xpath表达式中正确匹配和提取包含u+00a0字符文本的方法。我们将演示如何使用php的unicode转…

    2025年12月13日
    000
  • PHP中处理嵌套数组:JSON解码与数据访问实战指南

    本教程详细讲解如何在php中高效地处理和访问由json字符串解码而来的嵌套数组数据。我们将涵盖理解多层数组结构、通过键名直接访问特定值、处理索引数组以及安全访问的最佳实践,旨在帮助开发者准确提取所需信息。 引言 在现代Web开发中,PHP经常需要与各种数据格式交互,其中JSON因其轻量级和易读性而广…

    2025年12月13日
    000
  • 在JavaScript中监听Laravel Livewire消息生命周期钩子

    本文深入探讨了如何在javascript中利用laravel livewire提供的全局生命周期钩子。通过注册`livewire.hook`,开发者可以在livewire组件与后端通信的不同阶段(如消息发送、接收、处理等)介入。文章详细介绍了如何通过检查消息负载(`message.updateque…

    2025年12月13日
    000
  • PHP中多维数组多条件查找特定数据的高效方法

    本教程详细介绍了如何在PHP多维数组中根据多个条件查找特定数据。针对`array_search`无法满足多列搜索需求的问题,文章重点讲解了如何利用`array_filter`结合匿名函数实现灵活且高效的复合条件过滤,并提供了具体的代码示例和结果判断方法。 引言:多维数组多条件查找的挑战 在PHP开发…

    2025年12月13日
    000
  • Composer path 仓库:高效管理本地依赖与解决Git访问限制

    本教程旨在解决symfony项目中因git访问限制导致无法直接管理`vendor`目录内依赖的问题。通过利用composer的`path`仓库类型,开发者可以将特定依赖从传统的`vendor`目录中移出至项目内的自定义路径,并使composer正确识别和加载这些本地包。文章将详细指导如何配置`com…

    2025年12月13日
    000
  • 使用.htaccess隐藏URL目录路径:构建清晰的链接别名教程

    本教程详细讲解如何利用apache的`mod_rewrite`模块和`.htaccess`文件,为网站url创建简洁的别名,从而隐藏实际的目录路径。通过修改前端链接和配置服务器端重写规则,实现url的视觉美化和结构优化,避免直接暴露文件路径,提升用户体验和安全性。 在Web开发中,尤其是在使用内容管…

    2025年12月13日
    000
  • php 怎么解密_用PHP分析加密特征快速解密文件方法【技巧】

    发现文件被PHP加密后,可通过分析代码中的加密函数特征定位解密方法:首先查找base64_decode、gzinflate、openssl_decrypt等函数判断加密类型;若为多层Base64编码,可编写脚本逐层解码直至还原源码;对于eval(gzinflate(base64_decode()))…

    2025年12月13日
    000
  • 在SQL查询中安全地结合多重条件与会话数据过滤

    在SQL查询中,通过使用逻辑运算符(如`AND`)可以轻松地组合多个`WHERE`子句条件,从而实现更精细的数据过滤。当需要根据用户会话数据(如登录用户名)来限制结果集时,可以将用户会话变量作为条件之一加入`WHERE`子句。然而,直接将用户输入或会话数据拼接到SQL查询字符串中存在严重的安全风险,…

    2025年12月13日
    000
  • PHP array_search() 的严格模式:避免类型转换导致的匹配错误

    当php的array_search()函数在处理包含点号的数字字符串时,默认的松散比较可能导致意外的匹配结果。本文将深入探讨array_search()的默认行为(类型转换)如何影响查找准确性,并详细介绍如何通过其第三个参数strict启用严格比较(===),从而确保精确匹配,有效解决因类型差异或相…

    2025年12月13日
    000

发表回复

登录后才能评论
关注微信