我试图在PHP中构建对象标识,以便当我拥有一个对象集合时,每个对象都可以有一个字符串作为标识符,然后所有这些标识符都被组合成一个唯一的md5来表示“集合的标识”。
为什么?这样,当不需要代码时,我可以选择跳过代码的重新执行:
interface SomeTestInterface
{
public function testFunction();
}
abstract class Identifiable
{
public function __toString()
{
$identity_shards = array_merge( get_object_vars( $this ), class_implements( $this ) );
$identity_string = '';
foreach( $identity_shards as $identity_shard_key => $identity_shard_value ) {
$identity_string .= (string) $identity_shard_key . (string) json_encode( $identity_shard_value );
}
return md5( get_class( $this ) . $identity_string );
}
}
class SomeBaseClass extends Identifiable implements SomeTestInterface
{
public function __construct( $number )
{
$this->number = $number;
$this->thing = 'a';
$this->other_thing = ['a','b','c',1,2,3,];
}
public function testFunction()
{
return 'a';
}
}
这在以下几个方面是可测试的:
for( $i = 1; $i < 10000; $i++ ) {
$class = new SomeBaseClass( $i );
(string) $class;
}
对于我来说,PHP7.3和WordPress,这需要100 me来执行。
我的微观决定:
json_encode
on $identity_shard_value
,因为您不能将数组强制转换为字符串,例如。json_encode
在我的经验中速度很快,并且知道如何处理这一切。abstract class
,因为json_encode
无法访问作用域类,因此不能对它找不到的类进行编码,所以我必须能够访问$this
,尽管这很奇怪,因为即使在抽象类中,我仍然不能对它进行编码,但我应该能够。我主要关心的是,我是否真的需要所有这些项目来构建我的对象标识,或者是否有另一种更快的方法。在0.1ms中有10000个物体,虽然它本身很好,但不一定是缩放的。
本质上,在框架的一个模块拥有的集合中实现Identifiable
的每个对象都有一个标识,然后我将其组合成一个最终的“集合标识”,以便稍后进行如下检查:
$collection_identity = getCollectionIdentity( $collection ); //MD5 computed from the identity of all these objects
if( $collection_identity != getCollectionIdentityByName( 'some_collection' ) {
setCollectionIdentity( 'some_collection', $collection_identity );
//re-execute code
} else {
retrieveDataFromStorage();
}
如您所见,它检查对象/集合是否有更改,如果是,则重新执行所有其他代码,但如果没有,它只是检索过去生成的“其他代码”,因此,这是一种使用持久存储来跳过重代码执行的方法。
发布于 2019-06-01 23:05:23
我认为这个代码很好,不能太快。但是..。
MD5散列很可能是唯一的,它毕竟有16^32 (3.4e38)值,但是一旦出现问题,两个不同的对象将具有相同的标识,特别是如果您经常使用它。这可能会在您的软件中引起非常罕见的、随机的错误。几乎不可能追踪到的Bugs。
我不认为__toString()
魔术方法是为了达到您现在使用它的目的。我学到了这一点:“你应该总是为了它的目的而使用它。”__toString()
的目的是为您提供对象的可读表示。通过现在将其用于标识对象,您将失去以后将其用于其预期目的的能力。
您还依赖于get_object_vars()
的一个未记录的属性,即它总是以相同的顺序返回变量。会吗?我不知道。很可能会,但不一定。随着PHP版本的改变,这种情况也可能发生变化,如果发生这种情况,您会感到非常头痛。您可以使用ksort()
来确保订单总是相同的,但是这会使事情慢很多。
我还在不同的地方以及手册中的注释中看到,get_object_vars()
不返回静态变量。这是有意义的,因为一个类的所有对象都共享这些变量的相同值,但是要记住这一点。
在一些散列集合中,身份散列的存储和检查可能是整个想法中最慢的部分。
然后,my最终问题删除以下代码:
正确编写的代码将知道其对象的标识,或者至少有一个100%可靠的方法来检查它。您的代码应该以这样一种方式编写,即它已经将对象复制降到最低。这段代码的结果似乎是无法编写好而高效的代码(对不起,我在这里试图说明一点)。
例如,许多对象可能已经有一个标识它们的简单ID整数。例如,基于数据库行的模型类很可能具有这样的ID。如果需要,大多数其他类都可以使用类似的方法来标识自己。一旦将这样的ID与类名组合,就应该有一个100%可靠的标识符。
如果您确实需要一种识别各种对象的方法,可以简单地向它们添加一个identity()
方法。就像这样:
id = $id;
}
public function identity() {
return get_class() . ":" . $this->id;
}
}
$myObject = new MyClass(999);
echo $myObject->identity();
?>
这将返回:
MyClass:999
我同意这是一个非常基本的例子,但是对于任何一个类都应该可以做类似的事情。
通过为每个类编写这样一个特定的标识符方法,您可以优化它,这意味着它将更快,并且在任何情况下都可以使它100%可靠。调试也要容易得多,因为您可以看到和阅读正在发生的事情。这里不能藏在神秘的哈希后面。
结论:尽管我反对,但我认为您的代码看起来还不错。然而,我确实怀疑,这种做法最终是否会造成更多的麻烦,这是值得的。
注意:评论中有更多的讨论。最后,酷意面写了对这个问题的答复。
发布于 2019-06-02 20:47:19
我最初的问题是:速度和可读性,结果是,使用__toString
时遇到了问题。
首先,我最初的方法有一个问题:
如果我的SomeBaseClass
在某个时候需要重写它从Identifiable
继承的__toString
函数,那么我通过同一个SomeBaseClass
集合的最初功能就会失败,基本上它不再是Identifiable
了,因为这个功能是重写的。
我所做的,基于@KIKO的建议--到目前为止还没有发现bugs -创建了一个继承IdentifiableInterface
的接口,如下所示:
/**
* Interfaces that contains a single method in regards to an object's unique & persistent identity.
*
* @internal Mostly used by objects that are inside containers (as such, they're of the same intent, but differ) where comparison between these objects is needed.
*/
interface IdentifiableInterface
{
/**
* Retrieves the object's identifier.
*
* @internal Do note that this is the object identifier which is meant for identification in the broader scope. You might have a, say, "suggestion identifier" which is specific to the Suggestions Module.
*
* @return string
*/
public function getUniqueObjectIdentifier();
}
然后,在我的作用域(子模块)功能中,我使用表示我集合中的每个对象的SuggestionInterface
:
use Sprout\SproutIdentity\Interfaces\IdentifiableInterface;
interface SuggestionInterface extends IdentifiableInterface
{
}
这基本上使任何SuggestionInterface
对象也实现了getUniqueObjectIdentifier
,从本质上说,它告诉用户这个对象和类似对象都是在集合中,并且作为集合成员的状态很重要。
虽然我在这里拍拍自己,但我认为这是一种优雅的方式,通过注释和继承(当涉及到接口时,我有点同意这一点),您就可以了解您的系统了。
最后,我是如何从一个助手函数中计算SuggestionInterface
s标识的集合:
public static function computeArrayIndetity( $array )
{
$identity = '';
foreach( $array as $array_item ) {
$identity .= $array_item->getUniqueObjectIdentifier();
}
return md5( $identity );
}
结果是什么呢?
10000 iterations:
new approach - 0.0005820830663045
old approach - 0.0020218133926392
100000 iterations:
new approach - 0.005621083577474
old approach - 0.019490500291189
当然,这就像把苹果和梨进行比较,当你看到其中一个做什么,另一个做什么,就像“嗯”,但我只是想展示一下,我是如何从一个复杂的、缓慢的方法中走出来的,这个方法本来是很好的,但结果却更优雅、更简单、更快。
虽然速度显然更好,但是新方法创建的副产品并不存在,并且不会因为__toString
而出现错误。
它确实要求开发人员设置该函数,因此它违背了自动化的目的,但我将创建一个帮助函数,他们可以使用该函数立即生成名称,而无需考虑它。
https://codereview.stackexchange.com/questions/221473
复制相似问题