我想弄清楚应该考虑哪些更好的表现:
我有一堆包含大量页面数据的对象。 一些对象可以拥有的数据示例:
该对象特定于每种类型的内容。我有一个定义渲染函数的接口。每个对象以不同方式实现此功能。
示例:
class PhpFragment extends FragmentBase {
public function render() {
//... render output for this type of data
}
}
我目前正在使用包含变量的父对象,这些变量可以包含上述类型的多个对象。该对象看起来像这样:
class pageData {
protected $CSS;
protected $PHP;
protected $JS;
protected $Meta;
protected etc...
public function getCSS() {
return $this->CSS;
}
public function getPHP() {
return $this->PHP;
}
public function getJS() {
return $this->JS;
}
}
每当我加载页面时,我会遍历模板并呈现与模板中的标记匹配的每个对象的数据。
例如:如果模板有一行需要CSS,我会调用pageData的getCSS函数,它返回一个对象数组。我将这些对象称为渲染函数并在页面中添加数据。
我想摆脱pageData对象中的这些固定变量,以便能够尽可能动态地使用我的设计。我希望pageData对象消失,只有一个不同的片段对象数组。
要实现这一点,我需要用聪明的东西替换pageData中的get-functions
吗?
我的首要任务是性能,所以我想我会查看所有对象一次以获取所有不同的类型,并将所有类型作为键放入数组中,然后数组的值将是包含与该类型匹配的对象的正确键的子数组。
在我开始完全改变设计之前,我想知道的是这个更快吗?
答案 0 :(得分:1)
我不知道这是否是提出这个问题的正确位置(它更像是一个代码审查问题IMO)。无论如何,如果我是你,我会考虑一些想法:
对象是功能单元,或表示一组特定值的实体。 DTO(与您的pageData类一样)服务于一个purpouse:分组,并表示属于一起的一组值。类具有类型(类型提示)和接口的事实使得代码库可测试,更易于理解,维护,调试和记录。
乍一看,简单的DTO与简单的数组并没有太大区别,是的,对象的 边际 性能成本。
您需要问的问题是,您是否希望以每个请求削减1或2毫秒的时间,代价是:增加开发时间,减少可测试性,更容易出错,并且难以维护代码。我认为仅仅因为这个原因,DTO比数组更有意义
如果您想要一个尽可能动态的对象,那么PHP可以让您可以动态地向实例添加属性:
Class Foo{}
$x = new Foo;
$x->bar = 'new property';
echo $x->bar;//echoes new property
所以从本质上讲,对象和数组一样灵活。但是,事先未声明的属性(再次 略微 )比预先声明的属性要慢。
当类定义声明3个属性时,这些属性存储在哈希表中。访问实例的成员时,将首先检查此哈希表。在内部,这些哈希表查找是O(1),如果没有声明属性,则任何"动态" 属性存储在第二个哈希表中。这个备用HT上的查找是 O(n)。并不可怕,但比他们需要的还要糟糕。
除了动态属性性能较差外,它们也总是公开的,所以你无法控制它们的值(它们可以在其他地方重新分配),它们当然容易受到人为错误(错字和#39; S):
$x = new Foo;
$x->foo = 'Set the value of foo';
echo $x->fo;//typo...
你现在所使用的方法
class User
{
protected $email;
public function setEmail($email)
{
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new \InvalidArgumentException('Invalid email');
}
$this->email = $email;
return $this;
}
}
这样的setter不仅允许我控制/检查设置属性的时间和地点,还可以验证某人试图分配给它的数据。您可以验证数据。您可以确保,无论如何,如果您收到User
的实例,电子邮件将为空或有效的电子邮件地址。
对象比数组更有意义的原因还有很多,但至少对我来说,这些对于2ms / req性能增益的好处超过了。
如果您追求的只是性能,那么您可能希望研究一下优于PHP的语言。不要误会我的意思:老实说,我喜欢PHP,但这只是一个事实,例如,Go可以做同样的事情,但速度更快。
阵列本质上是标量值。将数组传递给函数,并且函数内对该数组所做的任何更改都不会更改传递给该方法的数组。对象通过引用传递(排序)。这就是说:对象通过标识符传递
假设您有一个Foo
的实例。 Zend引擎将为该实例分配唯一的ID(例如123)。在内部调用函数并传递该实例时,您将该对象的标识符传递给方法而不是对象本身。
这有几个含义:当更改实例的状态时,PHP不必复制对象:它只使用ID来获取zval
(PHP的内部表示)变量),并在同一块内存上运行。最终结果是:你传递一个简单的值(一个int),无论对象发生什么,无论它发生在哪里,状态都是共享的。
数组是不同的:传递数组是(排序)传递该值的副本。实际上,PHP足够聪明地传递对现有数组的引用,但是一旦开始重新分配值,PHP就会拥有来创建副本。这是写时复制机制。简而言之,这个想法是:不要创建不必要的值副本,除非你必须:
function foo(array $data)
{
$x = $data[0];//read, no copy of argument is required
$data[1] = $x * $data[3];//now, we're altering the argument, a copy is created
}
$data = [1, 2, 3, 4];
foo($data);//passes reference
根据如何使用传递给函数的数组或对象,可能会比另一个执行得更好。总的来说:传递一个你只会用来读取值的数组很可能胜过传递一个对象。但是,如果您开始对数组/对象进行操作,则对象可能会胜过阵列...
是的,数组通常比对象快。但是他们不太安全,几乎不可能进行测试,更难维持非交际(public function doStuff(array $data)
并不像public function doStuff(User $data)
那样告诉我。
由于写时复制和实例传递给函数的方式,不可能说绝对确定哪个更快。它实际上归结为你所做的:数组相当小,你只读它的值,然后它可能比对象更快。
当您开始对数据进行操作时,它完全可能的对象可能会更快。
我不能在没有提及旧口头禅的情况下离开那里:
出于性能考虑,从对象切换到数组会产生微优化的味道。如果你事实上已经达到了这样的优势,那就是除了这些琐碎的东西之外没什么可以优化的,那么这个项目要么是一个小项目;或者你是第一个真正从事大型项目并且实际上完成的人。在所有其他情况下,你不应该浪费时间进行这种优化。
对于分析,然后进行优化更为重要的事情是:
只有当您经历过此列表以及更多内容时,您是否可以考虑考虑一些微优化。那当然,如果到那时你还没有遇到任何错误......