0

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!

  • You want an enum in a language that doesn't support it. A better IDE for writing PHP will catch the fact StringlyTypeFirstOption::type_3() is not a valid member of the class. It's difficult to make up for a lack of language features and the syntax checking of a good IDE. – Greg Burghardt Feb 26 at 14:48
  • Actually the phpstorm catch the inexistence of type3. This is great already. But even better than that is the fact the code will fail and the class will not be 'used' / 'bothered'. If it was setType('type_3') the class will be busy to discover if the type 3 is valid. – Rafael Soufraz Feb 26 at 14:54
  • Your first solution is actually better, because you get an error that the type_3 method is not defined. A run time exception is worse. Maybe consider concrete sub classes? – Greg Burghardt Feb 26 at 15:31
  • Unless there is behavior that you can put into these "stringly typed classes" then there really is no point in having a class. – Greg Burghardt Feb 26 at 15:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.