Vous avez lu avec attention notre premier billet sur R6, et vous en voulez encore plus ? C’est normal, nous vous avions promis de continuer à vous parler du package le plus installé de l’univR.
Le programme du jour ouvre l’appétit, donc asseyez-vous et venez découvrir nos plats : clonage et héritage, classes portables et non portables, et données partagées.
Le clonage
Pour créer un nouvel objet {R6}, vous appelez $new()
après le nom de la classe, nous avons déjà vu cela dans notre premier billet. Une fois un nouvel objet instancié avec new, vous pouvez créer une copie de cet objet, grâce à la méthode clone()
, qui peut se faire de manière classique, ou avec le paramètre deep=TRUE
: nous verrons juste après la différence entre ces deux méthodes.
Revenons à notre classe définie dans notre post précédent : team
.
[pastacode lang=”R” manual=”team%20%3C-%20R6Class(%22Team%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20public%20%3D%20list(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20contact%20%3D%20function()%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(data.frame(Mail%20%3D%20private%24privMail%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Phone%20%3D%20private%24privPhone))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20message(private%24privCopyright)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20initialize%20%3D%20function(Mail%2C%20Phone)%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20private%24privMail%20%3C-%20Mail%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20private%24privPhone%20%3C-%20Phone%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20finalize%20%3D%20function()%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20message(%22Merci%20d’avoir%20utilis%C3%A9%20notre%20classe%20R6%20!%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20private%20%3D%20list(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20privMail%20%3D%20NULL%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20privPhone%20%3D%20NULL%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20privCopyright%20%3D%20%22Classe%20cr%C3%A9%C3%A9e%20par%20ThinkR%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20active%20%3D%20list(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Mail%20%3D%20function()%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20private%24privMail%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Phone%20%3D%20function(value)%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(missing(value))%20return(private%24privPhone)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%20if%20(stringi%3A%3Astri_detect_regex(str%20%3D%20value%2C%20pattern%20%3D%20%22(%5B0-9%5D%7B2%7D%5C%5C.%3F)%7B5%7D%22))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20private%24privPhone%20%3C-%20value%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20message(%22Le%20format%20du%20num%C3%A9ro%20de%20t%C3%A9l%C3%A9phone%20est%20incorrect%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%0A)%0A%0Athinkr%20%3C-%20team%24new(Mail%20%3D%20%22team%40thinkr.fr%22%2C%20Phone%20%3D%20%2201.85.09.14.03%22)” message=”” highlight=”” provider=”manual”/]
Une belle classe, cela va sans dire (oui, une classe qui a de la classe). Nous avons un objet thinkr
, qui est une instance de team.
Maintenant, la grande question : comment créer un nouvel objet depuis un objet déjà instancié ? On pourrait tout simplement penser à la méthode classique, avec un <-.
[pastacode lang=”R” manual=”thinkr_bis%20%3C-%20thinkr%0A%23%20Changeons%20le%20t%C3%A9l%C3%A9phone%0Athinkr_bis%24Phone%20%3C-%20%2201.02.03.04.05%22%0A%23%20Retour%20sur%20le%20premier%20objet%0Athinkr%24Phone%0A%5B1%5D%20%2201.02.03.04.05%22″ message=”” highlight=”” provider=”manual”/]
Eh oui, souvenez-vous : la copie des objets {R6} se fait par référence, pas par valeur. Ici, thinkr
et thinkr_bis
pointent vers le même objet, donc changer un élément de l’un modifie également l’autre. Alors, comment effectuer une copie par valeur ? Direction la méthode clone
.
[pastacode lang=”R” manual=”thinkr%20%3C-%20team%24new(Mail%20%3D%20%22team%40thinkr.fr%22%2C%20Phone%20%3D%20%2201.85.09.14.03%22)%0Athinkr%24Phone%0A%5B1%5D%20%2201.85.09.14.03%22%0A%0Athinkr_bis%20%3C-%20thinkr%24clone()%0Athinkr_bis%24Phone%20%3C-%20%2201.%22%0A%60Le%20format%20du%20num%C3%A9ro%20de%20t%C3%A9l%C3%A9phone%20est%20incorrect%60%0A%23%20Oops%20%0A%0Athinkr_bis%24Phone%20%3C-%20%2201.01.01.01.01%22%0Athinkr_bis%24Phone%0A%5B1%5D%20%2201.01.01.01.01%22%0A%0Athinkr%24Phone%0A%5B1%5D%20%2201.85.09.14.03%22″ message=”” highlight=”” provider=”manual”/]
Ici, il s’agit bien d’un nouvel objet, qui a les mêmes comportements que son clone : on voit par exemple que le changement de numéro reste contrôlé. À noter également : les éléments changés dans thinkr_bis
n’affecteront pas thinkr
.
Du moins… parce que nous avons cloné une instance qui ne contient que des fonctions et des données. Si notre objet contient d’autres instances {R6}, la musique va être différente :
[pastacode lang=”R” manual=”Team1%20%3C-%20R6Class(%22Team1%22%2C%20public%20%3D%20list(mail%20%3D%20%22team%40thinkr.fr%22))%0A%0ATeamClone%3C-%20R6Class(%22TeamClone%22%2C%0A%20%20public%20%3D%20list(%0A%20%20%20%20teambis%20%3D%20NULL%2C%0A%20%20%20%20initialize%20%3D%20function()%20self%24teambis%20%3C-%20Team1%24new()%0A%20%20)%0A)%0A%0At1%20%3C-%20TeamClone%24new()%0At2%20%3C-%20t1%24clone()%0A%0At1%24teambis%24mail%0A%5B1%5D%20%22team%40thinkr.fr%22%0At2%24teambis%24mail%0A%5B1%5D%20%22team%40thinkr.fr%22%0A%0At2%24teambis%24mail%20%3C-%20%22colin%40thinkr.fr%22%0At2%24teambis%24mail%0A%5B1%5D%20%22colin%40thinkr.fr%22%0At1%24teambis%24mail%0A%5B1%5D%20%22colin%40thinkr.fr%22″ message=”” highlight=”” provider=”manual”/]
Ici, mail fait référence au même environnement R6 en mémoire. C’est pourquoi le remplacer dans une instance le transforme aussi dans l’autre. Pour empêcher ce comportement, c’est-à-dire effectuer une copie par valeur également de l’élément R6, il faut utiliser la méthode clone(deep = TRUE)
.
[pastacode lang=”R” manual=”t1%20%3C-%20TeamClone%24new()%0At2%20%3C-%20t1%24clone(deep%20%3D%20TRUE)%0A%0At1%24teambis%24mail%0A%5B1%5D%20%22team%40thinkr.fr%22%0At2%24teambis%24mail%0A%5B1%5D%20%22team%40thinkr.fr%22%0A%0At2%24teambis%24mail%20%3C-%20%22colin%40thinkr.fr%22%0At2%24teambis%24mail%0A%5B1%5D%20%22colin%40thinkr.fr%22%0At1%24teambis%24mail%0A%5B1%5D%20%22team%40thinkr.fr%22″ message=”” highlight=”” provider=”manual”/]
À savoir sur cette méthode clone
:
- Le deep clone ne copie que les éléments R6 (en plus des fonctions et données). Pour copier également les environnements et autres éléments avec références sémantiques, il est indispensable de passer par une méthode personnalisée.
- Il est possible d’empêcher la “clonabilité” (non, ce mot n’existe pas) de votre objet. Pour cela, il suffit d’ajouter `clonable = FALSE` à votre classe.
- La méthode `clone` ajoute 49.1 KB à la classe, mais seulement 112 bytes à chaque nouvel objet. Ce qui reste raisonnable en termes d’espace.
L’héritage
Ll’héritage est un concept clé de la programmation orientée objet (mais vous le saviez déjà , parce que vous avez lu notre billet sur le sujet 😉 ). Vous vous doutez que {R6} ne déroge pas à la règle.
Maintenant, on se dit qu’on aimerait bien créer des sous-classes, par exemple une pour définir les membres de l’équipe. Bien sûr, sans faire de copier-coller : certains champs peuvent être partagés par tout le monde, donc autant en profiter !
Pour définir l’héritage, rien de plus simple : nous avons besoin de… inherit = NomDeLObjet
!
[pastacode lang=”R” manual=”member%20%3C-%20R6Class(%22Member%22%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20inherit%20%3D%20team)%0A%0Acolin%20%3C-%20member%24new(Mail%20%3D%20%22colin%40thinkr.fr%22%2C%20Phone%20%3D%20%2201.85.09.14.03%22)%0A%0A%23%20Et%20maintenant%2C%20nous%20pouvons%20utiliser%20la%20fonction%20contact()%20d%C3%A9finie%20dans%20%60team%60%0A%0Acolin%24contact()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20Mail%20%20%20%20%20%20%20%20%20%20Phone%0A1%20colin%40thinkr.fr%2001.85.09.14.03%0A%60Classe%20cr%C3%A9%C3%A9e%20par%20ThinkR%60″ message=”” highlight=”” provider=”manual”/]
Le plus étant que les classes enfants peuvent, bien sûr, contenir des éléments supplémentaires, voire supplanter des méthodes des classes parentes.
[pastacode lang=”R” manual=”member%20%3C-%20R6Class(%22Member%22%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20inherit%20%3D%20team%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20public%20%3D%20list(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20contact%20%3D%20function()%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(data.frame(Mail%20%3D%20private%24privMail%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Phone%20%3D%20private%24privPhone%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20TwitterTeam%20%3D%20private%24privTwitterTeam))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20message(private%24privCopyright)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20initialize%20%3D%20function()%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(%22Hello%20!%22)%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20private%20%3D%20list(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20privTwitterTeam%20%3D%20%22%40thinkr_fr%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20privCopyright%20%3D%20%22Classe%20cr%C3%A9%C3%A9e%20par%20ThinkR%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20active%20%3D%20list(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20TwitterTeam%20%3D%20function()%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20private%24privTwitterTeam%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%0A)%0Acolin%20%3C-%20member%24new()%0A%5B1%5D%20%22Hello%20!%22%0Acolin%24TwitterTeam%0A%5B1%5D%20%22%40thinkr_fr%22%0A%0A%23%20Mais%20%0Acolin%24Phone%0ANULL%0A%23%20Et%20pourtant%20%0Acolin%24Phone%20%3C-%20%2201.85.09.14.03%22%0Acolin%24Phone%0A%5B1%5D%20%2201.85.09.14.03%22″ message=”” highlight=”” provider=”manual”/]
Eh oui, ici, notre fonction initialize
enfant a pris le dessus sur celle de notre classe parent. En clair : lorsqu’on fait appel à une méthode ou un objet d’une classe, R va chercher dans le scope de votre objet, l’exécuter s’il le trouve, et remonter l’arbre d’héritage si et seulement si il ne trouve pas ce que vous chercher dans l’objet enfant.
Nous sommes ici face à un problème : notre fonction initialize
prend le dessus sur la fonction de la classe parente. Autrement dit, impossible d’initialiser avec la méthode de la classe team
.
[pastacode lang=”R” manual=”colin%20%3C-%20member%24new(Mail%20%3D%20%22colin%40thinkr.fr%22%2C%20Phone%20%3D%20%2201.85.09.14.03%22)%0A%0AError%20in%20.subset2(public_bind_env%2C%20%22initialize%22)(…)%20%3A%20%0A%20%20arguments%20inutilis%C3%A9s%20(Mail%20%3D%20%22colin%40thinkr.fr%22%2C%20Phone%20%3D%20%2201.85.09.14.03%22)” message=”” highlight=”” provider=”manual”/]
Impossible dites-vous ? Pas avec l’utilisation de super$
.
Destiné à la gestion de l’héritage, ce symbole accède aux fonctions ou données de la classe parente. Par exemple, nous pouvons “pimper” la classe member
pour qu’elle utilise la méthode de sa classe mère. Profitons-en aussi pour ajouter des champs supplémentaires.
[pastacode lang=”R” manual=”member%20%3C-%20R6Class(%22Member%22%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20inherit%20%3D%20team%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20public%20%3D%20list(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20contact%20%3D%20function()%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(data.frame(Mail%20%3D%20private%24privMail%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Phone%20%3D%20private%24privPhone%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20TwitterTeam%20%3D%20private%24privTwitterTeam%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20TwitterPerso%20%3D%20private%24privTwitterPerso))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20message(private%24privCopyright)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20initialize%20%3D%20function(Mail%2C%20Phone%2C%20Twitter)%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(%22Hello%20!%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20super%24initialize(Mail%2C%20Phone)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20private%24privTwitterPerso%20%3C-%20Twitter%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20private%20%3D%20list(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20privTwitterTeam%20%3D%20%22%40thinkr_fr%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20privCopyright%20%3D%20%22Classe%20cr%C3%A9%C3%A9e%20par%20ThinkR%22%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20privTwitterPerso%20%3D%20NULL%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20active%20%3D%20list(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20TwitterTeam%20%3D%20function()%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20private%24privTwitterTeam%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20TwitterPerso%20%3D%20function(value)%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(missing(value))%20return(private%24privTwitterPerso)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%20if%20(stringi%3A%3Astri_detect_regex(str%20%3D%20value%2C%20pattern%20%3D%20%22%5E%40%22))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20private%24privTwitterPerso%20%3C-%20value%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20message(%22Erreur%20%3A%20le%20compte%20Twitter%20doit%20commencer%20par%20un%20%40%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%0A)” message=”” highlight=”” provider=”manual”/]
Testons donc un peu tout ça
[pastacode lang=”R” manual=”colin%20%3C-%20member%24new(Mail%20%3D%20%22colin%40thinkr.fr%22%2C%20Phone%20%3D%20%2201.85.09.14.03%22%2C%20Twitter%20%3D%20%22%40_colinfay%22)%0A%5B1%5D%20%22Hello%20!%22%0A%0Acolin%24contact()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20Mail%20%20%20%20%20%20%20%20%20%20Phone%20TwitterTeam%20TwitterPerso%0A1%20colin%40thinkr.fr%2001.85.09.14.03%20%20%40thinkr_fr%20%20%20%40_colinfay%0AClasse%20cr%C3%A9%C3%A9e%20par%20ThinkR%0A%0Acolin%24TwitterPerso%20%3C-%20%22_colinfay%22%0ALe%20compte%20Twitter%20doit%20commencer%20par%20un%20%40″ message=”” highlight=”” provider=”manual”/]
Bien, tout ça a l’air dans l’ordre !
Héritage multiple
Bon, puisqu’on y est, autant pousser le bouchon encore plus loin. Comment créer une chaine d’héritage ? C’est-à-dire, comme créer un grand-parent, un parent et un enfant, et que le plus jeune du lot puisse avec accès au plus âgé ?
Admettons que nous souhaitions une nouvelle classe qui contienne le compte Twitter de la team (donc qui héritera de la classe member
), mais sans avoir à spécifier à chaque fois le compte Twitter perso (obligatoire dans la classe member
). Une classe qui utilise pour cela la fonction initialize
de la classe team
, tout en conservant les données de member
. En clair, nous avons besoin d’hériter de tous les éléments de la classe member
, sauf de la fonction initialize
, qui est celle de la classe team
. Alors, on fait super$super$
, non ?
[pastacode lang=”R” manual=”light_member%20%3C-%20R6Class(%22LightMember%22%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20inherit%20%3D%20member%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20public%20%3D%20list(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20initialize%20%3D%20function(Mail%2C%20Phone)%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20super%24super%24initialize(Mail%2C%20Phone)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20))%0A%0Acolin_light%20%3C-%20light_member%24new(Mail%20%3D%20%22colin%40thinkr.fr%22%2C%20Phone%20%3D%20%2201.85.09.14.03%22)%0AError%20in%20.subset2(public_bind_env%2C%20%22initialize%22)(…)%20%3A%20%0A%20%20tentative%20d’appliquer%20un%20objet%20qui%20n’est%20pas%20une%20fonction” message=”” highlight=”” provider=”manual”/]
Eh non ! Par défaut, les classes {R6} on seulement accès aux éléments de leurs parents directs. Cependant, chaque classe peut exposer son parent, en passant par un active binding
.
Interlude : enrichir une classe
Nous devons enrichir notre classe team
d’une nouvelle méthode d'active binding
. Comment ça marche ? Prenons un exemple plus simple : ajouter un élément à la liste publique. Pour cela, nous devons faire appel à la méthode $set()
, qui permet d’ajouter, ou de remplacer (avec overwrite=TRUE
), un élément d’une classe déjà définie.
[pastacode lang=”R” manual=”team%24set(%22public%22%2C%20%22web%22%2C%20function()%20print(%22https%3A%2F%2Fthinkr.fr%22))%0A%0Athinkr%20%3C-%20team%24new(Mail%20%3D%20%22team%40thinkr.fr%22%2C%20Phone%20%3D%20%2201.85.09.14.03%22)%0Athinkr%24web()%0A%5B1%5D%20%22https%3A%2F%2Fthinkr.fr%22%0A%0Acolin%20%3C-%20member%24new(Mail%20%3D%20%22colin%40thinkr.fr%22%2C%20Phone%20%3D%20%2201.85.09.14.03%22%2C%20Twitter%20%3D%20%22%40_colinfay%22)%0A%5B1%5D%20%22Hello%20!%22%0Acolin%24web()%0A%5B1%5D%20%22https%3A%2F%2Fthinkr.fr%22″ message=”” highlight=”” provider=”manual”/]
Interlude : off
Ainsi donc, nous pouvons ajouter un élément à la liste active
de notre classe mère (member
), qui viendra exposer son parent (team
). Par convention, on utilise super_
, qui est une fonction qui renvoie tout simplement super
.
[pastacode lang=”R” manual=”member%24set(%22active%22%2C%20%22super_%22%2C%20function()%20super)%0A%0Alight_member%20%3C-%20R6Class(%22LightMember%22%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20inherit%20%3D%20member%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20public%20%3D%20list(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20initialize%20%3D%20function(Mail%2C%20Phone)%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20super%24super_%24initialize(Mail%2C%20Phone)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20))%0Acolin_light%20%3C-%20light_member%24new(Mail%20%3D%20%22colin%40thinkr.fr%22%2C%20Phone%20%3D%20%2201.85.09.14.03%22)%0Acolin_light%24Mail%0A%5B1%5D%20%22colin%40thinkr.fr%22%0Acolin_light%24Phone%0A%5B1%5D%20%2201.85.09.14.03%22%0Acolin_light%24TwitterTeam%0A%5B1%5D%20%22%40thinkr_fr%22%0Acolin_light%24TwitterPerso%0ANULL%0A%0A%23%20En%20cas%20de%20besoin%20%0Acolin_light%24TwitterPerso%20%3C-%20%22_colinfay%22%0A%60Erreur%20%3A%20le%20compte%20Twitter%20doit%20commencer%20par%20un%20%40%60%0A%23Oops%20%0Acolin_light%24TwitterPerso%20%3C-%20%22%40_colinfay%22%0Acolin_light%24TwitterPerso%0A%5B1%5D%20%22%40_colinfay%22″ message=”” highlight=”” provider=”manual”/]
Classe portables et non portables
Par défaut, {R6} crée des classes portables entre packages. Autrement dit, une classe créée dans un package sera accessible dans un autre, grâce au concept de portabilité. Derrière ce nom un brin barbare, on retrouve tout simplement l’idée que, lorsqu’une classe hérite d’une superclasse, elle hérite également de son environnement : la méthode est donc exécutée dans l’environnement parent, pas dans l’environnement enfant.
Pour empêcher cette fonctionnalité, il suffit d’indiquer portable=FALSE
dans la classe. Pourquoi l’empêcher ? Une classe non portable permet de ne pas avoir à utiliser self
pour faire référence aux objets internes. Les classes non portables affichent également des performances légèrement meilleures en termes d’exécution (cela dit, nous parlons ici de quelques microsecondes).
[pastacode lang=”R” manual=”classe_non_portable%20%3C-%20R6Class(%22NonPort%22%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20portable%20%3D%20FALSE%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20public%20%3D%20list(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20message%20%3D%20%22Pas%20besoin%20de%20self%22%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20initialize%20%3D%20function()%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(message)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%0A)%0A%0Aclasse_non_portable%24new()%0A%5B1%5D%20%22Pas%20besoin%20de%20self%22%20%0A%0Aclasse_portable%20%3C-%20R6Class(%22Port%22%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20portable%20%3D%20TRUE%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20public%20%3D%20list(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20message%20%3D%20%22Besoin%20de%20self%22%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20initialize%20%3D%20function()%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(self%24message)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%0A)%0Aclasse_portable%24new()%0A%5B1%5D%20%22Besoin%20de%20self%22″ message=”” highlight=”” provider=”manual”/]
Et en termes de performance ?
[pastacode lang=”R” manual=”library(microbenchmark)%0Amb%20%3C-%20microbenchmark(classe_non_portable%24new()%2C%20classe_portable%24new()%2C%20times%20%3D%2010000)%0Amb%0AUnit%3A%20microseconds%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20expr%20%20%20%20min%20%20%20%20%20%20lq%20%20%20%20%20mean%20%20median%20%20%20%20%20%20%20uq%20%20%20%20%20%20max%20neval%20cld%0A%20classe_non_portable%24new()%2064.568%2069.6985%20102.6591%2076.4940%20112.9245%202659.403%2010000%20%20a%20%0A%20%20%20%20%20classe_portable%24new()%2066.352%2071.5030%20105.8632%2078.4305%20110.4635%203568.485%2010000%20%20%20b%0Aautoplot(mb)” message=”” highlight=”” provider=”manual”/]
Oui, nous parlons vraiment ici de microsecondes…
Partager des données entre classes
Bien bien bien. Et sinon, comment partager des données entre toutes les classes ? Car oui, nous avons vu que les classes enfants héritaient de leurs parents, et pouvaient spécifier des éléments supplémentaires ou en modifier. Mais comment cela se passe si nous voulons changer un élément dans toutes les instances de classes, sans avoir à remonter l’arbre pour changer à chaque fois ? Pour cela, il suffit de créer un environnement dans une méthode shared
, et de partager cet environnement avec toutes les instances de la classe.
Ici, par exemple, nous allons rendre l’élément web
accessible à tous, mais aussi modifiable.
[pastacode lang=”R” manual=”team%20%3C-%20R6Class(%22Team%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20public%20%3D%20list(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20contact%20%3D%20function()%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(data.frame(Mail%20%3D%20private%24privMail%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Phone%20%3D%20private%24privPhone))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20message(private%24privCopyright)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20initialize%20%3D%20function(Mail%2C%20Phone)%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20private%24privMail%20%3C-%20Mail%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20private%24privPhone%20%3C-%20Phone%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20finalize%20%3D%20function()%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20message(%22Merci%20d’avoir%20utilis%C3%A9%20notre%20classe%20R6%20!%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20private%20%3D%20list(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20privMail%20%3D%20NULL%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20privPhone%20%3D%20NULL%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20privCopyright%20%3D%20%22Classe%20cr%C3%A9%C3%A9e%20par%20ThinkR%22%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20shared%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20env%20%3C-%20new.env()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20env%24web%20%3C-%20%22https%3A%2F%2Fthinkr.fr%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20env%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20active%20%3D%20list(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Mail%20%3D%20function()%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20private%24privMail%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Phone%20%3D%20function(value)%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(missing(value))%20return(private%24privPhone)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%20if%20(stringi%3A%3Astri_detect_regex(str%20%3D%20value%2C%20pattern%20%3D%20%22(%5B0-9%5D%7B2%7D%5C%5C.%3F)%7B5%7D%22))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20private%24privPhone%20%3C-%20value%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20message(%22Le%20format%20du%20num%C3%A9ro%20de%20t%C3%A9l%C3%A9phone%20est%20incorrect%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20web%20%3D%20function(value)%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(missing(value))%20return(private%24shared%24web)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20private%24shared%24web%20%3C-%20value%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%0A)%0A%0Athinkr%20%3C-%20team%24new(Mail%20%3D%20%22team%40thinkr.fr%22%2C%20Phone%20%3D%20%2201.85.09.14.03%22)%0Acolin%20%3C-%20member%24new(Mail%20%3D%20%22colin%40thinkr.fr%22%2C%20Phone%20%3D%20%2201.85.09.14.03%22%2C%20Twitter%20%3D%20%22%40_colinfay%22)%0Acolin_light%20%3C-%20light_member%24new(Mail%20%3D%20%22colin%40thinkr.fr%22%2C%20Phone%20%3D%20%2201.85.09.14.03%22)%0A%0Athinkr%24web%0A%5B1%5D%20%22https%3A%2F%2Fthinkr.fr%22%0Acolin%24web%0A%5B1%5D%20%22https%3A%2F%2Fthinkr.fr%22%0Acolin_light%24web%0A%5B1%5D%20%22https%3A%2F%2Fthinkr.fr%22%0A%0Acolin_light%24web%20%3C-%20%22https%3A%2F%2Fthinkr.fr%2Fblog%2F%22%0Athinkr%24web%0A%5B1%5D%20%22https%3A%2F%2Fthinkr.fr%2Fblog%2F%22%0Acolin%24web%0A%5B1%5D%20%22https%3A%2F%2Fthinkr.fr%2Fblog%2F%22%0Acolin_light%24web%0A%5B1%5D%20%22https%3A%2F%2Fthinkr.fr%2Fblog%2F%22%0A” message=”” highlight=”” provider=”manual”/]
Easy peasy, n’est-ce pas ?
Impression customisée
Enfin (et ça sera tout pour aujourd’hui), il est possible de customiser le comportement de votre objet lorsque vous tapez son nom dans la console : ce qui est, rappelons-le, l’équivalent de taper print(objet)
. Pour ce faire, vous pouvez spécifier une méthode print
au sein de votre objet.
[pastacode lang=”R” manual=”%23%20Comportement%20par%20d%C3%A9fault%0Acolin_light%0A%3CLightMember%3E%0A%20%20Inherits%20from%3A%20%3CMember%3E%0A%20%20Public%3A%0A%20%20%20%20clone%3A%20function%20(deep%20%3D%20FALSE)%20%0A%20%20%20%20contact%3A%20function%20()%20%0A%20%20%20%20finalize%3A%20function%20()%20%0A%20%20%20%20initialize%3A%20function%20(Mail%2C%20Phone)%20%0A%20%20%20%20Mail%3A%20active%20binding%0A%20%20%20%20Phone%3A%20active%20binding%0A%20%20%20%20super_%3A%20active%20binding%0A%20%20%20%20TwitterPerso%3A%20active%20binding%0A%20%20%20%20TwitterTeam%3A%20active%20binding%0A%20%20%20%20web%3A%20active%20binding%0A%20%20Private%3A%0A%20%20%20%20privCopyright%3A%20Classe%20cr%C3%A9%C3%A9e%20par%20ThinkR%0A%20%20%20%20privMail%3A%20colin%40thinkr.fr%0A%20%20%20%20privPhone%3A%2001.85.09.14.03%0A%20%20%20%20privTwitterPerso%3A%20NULL%0A%20%20%20%20privTwitterTeam%3A%20%40thinkr_fr%0A%20%20%20%20shared%3A%20environment%0A%0A%23%20Comportement%20customis%C3%A9%20%0Alight_member%20%3C-%20R6Class(%22LightMember%22%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20inherit%20%3D%20member%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20public%20%3D%20list(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20initialize%20%3D%20function(Mail%2C%20Phone)%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20super%24super_%24initialize(Mail%2C%20Phone)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print%20%3D%20function()%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(self%24Mail)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(self%24Phone)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20))%0A%0Acolin_light%20%3C-%20light_member%24new(Mail%20%3D%20%22colin%40thinkr.fr%22%2C%20Phone%20%3D%20%2201.85.09.14.03%22)%0Acolin_light%0A%5B1%5D%20%22colin%40thinkr.fr%22%0A%5B1%5D%20%2201.85.09.14.03%22″ message=”” highlight=”” provider=”manual”/]
Bien, on a pas mal bossé aujourd’hui, et nous sommes entrés dans des modalités (très) avancées de la programmation avec {R6}. Il nous restera quelques concepts à voir, mais on en reparlera une autre fois. En attendant, n’hésitez pas à mettre les mains dans la mécanique, et si vous rencontrez des soucis, envoyez nous un tweet !
Laisser un commentaire