kiz*_*zx2 38 php associative-array value-objects
(这个问题使用PHP作为上下文,但不仅限于PHP.例如,任何内置哈希的语言也是相关的)
我们来看看这个例子(PHP):
function makeAFredUsingAssoc()
{
return array(
'id'=>1337,
'height'=>137,
'name'=>"Green Fred");
}
Run Code Online (Sandbox Code Playgroud)
与:
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");
}
Run Code Online (Sandbox Code Playgroud)
方法#1当然更简洁,但它可能很容易导致错误,例如
$myFred = makeAFredUsingAssoc();
return $myFred['naem']; // notice teh typo here
Run Code Online (Sandbox Code Playgroud)
当然,有人可能会争辩说$myFred->naem同样会导致错误,这是事实.然而,正式课程对我来说感觉更加僵硬,但我无法证明这一点.
使用每种方法的优点/缺点是什么?人们何时应该使用哪种方法?
Cro*_*zin 29
像这样一个简单的类:
class PersonalData {
protected $firstname;
protected $lastname;
// Getters/setters here
}
Run Code Online (Sandbox Code Playgroud)
与阵列相比几乎没有什么优势.
$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());
}
Run Code Online (Sandbox Code Playgroud)我们可以在set/get操作之前添加一些额外的代码,比如验证或记录:
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;
Run Code Online (Sandbox Code Playgroud)
}
Countable,Serializable或Iterator接口,所以我们的结构可以使用foreach循环等.唯一的缺点似乎是速度.创建阵列并对其进行操作更快.但是我们都知道,在许多情况下,CPU时间比程序员时间便宜得多.;)
Zac*_*ese 28
在表面下,这两种方法是等价的.但是,在使用类时,您可以获得大多数标准OO优势:封装,继承等.
另外,请看以下示例:
$arr['naem'] = 'John';
Run Code Online (Sandbox Code Playgroud)
是完全有效的,可能是一个难以找到的bug.
另一方面,
$class->setNaem('John');
Run Code Online (Sandbox Code Playgroud)
永远不会工作.
在考虑了一段时间之后,这是我自己的答案.
关于在数组上优选值对象的主要原因是清晰度.
考虑这个功能:
// Yes, you can specify parameter types in PHP
function MagicFunction(Fred $fred)
{
// ...
}
Run Code Online (Sandbox Code Playgroud)
与
function MagicFunction(array $fred)
{
}
Run Code Online (Sandbox Code Playgroud)
意图更清晰.功能作者可以执行他的要求.
更重要的是,作为用户,我可以轻松查找构成有效Fred的内容.我只需要打开Fred.php并发现它的内部结构.
呼叫者和被呼叫者之间存在合同.使用值对象,此契约可以编写为语法检查代码:
class Fred
{
public $name;
// ...
}
Run Code Online (Sandbox Code Playgroud)
如果我使用数组,我只希望我的用户会阅读评论或文档:
// IMPORTANT! You need to specify 'name' and 'age'
function MagicFunction(array $fred)
{
}
Run Code Online (Sandbox Code Playgroud)
根据UseCase,我可能会使用或.该类的优点是我可以像使用Type一样使用它,并在方法或任何内省方法上使用Type Hints.如果我只是想从查询或其他东西传递一些随机数据集,我可能会使用该数组.所以我想只要弗雷德在我的模型中有特殊意义,我就会使用一个类.
旁注:
ValueObjects应该是不可变的.至少如果您在域驱动设计中引用Eric Evan的定义.在Fowler的PoEA中,ValueObjects不一定必须是不可变的(虽然它是建议的),但它们不应该具有身份,Fred显然就是这种情况.