PHP 中的值对象与关联数组

发布于 2024-08-17 21:01:29 字数 873 浏览 2 评论 0原文

(这个问题使用 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 同样会导致错误,这是事实。然而,正式的课程对我来说感觉更严格,但我不能真正证明它是合理的。

使用每种方法的优点/缺点是什么?人们应该何时使用哪种方法?

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

Let's look at this example (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

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

Of course, one might argue that $myFred->naem will 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.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

平定天下 2024-08-24 21:01:29

从表面上看,这两种方法是等效的。然而,当使用类时,您可以获得大多数标准 OO 好处:封装、继承等。

此外,请查看以下示例:

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

完全有效,并且可能是很难发现的错误。

另一方面,

$class->setNaem('John');

永远不会起作用。

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

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.

酒几许 2024-08-24 21:01:29

像这样的简单类:

class PersonalData {
    protected $firstname;
    protected $lastname;

    // Getters/setters here
}

与数组相比几乎没有什么优点。

  1. 不可能出现一些拼写错误。 $data['firtsname'] = 'Chris'; 将起作用,而 $data->setFirtsname('Chris'); 将抛出错误。
  2. 类型提示:PHP 数组可以包含所有内容(包括什么都不包含),而定义良好的类仅包含指定的数据。

    public function doSth(array $personalData) {
        $this->doSthElse($personalData['firstname']); // 如果“firstname”索引不存在怎么办?
    }
    
    
    公共函数 doSth(PersonalData $personalData) {
        // 我保证以下方法存在。 
        // 在最坏的情况下它将返回 NULL 或一些默认值
        $this->doSthElse($personalData->getFirstname());
    }
    
  3. 我们可以在设置/获取操作之前添加一些额外的代码,例如验证或日志记录:

    public function setFirstname($firstname) {
        if (/* 与“名字”正则表达式不匹配 */) {
            抛出新的 InvalidArgumentException('等等等等');
        }
    
    
    
    if (/* 处于调试模式 */) {
        log('名字设置为:' . $firstname);
    }
    
    
    $this->名字 = $名字;
    

    }

  4. 我们可以利用 OOP 的所有优点,例如继承、多态性、类型提示、封装等等...
  5. 如前所述在我们所有的“结构”可以从某个基类继承之前,该基类提供 CountableSerializedIterator 接口的实现,因此我们的结构可以使用foreach 循环等。IDE
  6. 支持。

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

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, Serializable or Iterator interfaces, so our structs could use foreach loops etc.
  6. IDE support.

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. ;)

久随 2024-08-24 21:01:29

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

相对于数组优先选择值对象,最主要的一点是清晰度

考虑一下这个函数:

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

vs

function MagicFunction(array $fred)
{
}

意图更清晰。函数作者可以强制执行他的要求。

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

调用者和被调用者之间存在契约。使用值对象,这个合约可以编写为经过语法检查的代码:

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

如果我使用数组,我只能希望我的用户能够阅读注释或文档:

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

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

The main thing about preferring value objects over 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.php and discover its internals.

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)
{
}
只是在用心讲痛 2024-08-24 21:01:29

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

旁注:
ValueObject 应该是不可变的。至少如果您引用 Eric Evan 在领域驱动设计中的定义。在 Fowler 的 PoEA 中,ValueObject 不一定必须是不可变的(尽管这是建议的),但它们不应该具有身份,Fred 显然就是这种情况。

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 Fred has special meaning in my model, I'd use a class.

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.

夜未央樱花落 2024-08-24 21:01:29

让我向您提出这个问题:

$myFred['naem'] 这样的拼写错误和像 $myFred->naem 这样的拼写错误有什么不同?这两种情况仍然存在同样的问题,并且都出错。

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

  • 如果您只是从方法返回查询的子集,则只需返回一个数组即可。
  • 如果您将数据存储为某个类中的公共/私有/静态/受保护变量,则最好将其存储为 stdClass。
  • 如果您稍后要将其传递给另一个类方法,您可能更喜欢 Fred 类的严格类型,即 public functionacceptClass(Fred $fredObj)

您可以如果要用作返回值,则创建标准类与创建数组一样容易。在这种情况下,您可以不太关心严格类型。

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

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.

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

  • 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/protected variable 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 Fred class, i.e. 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;
﹎☆浅夏丿初晴 2024-08-24 21:01:29

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

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

一笔一画续写前缘 2024-08-24 21:01:29

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

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

另一个认为你对关联数组感到不满的地方是你不能使用智能感知。

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

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).

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.

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.

耳根太软 2024-08-24 21:01:29

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

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.

燃情 2024-08-24 21:01:29

老实说,我两个都喜欢。

  • 哈希数组比创建对象快得多,时间就是金钱!
  • 但是,JSON 不喜欢哈希数组(这看起来有点像 OOP OCD)。
  • 也许对于多人的项目来说,一个定义明确的类会更好。
  • 哈希数组可能需要更多的 CPU 时间和内存(对象具有预定义的数量),但很难确定每种情况。

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

我不喜欢这样,但似乎上课是更安全的方式。

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.

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.

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

清晨说晚安 2024-08-24 21:01:29

正确的值对象的好处是,没有办法真正创建一个无效的值对象,也没有办法改变现有的值对象(完整性和“不变性”)。仅使用 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;
}

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.

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;
}
忆离笙 2024-08-24 21:01:29

我更喜欢像第二个示例一样具有硬编码属性。我觉得它更清楚地定义了预期的类结构(以及类上所有可能的属性)。与第一个示例相反,第一个示例归结为始终记住使用相同的键名称。对于第二个,您可以随时返回并查看该类,只需查看文件顶部即可了解其属性。

你会更好地知道你在第二个上做错了——如果你尝试echo $this->doesntExist,你会得到一个错误,而如果你尝试echo array['doesntExist'] 你不会。

I prefer to have hard-coded properties like in your second example. I feel like it more clearly defines the expected class structure (and all possible properties on the class). As opposed to the first example which boils down to just always remembering to use the same key names. With the second you can always go back and look at the class to get an idea of the properties just by looking at the top of the file.

You'll better know you're doing something wrong with the second one -- if you try to echo $this->doesntExist you'll get an error whereas if you try to echo array['doesntExist'] you won't.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文