lundi 6 août 2018

How to distinguish between "unset" and "no-assignment" for NULL values?

In php in this example – but in general programming actually, is there a way distinguish between a "no-assignment" and "unset-value" command for null values when merging 2 immutable data objects of the same type?

Consider this php class that is an immutable data object. It takes a string and an integer in its constructor, and only provides accessors for the values:

class Data
{
    protected $someNumber;
    protected $someString;

    public function __construct(?int $someNumber, ?string $someString)
    {
        $this->someNumber = $someNumber;
        $this->someString = $someString;
    }

    public function getSomeNumber(): ?int
    {
        return $this->someNumber;
    }

    public function getSomeString(): ?string
    {
        return $this->someString;
    }
}

The values can be either null or their respective datatypes string or integer at all times. Also the constructor accepts null values instead of string and / or int: the UNSET operation.

Now I want to be able to merge 2 instances of Data, something like this simplified factory method that accepts $first and $second, where data in $second overrides data in $first, if present.

class DataFactory
{
    public function merge(Data $first, Data $second): Data
    {
        $firstNumber = $first->getSomeNumber();
        $firstString = $first->getSomeString();

        $secondNumber = $second->getSomeNumber();
        $secondString = $second->getSomeString();

        // This must be this strict.
        $newNumber = null === $secondNumber ? $firstNumber : $secondNumber;
        $newString = null === $secondString ? $firstString : $secondString;

        return new Data($newNumber, $newString);
    }
}

In the above example, null values returned by the accessors of $second are interpreted as NO UPDATE operation: when null is encountered the corresponding value of $first is kept. The problem is that I want to be able to distinguish between a request for either a NO UPDATE operation or an UNSET operation within merge.

The strict typing in the classes disallows the use of some sort of string constant like "DATA_UNSET_FIELD" as a value to flag, so implementing this directly on the data itself seems impossible. More so even because passing it null should definitely mean UNSET.

I am thinking about some sort of update lens that explicitly specifies the properties that should be UNSET, so that null values in $second would simply mean NO UPDATE.

What would be a compact object oriented pattern to solve this? I can already imagine problems like exploding plain array schemas or a class explosion of strategy classes as data grows. Also I'm slightly concerned about the "mobility" of Data objects as new objects have to be associated with them at some point.

Thanks in advance!

This is an actual problem I'm facing in a live project :)

Aucun commentaire:

Enregistrer un commentaire