Objets-Valeurs et immutabilité

Cet article a été publié depuis plus de 6 mois, cela signifie que le contenu peut ne plus être d'actualité.

J'ai déjà parlé plusieurs fois des Objets-Valeurs (Value Objects) et quand les utiliser, mais je n'ai encore jamais évoqué le concept d'immutabilité qui en est indissociable.

Un objet immutable possède un état qui ne peut pas changer au cours du temps. Pourquoi est-il aussi important qu'un objet-valeurs ne puisse changer ? Les objets-valeurs ont pour objectif de représenter une donnée à un instant T. Ce qui a de la valeur pour un tel objet est son état. De ce fait, si l'état de notre objet change, il ne peut être représenté par une même instance (contrairement à un objet de type Entité) puisque c'est l'état de notre objet qui détermine son identité.

Prenons un exemple concret. Si dans un modèle objet, nous souhaitons représenter une couleur par un Objet-Valeurs, il est logique que ce dernier soit immutable. L'état caractèristique de l'objet et la couleur qui lui est associé, si cette dernière change, elle ne peut être associé à la même instance et il devra s'agir d'un nouvel objet.

Cela veut également dire que s'il est possible d'effectuer des opérations sur notre Objet-Valeurs, le résultat provoquera la création d'un nouvel Objet-Valeurs. Reprenons l'exemple de notre objet Money :

class Money
{
  /** @var int */
  private $amount;

  /** @var string */
  private $currency;

  /**
   * @param int    $amount
   * @param string $currency
   */
  public function __construct($amount, $currency)
  {
    $this->amount   = $amount;
    $this->currency = $currency;
  }

  /**
   * @return int
   */
  public function getAmount()
  {
    return $this->amount;
  }

  /**
   * @return float
   */
  public function getFormatedAmount()
  {
    return round($this->amount / 100, 2);
  }
}

Nous souhaitons ajouter la possibilité d'additionner deux objets de types Money. Pour cela nous ajoutons le code suivant :

class Money
{
  // ...

  /**
   * @param Money $money
   * @return static
   */
  public function add(Money $money)
  {
    // check same currency

    $value = $this->amount + $money->getAmoun();

    return new static($value, $this->currency);
  }
}

Le code ci-dessus permet d'ajouter deux objets Money et l'objet qui en résulte est un nouvel objet-valeurs. Il est ainsi possible d'ajouter des comportements à ces derniers tout en conservant le principe d'immutabilité.