Value objects vs associative arrays in PHP

Viewed 25701

(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?

11 Answers

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.

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.

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;

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

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.

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.

Related