Cloner / copier un objet de l’intérieur ou de l’extérieur

Lorsque vous utiliser la programmation orientation objet vous avez parfois besoin de faire des copies identiques d’objet pour le modifier sans altérer l’original.

Le premier obstacle en PHP est que tous les objets sont des références. Alors si vous essayez de faire comme les autres variables vous aurez un résultat inattendu.

En PHP toutes les valeurs scalaires sont des copies ils ont donc un espace mémoire diffèrent. (Prenez note : Pour une question d’optimisation PHP est asse intelligent pour ne pas allouer un nouvel espace mémoire tant que la valeur n’est pas modifier.)

1
2
3
4
5
6
$name = 'Yanik';
$newName = $name;
$newName = 'Pascal';
 
echo $name; // Yanik
echo $newName; // Pascal

Dans l’exemple précédent nous voyons que la variable $name n’as pas été altérer.

Maintenant, voyez ce qui se passe avec un objet.

1
2
3
4
5
6
7
8
9
10
$user = new MyUser();
$user->fName = 'Yanik';
$user->lName = 'Lupien';
 
$newUser = $user;
 
$newUser->fName = 'Pascal';
 
echo "{$user->fName} {$user->lName}"; // Pascal Lupien
echo "{$newUser->fName} {$newUser->lName}"; // Pascal Lupien

Pour être en mesure d’avoir un objet distinct qui a les mêmes valeurs que le l’original vous avez plusieurs possibilité qui s’offre à vous.

Cloné de l’extérieur

Partons de l’exemple suivant.

1
2
3
$user = new MyUser();
$user->fName = 'Yanik';
$user->lName = 'Lupien;

Puisque PHP copie les valeurs scalaire vous pouvez faire vous même l’association de tous les membres un a un.

1
2
3
4
// Creation d'un nouvel objet et assignation des propriétés une a une.
$newUser = new MyUser();
$newUser->fName = $user->fName;
$newUser->lName = $user->lName;

Tout se passe bien si les membres sont tous accessibles publiquement.

Cette technique pose quelques problèmes.

  • Le script, classe, méthode ou fonction qui utilise l’objet MyUser et qui fait une copie de cette façon connaît maintenant le détail interne l’objet copié. Le jour où vous allez ajouter un nouveau membre à votre classe MyUser vous aurez besoin de revoir tous les endroits ou vous avez fait ce genre de copie.
  • Vous allez aussi probablement rencontrer un problème lorsque vous allez ajouter des membres protected et ou prive. Vous ne serez pas en mesure de copier ces membres et il est très probable qu’il soit nécessaire pour le bon fonctionnement de l’objet.

Duplication par clone (De l’intérieur)

PHP offre un opérateur qui est fait pour la duplication d’objet.

1
$b = clone $a;

À l’interne, PHP crée un nouvel objet sans appeler le constructeur et assigne tous les membres scalaires de celui à un nouvel espace mémoire. De cette façon si vous modifiez les membres du clone, l’original ne sera pas altéré. Prenez note que si votre objet contient des membres objet l’opérateur clone va faire des références à ceux-ci. Il est toute foi possible de cloner ceux-ci de l’interne par l’implémentation de la méthode __clone() { }.

Quand implémenter la méthode __clone() ?

Lorsqu’un ou plusieurs membres pointent sur d’autres objets dans le cas d’une agrégation d’objet. L’exemple le plus simple serait un objet voiture. L’objet voiture est fait de plusieurs objets tel que 4 roues, un moteur, et un volant.
Au moment d’un clonage de voiture, toutes les composantes de celle-ci doivent être clonées alors que le membre conducteur lui ne devrait pas être cloné puisque la voiture n’en est pas propriétaire. Dans ce cas-ci, l’idéal serait probablement de définir le membre conducteur à null ou lancer un exception qu’il n’est pas possible de cloner un véhicule en cour d’utilisation.

Puisque par défaut PHP fait des références aux objets, il est donc nécessaire de spécifier quel membre de l’agrégation cloner. La méthode __clone() de la classe voiture devrait donc ressembler à :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Car {
  /**
   * @var IWeel
   */
  private $weel;
 
  /**
   * @var ITire
   */
  private $tire1;
  private $tire2;
  private $tire3;
  private $tire4;
 
  /**
   * @var IEngine
   */
  private $engine
 
  /**
   * @var IDriver
   */
  private $driver;
 
  /**
   * Clone all members (Object agregation)
   * @return void 
   */
  function __clone() {
    if ($this->driver !== null) {
      throw new Exception("Unable to clone, car is in use.");
    }
 
    $this->tire1 = clone $this->tire1;
    $this->tire2 = clone $this->tire2;
    $this->tire3 = clone $this->tire3;
    $this->tire4 = clone $this->tire4;
    $this->weel = clone $this->weel;
    $this->engine = clone $this->engine;
  }
}

Les avantages de cette technique

  • Vous n’exposez pas les détails et la structure interne de l’objet que vous avez à dupliquer.
  • Peut importe si le moteur est un V4 ou un V8 l’important est seulement qu’il implémente l’interface IEngine.
  • Si votre classe évolue dans le temps, vous n’aurez pas à modifier tous les endroits ou il y aurait des duplications d’instance.
  • Encapsulation de la copie dans l’objet lui-même. Puisque votre classe est la mieux placée pour se copier elle-même. Puisqu’elle connaît tous ses membres public, protected et privé et qu’elle y a accès. De plus, elle est en mesure de distinguer ses membres qui sont des dépendances externent de ceux qui forment une agrégation.
Leave a Comment