PHP中的值对象与关联数组
Posted
技术标签:
【中文标题】PHP中的值对象与关联数组【英文标题】:Value objects vs associative arrays in PHP 【发布时间】:2011-01-04 15:25:56 【问题描述】:(此问题使用 php 作为上下文,但不仅限于 PHP。例如,任何具有内置哈希的语言也是相关的)
让我们看一下这个例子(PHP):
function makeAFredUsingAssoc()
return array(
'id'=>1337,
'height'=>137,
'name'=>"Green Fred");
对比:
class Fred
public $id;
public $height;
public $name;
public function __construct($id, $height, $name)
$this->id = $id;
$this->height = $height;
$this->name = $name;
function makeAFredUsingValueObject()
return new Fred(1337, 137, "Green Fred");
方法#1当然更简洁,但是它很容易导致错误,例如
$myFred = makeAFredUsingAssoc();
return $myFred['naem']; // notice teh typo here
当然,有人可能会争辩说$myFred->naem
同样会导致错误,这是事实。然而,上正式的课程对我来说感觉更僵硬,但我无法证明它的合理性。
使用每种方法的优缺点是什么?人们应该何时使用哪种方法?
【问题讨论】:
【参考方案1】:我使用 OOP 语言已经超过 10 年了。 如果您了解对象的工作方式,您会喜欢它。 继承、多态、封装、重载是OOP的关键优势。 另一方面,当我们谈论 PHP 时,我们必须考虑到 PHP 并不是一种功能齐全的面向对象语言。 例如,我们不能使用方法重载或构造函数重载(直截了当)。
PHP 中的关联数组是一个非常好的特性,但我认为这会损害 php 企业应用程序。 当您编写代码时,您希望获得干净且可维护的应用程序。
您对关联数组的另一种看法是您不能使用智能感知。
所以我认为如果你想编写更简洁和更易于维护的代码,你必须在提供 OOP 功能时使用它。
【讨论】:
【参考方案2】:正确的值对象的好处是,没有办法实际制作无效的值对象,也无法更改存在的值对象(完整性和“不变性”)。只有 getter 和类型提示参数,没有办法在可编译的代码中搞砸,显然你可以很容易地使用可延展数组来做到这一点。
或者,您可以在公共构造函数中进行验证并引发异常,但这提供了一种更温和的工厂方法。
class Color
public static function create($name, $rgb)
// validate both
if ($bothValid)
return new self($name, $rgb);
else
return false;
public function getName() return $this->_name;
public function getRgb() return $this->_rgb;
protected function __construct($name, $rgb)
$this->_name = $name;
$this->_rgb = $rgb;
protected $_name;
protected $_rgb;
【讨论】:
【参考方案3】:想了很久,我自己的回答。
优先考虑值对象而不是数组的主要特点是清晰。
考虑这个函数:
// Yes, you can specify parameter types in PHP
function MagicFunction(Fred $fred)
// ...
对
function MagicFunction(array $fred)
意图更清晰。函数作者可以强制执行他的要求。
更重要的是,作为用户,我可以轻松查找什么是有效的 Fred。我只需要打开Fred.php
并发现它的内部结构。
调用者和被调用者之间存在契约。使用值对象,这个合约可以写成经过语法检查的代码:
class Fred
public $name;
// ...
如果我使用数组,我只能希望我的用户会阅读 cmets 或文档:
// IMPORTANT! You need to specify 'name' and 'age'
function MagicFunction(array $fred)
【讨论】:
【参考方案4】:像这样一个简单的类:
class PersonalData
protected $firstname;
protected $lastname;
// Getters/setters here
与数组相比几乎没有优势。
-
不可能出现一些错别字。
$data['firtsname'] = 'Chris';
将起作用,而 $data->setFirtsname('Chris');
将引发错误。
类型提示:PHP 数组可以包含所有内容(包括任何内容),而定义明确的类只包含指定的数据。
public function doSth(array $personalData)
$this->doSthElse($personalData['firstname']); // What if "firstname" index doesn't exist?
public function doSth(PersonalData $personalData)
// I am guaranteed that following method exists.
// In worst case it will return NULL or some default value
$this->doSthElse($personalData->getFirstname());
我们可以在设置/获取操作之前添加一些额外的代码,例如验证或日志记录:
public function setFirstname($firstname)
if (/* doesn't match "firstname" regular expression */)
throw new InvalidArgumentException('blah blah blah');
if (/* in debbug mode */)
log('Firstname set to: ' . $firstname);
$this->firstname = $firstname;
我们可以利用 OOP 的所有好处,例如继承、多态性、类型提示、封装等等......
如前所述,我们所有的“结构”都可以从提供Countable
、Serializable
或Iterator
接口实现的基类继承,因此我们的结构可以使用foreach
循环等。
IDE 支持。
唯一的缺点似乎是速度。创建数组并对其进行操作更快。但是我们都知道,在很多情况下,CPU 时间比程序员的时间便宜得多。 ;)
【讨论】:
我特别同意#2。它启发了我在下面的答案。 我认为使用数组可以更好地完成“验证和记录”,因为您可能需要一起验证事物而不是单独验证。【参考方案5】:老实说,我都喜欢他们。
哈希数组比创建对象要快得多,而且时间就是金钱! 但是,JSON 不喜欢哈希数组(这看起来有点像 OOP OCD)。 也许对于多人项目,定义明确的类会更好。 哈希数组可能会占用更多 CPU 时间和内存(对象具有预定义的数量),但很难确定每种情况都适用。但真正糟糕的是考虑过多使用哪一个。就像我说的,JSON 不喜欢哈希。糟糕,我使用了一个数组。我现在得修改几千行代码。
我不喜欢它,但似乎上课是更安全的方式。
【讨论】:
json_encode(array) 有什么问题?【参考方案6】:根据用例,我可能会使用或。该类的优点是我可以像使用类型一样使用它,并在方法或任何自省方法上使用类型提示。如果我只想从查询或其他东西中传递一些随机数据集,我可能会使用数组。所以我想只要 Fred 在我的模型中有特殊含义,我就会使用一个类。
附注: ValueObjects 应该是不可变的。至少如果您参考 Eric Evan 在领域驱动设计中的定义。在 Fowler 的 PoEA 中,ValueObjects 不一定必须是不可变的(尽管有人建议),但它们不应该具有身份,这显然是 Fred 的情况。
【讨论】:
【参考方案7】:让我向你提出这个问题:
像$myFred['naem']
这样的错字和像$myFred->naem
这样的错字有什么不同?在这两种情况下仍然存在相同的问题,并且它们都错误。
我喜欢在编程时使用KISS(保持简单,愚蠢)。
如果您只是从方法返回查询的子集,只需返回一个数组。 如果您将数据作为 public/private/static/protected 变量存储在您的某个类中,最好将其存储为 stdClass。 如果您稍后将其传递给另一个类方法,您可能更喜欢Fred
类的严格类型,即public function acceptsClass(Fred $fredObj)
如果要将其用作返回值,则可以像创建数组一样轻松地创建一个标准类。在这种情况下,您可以不太关心严格类型。
$class = new stdClass();
$class->param = 'value';
$class->param2 = 'value2';
return $class;
【讨论】:
任何人都喜欢使用 stdClass 而不是 assoc 的任何原因。大批?它们的功能在我看来几乎相同。副会。数组语法更加清晰和简单。 @kizz - 主要是当您需要存储对象数组以便于迭代时,数据库抽象层如何返回对象数组(每个对象代表一行)。 一个stdClass
can be iterated over just like an array。在关联数组上使用stdClass
没有直接价值,您将无法访问所有酷炫的array functions。是的,访问公共 property 时仍然可能发生拼写错误,但其他示例使用公共 methods,如果出现拼写错误,则无法正确编译。如果你打算使用一个值对象,你至少应该以不变性为目标。【参考方案8】:
哈希专家:它能够处理设计时未知的名称-值组合。
【讨论】:
上课也可以。如果您只是实例化一个类,然后执行类似 $class->madeup = 'hello'; 之类的操作回声$类->化妆;它会回显 'hello' :) (当然,当使用 php 类时,这在其他语言上不确定) 嗯...你是对的。这将在所有允许在运行时修改类的动态语言中得到支持。有使用哈希的专业人士......【参考方案9】:表面上,这两种方法是等价的。但是,在使用类时,您可以获得大多数标准 OO 好处:封装、继承等。
另外,请看以下示例:
$arr['naem'] = 'John';
完全有效,可能是一个很难找到的错误。
另一方面,
$class->setNaem('John');
永远不会工作。
【讨论】:
+1。一旦你摆脱了公共成员变量,类的优势就变得明显了。 我很好奇有人扩展了这个答案,这也与编写可测试代码 (PHPUnit)、使用其中一个的优点/缺点有关?【参考方案10】:当返回值代表应用程序中的实体时,您应该使用对象,因为这是 OOP 的目的。如果您只想返回一组不相关的值,那么它就不是那么明确了。但是,如果它是公共 API 的一部分,那么声明的类仍然是最好的方法。
【讨论】:
我非常努力地尝试并没有想到我想要返回一组不相关值的实际场景。也许在读取 CSV 时,但 PHP 的 fgetcsv() 无论如何都会给出一个数字数组,所以...【参考方案11】:我更喜欢像您的第二个示例中那样具有硬编码属性。我觉得它更清楚地定义了预期的类结构(以及类上所有可能的属性)。与第一个示例相反,它归结为始终记住使用相同的键名。有了第二个,您总是可以返回并查看类,只需查看文件顶部即可了解属性。
你会更好地知道你在第二个中做错了——如果你尝试echo $this->doesntExist
,你会得到一个错误,而如果你尝试echo array['doesntExist']
,你不会。
【讨论】:
其实你会得到一个NOTICE
of PHP Notice: Undefined index: doesntExist
抱歉,对,您会收到通知,但有时这比页面彻底中断要难得多。这可能会被忽视一段时间(没有双关语)。以上是关于PHP中的值对象与关联数组的主要内容,如果未能解决你的问题,请参考以下文章