
本文探讨了如何在PHP中遵循协变/逆变原则的同时,避免编写重复代码。通过移除`BaseBarClass::getFooBase()`方法的返回类型声明,或者在PHP8及以上版本中使用`: mixed`,可以解决子类`getFoo()`方法违反协变/逆变原则的问题,同时保持代码的简洁性和可维护性。
在面向对象编程中,协变和逆变是类型系统中的重要概念,它们描述了方法参数和返回值类型在继承关系中的变化规则。在PHP中,严格遵循这些规则可以提高代码的健壮性和可维护性。然而,在某些情况下,为了避免代码重复,我们可能会遇到违反这些规则的情况。本文将探讨一种解决此类问题的实用方法。
考虑以下场景:我们有一个基类BaseFooClass和多个子类ChildFooClass1、ChildFooClass2等。还有一个基类BaseBarClass,它负责创建BaseFooClass及其子类的实例。BaseBarClass有一个受保护的方法getFooBase(),用于从远程数据源获取数据并创建BaseFooClass的实例。每个ChildBarClass都有一个getFoo()方法,该方法调用getFooBase()来获取特定类型的ChildFooClass实例。
以下是代码示例:
<?php
class BaseFooClass {
protected $keys = [];
private $map = [];
public function __construct($keyValuePairs) {
foreach($this->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,因此移除返回类型声明不会对代码的外部接口产生影响。
芝士饼
芝士饼是一个一站式AI原生应用开发平台,简单几步即可完成应用的创建与发布。
84
查看详情
或者,在PHP8及以上版本中,可以使用: mixed作为getFooBase()的返回类型声明。mixed类型可以接受任何类型的值,因此也可以避免协变/逆变冲突。
修改后的代码如下:
abstract class BaseBarClass {
protected $classIndex;
protected function getFooBase(int $dataIndex) //: ?BaseFooClass Remove the return type or use : mixed
{
// ...
}
}优点:
注意事项:
总结:
通过移除或放宽基类方法的返回类型声明,可以在遵循协变/逆变原则的同时,避免编写重复代码。这种方法简单有效,可以提高代码的可维护性和可读性。在实际开发中,应根据具体情况选择合适的解决方案,并仔细考虑类型安全和代码可维护性之间的平衡。
以上就是遵循协变/逆变原则,避免重复代码的最佳实践的详细内容,更多请关注php中文网其它相关文章!
# 是一个
# 成都营销策划推广方法
# seo技术常识
# 网店微博推广营销
# 新郑网站建设服务
# 短信推广网站制作
# 博创网站建设团队
# 黄岩seo排名优化流程
# 飞机推广营销方案
# 黑帽seo 蜘蛛劫持
# 精准引流网站推广方式
# 都有
# php
# 的是
# 有一个
# 怎么看
# 芝士
# 面向对象
# 移除
# 子类
# 逆变
# 面向对象编程
# ai
# php8
相关栏目:
【
Google疑问12 】
【
Facebook疑问10 】
【
优化推广96088 】
【
技术知识133117 】
【
IDC资讯59369 】
【
网络运营7196 】
【
IT资讯61894 】
相关推荐:
C++ switch case字符串_C++如何实现字符串switch匹配
消除网页顶部意外空白线:CSS布局常见问题与解决方案
批改网网页版登录 批改网电脑版学生登录入口
漫蛙漫画官方版直通入口 2025漫蛙漫画免注册访问说明
Excel如何制作月度销售统计图_Excel动态图表制作与控件应用
抖音网页版地址直接进入_抖音网页版在线观看入口
j*a中赋值运算符是什么?
稻壳阅读器官方直达网址链接 稻壳阅读器文档阅读平台主页资源入口
咸鱼怎么设置仅粉丝可见的动态_咸鱼动态粉丝可见设置方法
Composer reinstall命令重装损坏的包
冬季去哪个城市旅游更有可能观测到极光
折叠屏手机充不进电是什么问题? 特殊结构带来的维修难点
《edge浏览器》关闭翻译功能方法
《红果免费短剧》下载观看方法
Win10共享文件夹设置方法 Win10局域网文件共享全攻略【教程】
免费占卜在线神算_免费占卜手机神算
苹果如何下载nanobanana
Microsoft Edge网页字体太淡看不清怎么办_Microsoft Edge字体渲染优化技巧
Linux如何优化系统启动流程_Linux启动项优化方案
Linux如何开发轻量级数据服务模块_Linux服务化设计
火狐浏览器如何刷新修复浏览器 火狐浏览器“重置Firefox”功能详解
《tt语音》超级玩家开通方法
铁路12306官网入口 铁路12306中国铁路官网登录首页
Yandex浏览器官方入口_Yandex搜索引擎中文版
如何用mysql开发用户注册登录功能_mysql用户注册登录数据库设计
Teambition网盘如何共享文件
Golang中的rune与byte类型区别是什么_Golang字符与字节处理详解
win11如何开启单声道音频 Win11为听障用户合并左右声道【辅助】
Flexbox布局实践:实现底部页脚与顶部粘性导航条的完美结合
宝妈做视频号该写什么标签话题?宝妈关注的话题有哪些?
鲨鱼剧场app金币获取方法
从HTML表单获取逗号分隔值并转换为NumPy数组进行预测
J*aScript深度克隆:实现高效、健壮与安全的复杂对象复制
包子漫画在线观看入口 包子漫画网正版全集链接
POKI小游戏在线免费入口链接 POKI小游戏无下载秒玩玩
《星露谷物语》克林特好感度事件介绍
windows server2019显卡驱动怎么安装_winserver2019显卡驱动安装与远程桌面优化
向往的生活小游戏启动处_向往的生活小游戏立即启动
byrutor直接访问入口 byrutor官方游戏库
MySQL多重关联查询:利用别名高效获取同一表的多个关联字段
顺丰快递怎么查物流_顺丰快递物流信息实时查询操作指南
139邮箱登录入口官网 139邮箱登录入口官网网址
怎样让Windows 11的开始菜单恢复经典样式_Open-Shell工具使用指南【怀旧】
Win10锁屏时间怎么设置 Win10调整自动锁屏时间方法
sublime text 4如何安装_最新版sublime下载与汉化教程
iQOO手机信号差网络不稳定怎么办 信号问题原因排查与增强设置【攻略】
《米姆米姆哈》米姆获取及技能攻略
手机坏了微信聊天记录怎么导出来 新手机恢复聊天记录技巧
J*a中逻辑运算符如何使用_逻辑与或非的基础用法讲解
Python自动化抓取GBGB赛狗比赛结果:日期范围与赛道筛选教程
2025-10-31
运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。