PHP 中的值对象与关联数组

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2056931/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 04:50:31  来源:igfitidea点击:

Value objects vs associative arrays in PHP

phpassociative-arrayvalue-objects

提问by kizzx2

(This question uses PHP as context but isn't restricted to PHP only. e.g. Any language with built in hash is also relevant)

(这个问题使用 PHP 作为上下文,但不仅限于 PHP。例如,任何带有内置哈希的语言也是相关的)

Let's look at this example (PHP):

让我们看一下这个例子(PHP):

function makeAFredUsingAssoc()
{
    return array(
        'id'=>1337,
        'height'=>137,
        'name'=>"Green Fred");
}

Versus:

相对:

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");
}

Method #1 is of course terser, however it may easily lead to error such as

方法#1当然更简洁,但是很容易导致错误,比如

$myFred = makeAFredUsingAssoc();
return $myFred['naem']; // notice teh typo here

Of course, one might argue that $myFred->naemwill equally lead to error, which is true. However having a formal class just feels more rigid to me, but I can't really justify it.

当然,有人可能会争辩说这$myFred->naem同样会导致错误,这是真的。然而,有一个正式的课程对我来说感觉更僵硬,但我真的无法证明它的合理性。

What would be the pros/cons to using each approach and when should people use which approach?

使用每种方法的利弊是什么,人们应该何时使用哪种方法?

采纳答案by Zack Marrapese

Under the surface, the two approaches are equivalent. However, you get most of the standard OO benefits when using a class: encapsulation, inheritance, etc.

在表面之下,这两种方法是等效的。但是,在使用类时,您可以获得大部分标准 OO 好处:封装、继承等。

Also, look at the following examples:

另外,请查看以下示例:

$arr['naem'] = 'John';

is perfectly valid and could be a difficult bug to find.

是完全有效的,可能是一个很难找到的错误。

On the other hand,

另一方面,

$class->setNaem('John');

will never work.

永远不会工作。

回答by Crozin

A simple class like this one:

一个像这样的简单类:

class PersonalData {
    protected $firstname;
    protected $lastname;

    // Getters/setters here
}

Has few advantages over an array.

与数组相比几乎没有优势。

  1. There is no possibility to make some typos. $data['firtsname'] = 'Chris';will work while $data->setFirtsname('Chris');will throw en error.
  2. Type hinting: PHP arrays can contain everything (including nothing) while well defined class contains only specified data.

    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());
    }
    
  3. We can add some extra code before set/get operations, like validation or logging:

    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;
    
    }
  4. We can use all the benefits of OOP like inheritance, polymorphism, type hinting, encapsulation and so on...
  5. As mentioned before all of our "structs" can inherit from some base class that provides implementation for Countable, Serializableor Iteratorinterfaces, so our structs could use foreachloops etc.
  6. IDE support.
  1. 没有可能犯一些错别字。$data['firtsname'] = 'Chris';会工作,同时$data->setFirtsname('Chris');会抛出错误。
  2. 类型提示: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());
    }
    
  3. 我们可以在设置/获取操作之前添加一些额外的代码,比如验证或日志记录:

    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;
    
    }
  4. 我们可以利用 OOP 的所有好处,比如继承、多态、类型提示、封装等等……
  5. 至于我们所有的“结构”的前面提到的可以从一些基类,提供了实现继承CountableSerializableIterator接口,所以我们的结构可以使用foreach循环等。
  6. IDE 支持。

The only disadvantage seems to be speed. Creation of an array and operating on it is faster. However we all know that in many cases CPU time is much cheaper than programmer time. ;)

唯一的缺点似乎是速度。创建数组并对其进行操作会更快。但是我们都知道,在很多情况下,CPU 时间比程序员时间便宜得多。;)

回答by kizzx2

After thinking about it for some time, here's my own answer.

经过一段时间的思考,这是我自己的答案。

The main thing about preferring value objectsover arrays is clarity.

优先考虑值对象而不是数组的主要问题是清晰度

Consider this function:

考虑这个函数:

// Yes, you can specify parameter types in PHP
function MagicFunction(Fred $fred)
{
    // ...
}

versus

相对

function MagicFunction(array $fred)
{
}

The intent is clearer. The function author can enforce his requirement.

意图更加明确。函数作者可以强制执行他的要求。

More importantly, as the user, I can easily look up what constitutes a valid Fred. I just need to open Fred.phpand discover its internals.

更重要的是,作为用户,我可以轻松查找构成有效 Fred 的内容。我只需要打开Fred.php并发现它的内部结构。

There is a contract between the caller and the callee. Using value objects, this contract can be written as syntax-checked code:

调用者和被调用者之间存在合同。使用值对象,这个合约可以写成语法检查代码:

class Fred
{
    public $name;
    // ...
}

If I used an array, I can only hope my user would read the comments or the documentation:

如果我使用数组,我只能希望我的用户会阅读评论或文档:

// IMPORTANT! You need to specify 'name' and 'age'
function MagicFunction(array $fred)
{
}

回答by Gordon

Depending on the UseCase I might use either or. The advantage of the class is that I can use it like a Type and use Type Hints on methods or any introspection methods. If I just want to pass around some random dataset from a query or something, I'd likely use the array. So I guess as long as Fredhas special meaning in my model, I'd use a class.

根据用例,我可能会使用或。该类的优点是我可以像 Type 一样使用它,并在方法或任何内省方法上使用 Type Hints。如果我只想从查询或其他东西中传递一些随机数据集,我可能会使用该数组。所以我想只要Fred在我的模型中具有特殊意义,我就会使用一个类。

On a sidenote:
ValueObjects are supposed to be immutable. At least if you are refering to Eric Evan's definition in Domain Driven Design. In Fowler's PoEA, ValueObjects do not necessarily have to be immutable (though it is suggested), but they should not have identity, which is clearly the case with Fred.

旁注:
ValueObjects 应该是不可变的。至少如果您参考 Eric Evan 在领域驱动设计中的定义。在 Fowler 的 PoEA 中,ValueObjects 不一定必须是不可变的(尽管有人建议这样做),但它们不应该具有标识,这对于Fred来说显然就是这种情况。

回答by Corey Ballou

Let me pose this question to you:

让我向你提出这个问题:

What's so different about making a typo like $myFred['naem']and making a typo like $myFred->naem? The same issue still exists in both cases and they both error.

打错字$myFred['naem']和打错字有$myFred->naem什么不同?在这两种情况下仍然存在相同的问题,并且它们都出错。

I like to use KISS(keep it simple, stupid) when I program.

我在编程时喜欢使用KISS(保持简单,愚蠢)。

  • If you are simply returning a subset of a query from a method, simply return an array.
  • If you are storing the data as a public/private/static/protectedvariable in one of your classes, it would be best to store it as a stdClass.
  • If you are going to later pass this to another class method, you might prefer the strict typing of the Fredclass, i.e. public function acceptsClass(Fred $fredObj)
  • 如果您只是从方法中返回查询的子集,只需返回一个数组。
  • 如果您将数据作为公共/私有/静态/受保护变量存储在您的一个类中,最好将其存储为 stdClass。
  • 如果您稍后打算将此传递给另一个类方法,您可能更喜欢类的严格类型Fred,即public function acceptsClass(Fred $fredObj)

You could have just as easily created a standard class as opposed to an array if it is to be used as a return value. In this case you could care less about strict typing.

如果要将它用作返回值,您可以轻松地创建一个标准类而不是数组。在这种情况下,您可能不太关心严格的类型。

$class = new stdClass();
$class->param = 'value';
$class->param2 = 'value2';
return $class;

回答by Veger

A pro for the hash: It is able to handle name-value combinations which are unknown at design time.

散列的优点:它能够处理在设计时未知的名称-值组合。

回答by Steve Clay

The benefit of a proper Value Object is that there's no way to actually make an invalid one and no way to change one that exists (integrity and "immutability"). With only getters and type hinting parameters, there's NO WAY to screw it up in compilable code, which you can obviously easily do with malleable arrays.

一个合适的值对象的好处是没有办法实际制造一个无效的值对象,也没有办法改变一个存在的值对象(完整性和“不变性”)。只有 getter 和类型提示参数,没有办法在可编译的代码中搞砸,这显然可以用可塑性数组轻松完成。

Alternatively you could validate in a public constructor and throw an exception, but this provides a gentler factory method.

或者,您可以在公共构造函数中进行验证并抛出异常,但这提供了更温和的工厂方法。

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;
}

回答by Sanosay

I have worked with OOP Languages over 10 years. If you understand the way objects work you will love it. Inheritance, Polymorphism, Encapsulation, Overloading are the key advantage of OOP. On the other hand when we talk about PHP we have to consider that PHP isn't a full featured Object Oriented language. For example we cant use method overloading or constructor overloading (straightforward).

我使用 OOP 语言已经超过 10 年了。如果您了解对象的工作方式,您就会爱上它。继承、多态、封装、重载是 OOP 的关键优势。另一方面,当我们谈论 PHP 时,我们必须考虑 PHP 不是一种功能齐全的面向对象语言。例如,我们不能使用方法重载或构造函数重载(直接)。

Associative arrays in PHP is a VERY nice feature but i think that harms php enterprise applications. When you write code you want to get clean and maintainable application.

PHP 中的关联数组是一个非常好的功能,但我认为这会损害 php 企业应用程序。当您编写代码时,您希望获得干净且可维护的应用程序。

Another think that you loose with Associative arrays is that you can't use intellisense.

另一个认为你对关联数组松懈的想法是你不能使用智能感知。

So i think if you want to write cleanner and more maintainable code you have to use the OOP features when it is provided.

所以我认为如果你想编写更干净、更易于维护的代码,你必须在提供 OOP 特性时使用它。

回答by Josh

Honestly, I like them both.

老实说,我喜欢他们两个。

  • Hash arrays are way faster than making objects, and time is money!
  • But, JSON doesn't like hash arrays (which seems a bit like OOP OCD).
  • Maybe for projects with multiple people, a well-defined class would be better.
  • Hash arrays might take more CPU time and memory (an object has a predefined amount), though its hard to be sure for every scenario.
  • 散列数组比创建对象快得多,而且时间就是金钱!
  • 但是,JSON 不喜欢散列数组(这看起来有点像 OOP OCD)。
  • 也许对于多人的项目,定义明确的类会更好。
  • 散列数组可能需要更多的 CPU 时间和内存(一个对象有一个预定义的数量),尽管很难确定每种情况。

But what really sucks is thinking about which one to use too much. Like I said, JSON doesn't like hashes. Oops, I used an array. I got to change a few thousand lines of code now.

但真正糟糕的是考虑过多使用哪一个。就像我说的,JSON 不喜欢散列。糟糕,我使用了一个数组。我现在必须更改几千行代码。

I don't like it, but it seems that classes are the safer way to go.

我不喜欢它,但似乎类是更安全的方式。

回答by Will Vousden

When the return value represents an entity in your application, you should use an object, as this is the purpose of OOP. If you just want to return a group of unrelated values then it's not so clear cut. If it's part of a public API, though, then a declared class is still the best way to go.

当返回值代表应用程序中的实体时,您应该使用对象,因为这是 OOP 的目的。如果您只想返回一组不相关的值,那么它就不是那么明确了。但是,如果它是公共 API 的一部分,那么声明的类仍然是最好的方法。