mardi 26 février 2019

Apply Value Object (Stringly Typed) in PHP

What is the best way to prevent wrong states in an object? This is the source of my question.

Basically my curiosity started with the intention of not letting a class exist with wrong values. Prevent programmers from making mistakes in class implementations and extensions.

I did not want a class to even bother to have to deal with a wrong value. I just wanted it not to be started if a wrong value of a given status or type was passed.

I work a lot with types in string, due to a demand for legacy code. For this I find it interesting to work with "value objects". That aparently is the primary design idea. I tried to go for something with stringly-typed (another new term I just found out).

So below I have the first example. The scenario is: a class need a type and instead invoke a string 'type_x' invoke a class and this class solving the value if it is valid. This class is the one we will see below.

/**
 * StringlyTypeSecondOption
 */
class StringlyTypeFirstOption
{
    private $type;

    public static function type_1()
    {
        return new self('type_1');
    }

    public static function type_2()
    {
        return new self('type_2');
    }

    private function __construct(string $type)
    {
        $this->type = $type;
    }

    public function __toString()
    {
        return $this->type;
    }
}

echo StringlyTypeFirstOption::type_2(); //here its ok

echo StringlyTypeFirstOption::type_3(); //here we have an error cause type_3 doesnt exists

This is a very good example because we havent no if or throw exception or any logic of verification. Is oop on its own. And I think its good.

And now we have the second example. Will provide a solution for the same problem I proposed.

class StringlyTypeSecondOption
{
    private $type;

    const TYPE1 = 'type_1';
    const TYPE2 = 'type_2';

    private const ALLOWED_TYPES = [StringlyTypeSecondOption::TYPE1, StringlyTypeSecondOption::TYPE2];

    public static function factory($type)
    {
        if (!in_array($type, StringlyTypeSecondOption::ALLOWED_TYPES, true)) {
            throw new Exception("Invalid type: {$type}");
        }

        return new self($type);
    }

    private function __construct(string $type)
    {
        $this->type = $type;
    }

    public function __toString()
    {
        return $this->type;
    }
}

echo StringlyTypeSecondOption::factory('type_2'); //here its ok

echo StringlyTypeSecondOption::factory('type_3'); //here we have an exception cause type_3 doesnt exists

Is a very good example too but I already have some logic and is not so pure like the first one. But solve the problem like a charm too.

Both imlementations have strengths and weaknesses (I think). But if there is a consolidated design that fixes allowed values for a state of a class, what its name how to implement and what is the best oop beatiful and designed strategy to prevent an invalid value in an object?

I think this is more a discussion over an exact solution. If this was not the right place I ask the moderators to direct me to a better channel.

Thanks advance!

Aucun commentaire:

Enregistrer un commentaire