在PHP面向对象编程中,我应该何时使用stdClass类,何时使用数组?

73
在我工作中一个大的重构期间,我希望引入stdClass作为从函数中返回数据的一种方式,并且正在努力寻找非主观的论据来支持我的决定。
在什么情况下使用stdClass比使用数组更好?
使用stdClass有哪些好处,而不是使用数组?
有些人可能会说函数必须尽可能小而具体,以便能够返回一个单一的值。我的决定使用stdClass是暂时的,因为我希望在长期运行中为每个过程找到正确的值对象。

它并没有回答我的问题。他说“真正的”类会是最好的方法,这点(我认为)我们都同意。 但我仍然想知道,在什么情况下使用stdClass返回值比数组更好。 - JonG
你可能需要澄清关于Value Objects的概念。在DDD中,一个Value Object是不可变的,并且其目的与Java中的VO非常不同。 - Gordon
@Gordon 我的意思是,函数返回一个“类似VO”的指南。将VO作为(最好是)小对象,只有他的属性很重要,而不是他的身份。关于你的链接,你是想知道对我来说(我的问题),关于“VO应该是不可变的还是可变的”这个讨论是否很重要吗?顺便说一下,现在VO对于sun来说是TO(传输对象)。http://java.sun.com/blueprints/corej2eepatterns/Patterns/TransferObject.html - JonG
我还不确定你是否只是想解决PHP不支持多返回值的问题,或者还有其他需求。而且,可变性是否很重要?也许你正在寻找元组(就像Python中的元组)? - Gordon
@Gordon 谢谢您的关注。正如您所说,我想我的问题主要集中在寻找一种“好”的方式(在面向对象编程中),以将多个值作为函数调用的返回传递。您认为我应该编辑我的问题还是这些评论就可以了? - JonG
7个回答

63

通常的做法是:

  • 在返回一个已定义数据结构且有固定分支的情况下,使用对象:

     $person
       -> name = "John"
       -> surname = "Miller"
       -> address = "123 Fake St"
    
  • 当返回一个列表时,请使用数组:

  •   "John Miller"
      "Peter Miller"
      "Josh Swanson"
      "Harry Miller"
    
    使用对象数组来返回结构化信息列表:
      $person[0]
        -> name = "John"
        -> surname = "Miller"
        -> address = "123 Fake St"
    
      $person[1]
        -> name = "Peter"
        -> surname = "Miller"
        -> address = "345 High St"
    

    对象不适合保存数据列表,因为您总是需要一个关键字来引用它们。数组可以同时满足两个功能-保存任意列表和数据结构。

    因此,如果您想要的话,针对第一个和第三个示例,您可以使用关联数组代替对象。我认为这只是风格和偏好的问题。

    @Deceze提出了一些关于何时使用对象的好观点(验证、类型检查和未来方法)。


我非常喜欢/同意第1和第2个论点。 - JonG
1
我要求提供“非主观”的参数,但遗憾的是没有被给予。我将遵循您的回答(根据其他人的投票)因为它显示了我从现在开始将继续遵循的:数组用于列表;stdClass用于“多值”/属性。 - JonG
13
我强烈反对这个观点。如果你只有数据,即使是结构化的数据,那么你应该使用数组。对象是数据和相关行为的包装。将 stdClass 用作数组只是对 stdClass 的滥用。 - GordonM
4
同意GordonM的观点,使用stdClass来制造一个适当对象的幻象比起好处更多的是带来了伤害。几乎所有我看到使用stdClass的场景当中,都滥用了动态属性——这个类具有什么结构非常混乱,并且即使这个结构被遵循了,由于几乎每次使用都涉及实时填充属性,也很难搞清楚。如果你可能会错过属性或者不愿意创建一个真正的类,那么干脆使用一个数组也可以。 - srcspider
不是客观的答案。 - William Entriken
+1 给 @srcspider。我曾经看到过开发人员使用 stdClass,但他们只是以为代码是“面向对象”的,实际上并不是。在我看来,stdClass 唯一可能有用的地方就是在测试用例中。而且我不记得自己曾经这样使用过它。我认为应该从 PHP 语言中废除它。 - Marcelo

46
使用stdClass作为数组的替代品并不是很有用,因为它只增加了一个对象的开销,没有任何实际的好处。此外,你将错过许多有用的数组函数(例如array_intersect)。你至少应该创建自己的类来进行类型检查,或者添加方法来使对象值得使用。

1
我特别喜欢/同意你所说的关于“添加方法以使对象值得使用”的观点。 - JonG

18
我认为,只要你的唯一目的是从函数调用中返回多个任意数据类型,使用stdClass而不是数组没有任何合理的优势。
由于你不能在技术上原生地返回多个值,你必须使用一个可以容纳PHP中所有其他数据类型的容器。这可以是对象或数组。
function fn1() { return array(1,2); }
function fn2() { return array('one' => 1, 'two' => 2); }
function fn3() { return (object) array(1,2); }
function fn4() { return (object) array('one' => 1, 'two' => 2); }

以上所有的方法都可以使用。数组的速度稍微快一点,而且输入的工作量也少一些。与通用的stdClass相比,它还有一个明确的目的(stdClass有点含糊不清,不是吗)。这两者都只有隐式接口,所以你必须查看文档或函数体才能知道它们将包含什么。
如果你想不惜一切代价使用对象,你可以使用ArrayObject或SplFixedArray,但是如果你看一下它们的API,你会说你需要它们的功能来完成返回多个随机值的简单任务吗?我不这么认为。不过,别误会:如果你想使用stdClass,那就用吧。它不会破坏任何东西。但你也不会得到任何好处。为了至少获得一些好处,你可以创建一个名为ReturnValues的单独类。
可以是一个简单的标记类。
class ReturnValues {}

或者更加实用的东西
class ReturnValues implements Countable
{
    protected $values;
    public function __construct() { $this->values = func_get_args(); }
    public function __get($key) return $this->values[$key]; }
    public function count() { return count($this->values); }
}

虽然它的功能不多,而且从中获取值仍然通过隐式接口完成,但至少这个类现在有一个更明确定义的责任。您可以从这个类扩展,为特定操作创建ReturnValue对象,并为这些对象提供显式接口。
class FooReturnValues extends ReturnValues
{
    public function getFoo() { return $this->values['foo']; }
    public function getBar() { return $this->values['foo']; }
}

现在开发人员只需查看API,就可以知道foo()将返回哪些多个值。当然,为可能返回多个值的每个操作编写具体的ReturnValue类可能会很快变得乏味。就个人而言,我认为这对于最初的目的来说过于工程化。
无论如何,希望这有任何意义。

1
很有趣,你的“ReturnValues”提案。 - JonG
@JonG 谢谢。我想我的两个主要观点是:使返回的对象在语义上更清晰,并使其接口明确化。如果这些都不重要,那么在我看来这样做真的没有用。 - Gordon
我喜欢这个泛型类的想法,但是它是在特定领域/目的内泛型的。它有潜力实际上增加功能,而 stdClass 只是关联数组的另一种表示形式。 - IMSoP
我认为更易读的语法对大多数开发人员来说是主要的好处。然而,使用$options = ['a'=>'str', 'b'=>'str'];几乎同样优雅,并且允许使用为数组构建的许多本地/强大函数。因此,除非需要匹配JSON结构,否则我会坚持使用数组。 - Justin

4

好的,有三个不同之处:

  • 它们有一个身份。这就是为什么传递数组参数的默认方式是按值调用,而对象则是按共享调用
  • 语义上有所不同。如果使用对象,任何阅读代码的人都可以理解,该值代表某种实体模型,而数组应该作为集合或映射来使用。
  • 最后但并非最不重要的是,重构变得更加容易。如果您想使用具体类而不是stdClass,则只需实例化另一个类即可。这也允许您添加方法。

问候
back2dos


2
我能找到的唯一客观的信任投票是: json_decode 默认使用 stdClass,因此我们这些凡人在用户层应该在类似情况下使用 stdClass。

4
如果你想要获取数组而不是对象,你可以直接使用json_decode($json, true); - Peter
2
我们这些凡人在用户空间不仅仅是覆盖默认参数值。 - tishma

1
在数组与stdClass的测试中,PHP处理动态属性的方式比关联数组慢。我并不是为了争论微观优化而说这个,而是如果你要这样做,最好定义一个没有方法和设置公共属性的数据类,特别是如果你使用PHP 5.4+。在底层,定义的属性直接映射到C数组中,没有哈希表,而动态属性需要使用哈希表。这还有一个额外的好处,即稍后可以成为完整的类,而无需进行任何主要的接口重构。

PHP的性能特征会随着每个版本而改变。 - William Entriken

1

当我需要保持代码整洁且易于阅读时,我发现stdClass对象比数组更为有用。例如,考虑函数getProperties(),它返回一组属性,比如有关一个人的数据(姓名、年龄、性别)。如果getProperties()返回一个关联数组,则当您想要使用其中一个返回的属性时,您需要编写两个指令:

$data = getProperties();
echo $data['name'];

另一方面,如果getProperties()返回一个stdClass对象,那么你可以只用一条指令来编写它:

echo getProperties()->name;

4
请注意,如果您正在使用PHP 5.4或更高版本,则不再适用此限制,因为这只是PHP解析器的短板。现在你可以写 echo getProperties()['name'];。http://3v4l.org/t6i0r - IMSoP
@IMSoP,你可以这样做,但是很多系统仍在使用PHP 5.3,这将会抛出一个错误,让一些开发者感到困惑 :)。 - laketuna
2
@laketuna 是的,这就是我评论中的“如果”的原因 :) 还要注意的是,5.3现在已经正式不再受支持了,所以人们应该向他们的主机/管理员施压进行升级:http://php.net/eol.php - IMSoP

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接