所以我终于开始玩特性并且它们非常方便,我一直有的问题是我想要一些特性来为我的数据对象添加功能。 本身这很简单,只是在使用我的基础数据对象
中定义的方法时这样做abstract class Base_Object {
protected function _addToUpdate($field, $value) {
...
}
...
}
trait Extended_Object {
public function doSomeStuff() {
...
$this->_addToUpdate($someVar, $someOtherVar);
}
...
}
class User extends Base_Object {
use Extended_Object;
...
}
我的问题是,如果我的团队中的其他人决定在不扩展Extended_Object
的对象上使用Base_Object
特征。我曾考虑过检查_addToUpdate
方法,但理想情况下我希望在创建实例时显示错误。
我提出了一个有效的解决方案,但让我觉得有点脏,而且远非理想
abstract class Base_Object {
protected function _addToUpdate($field, $value) {
...
}
...
}
trait Extended_Object {
abstract protected function _addToUpdate($field, $value);
public function doSomeStuff() {
...
$this->_addToUpdate($someVar, $someOtherVar);
}
...
}
class User extends Base_Object {
use Extended_Object;
...
}
通过向Extended_Object
i添加一个抽象方法,如果Base_Object
中我需要的方法不存在,那么至少可以确定会显示错误但是我不保证该方法在问题实际上会做我想做的事。
理想情况下,当使用Extended_Object
特征实例化对象时,我希望能够运行类似下面代码的内容
if (!($this instanceof Base_Object)) {
throw new Inheritance_Exception("Base_Object");
}
我希望有人找到了这样做的方法,或者至少是比我更好的解决方案。
注意:我知道我可以使用构造函数执行此操作,但这只有在使用单个特征时才可行,如果我在以后决定创建其他几个对象扩展特征的话很快变得非常混乱
编辑:我确实认识到这个特质是真的设计用于我想要做的事情,但它们至少在一定程度上允许我解决单一继承的问题而且我知道我不是唯一的开发计划以这种方式使用它们
答案 0 :(得分:2)
我认为您使用abstract function
的解决方案正是该设施的目标。我同意你的直觉:
我无法保证所讨论的方法实际上会按我的意愿行事
但是,这与您在使用interface
时所做的假设相同:唯一被断言的是存在具有正确签名的方法(在PHP中,该方法主要取决于其名称和参数数量) )。在给定这些参数时,无法知道函数实际上是以特别有用的方式运行。
对于trait
,抽象方法与interface
实际上是同一种合同:“为了use
此trait
,您必须提供这些先决条件“。
我还要注意trait
的常见描述是“自动复制和粘贴”,因此通常应将其视为与对象层次结构分开。从技术上讲,它代表“水平代码重用”,而不是“多重继承”。例如,您可以定义允许 trait
到class
和implement
的{{1}},但对于外部代码,它是{ {1}}这很重要,而不是interface
。
答案 1 :(得分:2)
嗯(恕我直言)问题在于你使用特征的方式存在技术anti-pattern。
首先,什么是特质,为什么要使用它们 引用其中一个comments in php.net
了解特征是什么以及如何使用它们的最佳方法是 看看它们本质上是什么:语言辅助副本 并粘贴。
如果您可以将代码从一个类复制并粘贴到另一个类(和 我们都做到了这一点,即使我们尽量不这样做,因为它的代码 重复)然后你有一个特质的候选人。
现在,即使该特性允许您使用正在使用它们的类的成员,这并不意味着您应该这样做。回到SRP,这是一个真正的违规行为。
PHP允许你echo $_POST['XSS data']
,但这并不意味着你应该这样做。因此,无论您希望如何严格使用特质,您实际上在做的是您正在介绍问题,而您正试图解决它
所以要回答你的问题,只需重新设计你的代码,以便你不要使用类方法和成员,假设每个使用特征的类都应该有这些方法和成员。
答案 2 :(得分:0)
我遇到了类似的问题,但是有多个子类继承自基类。
在这一点上将特征抽象声明放入某种包含文件和特征本身是有意义的,例如:
abstract class Base_Object {
use BaseObjectTrait;
...
}
trait BaseObjectTrait {
protected function _addToUpdate($field, $value) {
...
}
}
trait BaseObjectTraitPrototypes {
abstract protected function _addToUpdate($field, $value);
}
trait Extended_Object {
use BaseObjectTraitPrototypes;
public function doSomeStuff() {
...
$this->_addToUpdate($someVar, $someOtherVar);
}
...
}
class User extends Base_Object {
use Extended_Object;
...
}
有谁能想到比这更优雅的解决方案?我从C编程的意义上非常宽松地使用术语'原型'。 'Header'文件同样来自C编程上下文,但仍然不理想。任何人都可以想到问题域的更简洁的概念,也许是php-fig可以接受的?
答案 3 :(得分:0)
我的问题是,如果我的团队中的其他人决定对未扩展
Extended_Object
的对象使用Base_Object
特征。
理想情况下,当使用Extended_Object特性实例化对象时,我希望能够运行类似下面的代码
if (!($this instanceof Base_Object)) { throw new Inheritance_Exception("Base_Object"); }
也许我错过了一些东西,但是如果这个特性只应用于Base_Object
的实例,那么听起来这个“特征”应该是Base_Object
的子类而不是一个特质?
User
类然后扩展这个Extended_Object
(一个子类),而不是use
Extended_Object
(一个特征)。< / p>
来自:http://php.net/manual/en/language.oop5.traits.php
Trait旨在通过使开发人员能够在生活在不同类层次结构中的几个独立类中自由地重用方法集来来减少单一继承的一些限制。