Search code examples
phpalgorithmdesign-patternssolid-principles

Refactoring a function that returns different data types


I'm not sure what the most efficient way of doing this would be but I believe it's good practice to have a function that returns one data type, whether it be a boolean or string but in some situations I can see how difficult it would be to implement such a strict rule, for example the code below will return an object if a database record is found otherwise it will return false.

public function get()
{
   $record = $this->db->query('select id, first_name, last_name from users where id = :id', ['id' => 1]);

   if ($record) {
      return new User($record['id'], $record['first_name'], $record['last_name']);
   } else {
      return false;
   }
}

My question is what is the recommended best practice in this situation?


Solution

  • It depends on context and on your preferences.

    For scalar types and arrays you have a certain default value such 0, empty string, empty array, etc. When default value is a regular result, you can use something deliberately wrong. For example indexOf in JavaScript returns -1 when substring not found. But PHP's strpos() returns false in this case.

    For objects, null (not the false) usually used as the absence of them. In some strict-typed languages (C#, Java) all object references are nullable: when method can return an object, it also can return a null. But this approach also may cause a lot of problems. They may be avoided with Null Object pattern in some cases.

    Also, PHP 7.1 provide a nullable type notation:

    function foo(): ?int
    {
        return null; // ok
    }
    

    In the end, you can throw an exception when no value. For example, in the Python method dict.get() raises a KeyError when requested key is not in dictionary.

    As you see, different languages and API use a different ways, there is no common right answer.

    My personal recommendation for your example is separation methods by behavior:

    // Get-method should always return a value
    function getObject(): ObjectClass
    {
        // ...obtain the $object;
        if (!$object) {
            throw new Exception();
        }
        return $object;
    }
    
    // Find-method can return value or NULL.
    function findObject(): ?ObjectClass
    {
        // ...obtain the $object
        return $object ?: null;
    }