3.1 Introduction aux objets
Dans l'introduction de la partie précédente, nous avions présenter les objets de façon un peu vague, comme un ensemble d'information en mémoire qui représentent quelque chose pour nous. Cette chose peut-être un nombre, une chaine de caractére, une fonction, etc. Maintenant que nous connaissons les objets basiques, nous allons essayer dans ce chapitre de mieux comprendre ce concepte d'objet. Les objets sont un concept essentiel en programmation, mais parfois un peu difficile à comprendre pour les débutants. Au début ce que nous allons dire sur les objets va vous sembler un peu éloigné de ce que nous avons vu dans la partie précédente. Au fur et à mesure des explications, le lien se mettra en place progressivement.
Qu'est ce qu'un objet ?
Commençons par présenter le concept d'un objet dans le langage de programmation R. Un objet en R est un rassemblement de variables et de fonctions, qui décrivent quelque chose. c'est comme une sorte de "boite" qui contient différentes variables et fonctions qui représente un même concept ou objet. Voici un schéma qui représente une telle boite :
Qu'est ce que cela signifie que les variables et fonctions de la boite décrivent notre objet ? Pour y voir plus clair, prennons un exemple simplifié. Essayons de décrit le schéma d'un objet chat
, comme l'animal. L'idée est de proposer des variables et des fonctions qui vont ensemble pour nous aider à décrire le concept d'un chat. En regroupant ces variables et fonctions ensemble, cela permet de signaler qu'elles représentent une certaine cohérence, en décrivant le même concept de "chat".
Décrivons un chat !
Commençons par lister un certain nombre de variables que nous pourrions utiliser pour décrire le concept d'un chat. Un chat dispose de nombreuses caractéristiques différentes. En voici quelques une qui sont évidentes. Pour chaque variable, nous indiquons son nom, et le type de donnée qu'elle contiendra.
prenom
qui serait uncharacter
et stockera le prenom de notre chat.age
qui serait uninteger
ou undouble
et stockerai l'âge de notre chat.male
qui serait unlogical
et vaudraitTRUE
si c'est notre chat est mâle, etFALSE
sinon.faim
, une variable qui représenterait l'effet de faim de notre chat et qui serait uninteger
. Plus ce nombre est grand, plus cela indique que notre chat est affamé.sante
, une variable de typeinteger
qui indique la santé de notre chat. Cela serait en quelque sorte le nombre de points de vies de notre chat. Si notre chat n'a plus de points de santé, il meurt !
Avec ces variables nous pouvons assez bien représenter les principaux aspects d'un chat numérique. Avec uniquement des variables, notre chat serait une simple description. Nous pourrions nous arrêtez là, et considérer que rassembler ensemble nos variables décrit suffisament bien notre objet "chat".
En général on ajoute également des fonctions dans notre boite, qui nous servent à intéragir avec notre objet. Elles peuvent par exemple modifier les variables de l'objet, les afficher, etc.
Voici quelques fonctions que nous pourrions ajouter à notre boite "chat" pour améliorer la description :
- Une fonction
miauler()
qui afficherait"Meaaawww"
dans la console. Qui imaginerai un chat qui ne miaule pas ? - Une seconde fonction
manger()
qui modifierai la variablefaim
de notre chat et augmenterai aussi sasante
. Cette fonction prendrait un nombre en paramétre et augmenterai la faim et la santé du nombre en question.
Là aussi, nous pourrions envisager une inifinité de fonctions à ajouter : sauter, ronronner, boire (avec une variable soif associée), etc. Nous nous contentons des deux que nous venons de décrire pour garder notre exemple simple.
Voici un nouveau shéma qui représente notre belle boite "chat" avec les variables et fonctions que nous y avons ajoutée :
Regrouper ensemble toutes ces variables et fonctions est plutôt logique et cohérent. Tous servent à décrire les caractéristiques et le comportement de notre objet "chat". Tous les objets en R suivent donc ce même schéma : ils consistent en une "boite" qui contient des variables et fonctions décrivant ensemble un certain concept.
C'est d'ailleurs pour cela qu'on parle de programmation orientée objet : on décrit des objets à l'aide de variables et de fonctions un peu comme on décrirait un objet dans la vie réelle : "un chat à 4 pattes, 2 oreilles, il ronronne, etc". On décrit ainsi un objet par ses caractéristiques (à l'aide des variables) et son comportement (les fonctions).
Un peu de vocabulaire
Avant d'aller plus loin, posons un petit peu de vocabulaire. Le but n'est pas de vous alourdir l'esprit avec des mots compliqués, mais de vous apprendre le vocabulaire courant en programmation. Bien nommer les choses, c'est un premier pas vers une meilleure compréhension.
La notion de classe
Le schéma qui désigne les différentes variables et fonctions qui décrivent un objet est appellée la classe
(class
en anglais). Quand on parle d'un objet de la classe chat, cela veut dire "je fais référence à un objet, qui est décrit par cette liste précise de variables et de fonctions". Exactement que comme dans la vraie vie si je vous montre un chat dans la rue, vous comprennez que je fais référence à un animal qui correspond au concept "petit félin domestique généralement gentil et ronronant".
Il y a parfois un petit abus de langage : la classe designe la structure d'un objet, comme l'idée platonicienne d'un chat. Néanmoins en pratique on dirait parfois qu'on parle d'un objet chat, sous entendu "un objet de la classe chat". Dans la vie courante, quand nous disons "regarde un chat", nous faisons à la fois référence à ce chat en particulier, mais aussi au concept général de "chat" dont ce chat particulier est la manifestation. Et bien c'est pareil en programmation !
Continuons avec nos précisions de vocabulaire.
Attributs et méthodes
Les variables et fonctions qui décrivent un objet ne s'appellent pas des variables et des fonctions. Il existe là aussi des termes spécifiques. Pour les variables, on parle des attributs
d'un objet. De même, ses fonctions sont désignés par le terme methodes
. Cela nous permet de faire la différence avec les variables et fonctions que l'on créer nous mêmes et qui n'appartiennent à aucun objet autre que eux-mêmes.
Remarque : En parlant des attributs, tous les objets ont au moins un attribut class
(ou d'un nom équivalent, comme className
) qui renseigne le nom de leur classe, ainsi qu'un attribut type
qui renseigne leur type. Dans notre exemple de description de la classe chat
, nous avions volontairement omis de montrer ces attributs, pour rendre le schéma plus lisible.
Voici un nouveau schèma mis à jour qui illustre ce qu'est un objet avec le vocabulaire mis à jour :
On y retrouve bien une "boite" qui contient différents attributs et méthodes qui décrivent notre objet. Avec un attribut className
qui indique la classe à laquelle appartient notre objet, et un attribut type
qui indique le type de l'objet.
Retour sur les objets simples
Nous avons bien compris les bases du concept d'objet : c'est un rassemblement de variables (appellés attributs) et de fonctions (appellés méthodes) qui décrivent un même concept. Maintenant essayons de faire le lien avec les objets que nous avons déjà rencontrés. Tous les éléments que nous connaissons jusque là (les vecteurs, les listes, les fonctions, etc.) sont des objets qui suivent le schéma que nous venons de voir. Pourtant, je parie que cela ne vous semble pas évident. En quoi un vecteur est bien un rassemblement d'attributs et de méthodes ? Un vecteur ne contient-il pas uniquement des chiffres (ou des booleans ou chaines) ?
Prennons un vecteur de nombres réels (double
) et voyons comment le comprendre comme un objet. Examinons le vecteur c(3.14, -4)
. Voici un shéma simplifié pour vous donner une idée de la structure de cet objet. Ce schéma indique pour chaque attribut le nom de l'attribut et sa valeur, ainsi que certaines méthodes de notre vecteur.
Quand nous créeons un vecteur en R, notre ordinateur ne met pas simplement que les composantes du vecteur dans notre boite. Il y ajoute aussi d'autres variables et fonctions. Le contenu de notre vecteur (ici, les nombres 3.14
et -4
) est stocké lui dans un "attribut interne", qui est ce que nous prennons en général pour le vecteur. Mais il existe d'autres attributs et fonctions !
En plus de cet attribut interne, un vecteur dispose toujours de trois autres attributs :
- Un attribut
length
, qui contient le nombre d'éléments de notre vecteur. - Un attribut
type
, qui contient la nature des données contenues dans l'attribut interne du vecteur. - Un attribut
class
, qui contient le nom de la classe du vecteur. Nous reviendrons juste après sur pourquoi la classe ici est "numeric" et pas "double" comme on pourrait s'y attendre. Oublions ce détail pour l'instant.
Cela nous permet de comprendre que quand nous affichons la longueur d'un vecteur, en réalité R ne vas pas l'inventer ou la calculer sur le moment : il va simplement regarder dans la boite et nous donner l'attribut length
qui décrit cette longueur.
C'est justement le rôle des différentes méthodes que nous permettre d'agir sur les attributs du vecteur. Ainsi la fonction length()
va récupérer la valeur de l'attribut length
de la boite vecteur dans laquelle elle se trouve, et nous renvoyer le résultat !
On retrouve aussi les fonctions typeof()
et class()
, qui renvoient simplement les valeurs des attributs type
et class
. Quand nous utilisions cette fonction typeof()
dans les chapitres précédent, R allait simplement nous renvoyer la valeur de l'attribut type
de l'objet en question !
La fonction mean()
que nous connaissons depuis la partie II, chapitre 11, est aussi présente. C'est par ce que les objets vecteurs disposent d'une fonction mean()
que nous pouvons faire la moyenne des compostants d'un vecteur ! Remarquez que faire une moyenne de character
n'aurait pas de sens. Cette fonction n'existe donc pas dans la boite des vecteurs de chaines ! Ou plus précisement, elle existe mais renvoit simplement une erreur.
Il existe de nombreuses autres fonctions dans la boite d'un vecteur de type double
: la fonction c()
qui ajoute des vecteurs, mais aussi toutes les fonctions de conversion, etc. Tous les fonctions qui marchent avec des vecteurs de ce type se retrouvent ainsi dans cette boite !
Normalement vous devriez mieux avoir une idée de comment les éléments que nous connaisons suivent le même schéma que tous les objets : ils sont composés d'attributs et de méthodes.
Objetception
Une remarque qui peut vous venir à l'esprit quand vous commencez à comprendre ce concept d'objet, c'est de vous demander ce que sont les attributs des objets. L'attribut length
de notre vecteur c(3.14, -4)
n'est-il pas lui même une variable, et donc un objet ? Avec ses propres propriétés et méthodes ?
C'est bien le cas ! On pourrait ainsi dessiner un schéma sans fin, ou chaque objet compose des objets qui en composent d'autres :
C'est exactement de cette façon qu'il faut voir les élements que l'on manipule en R ! Chaque objet est composé de différents attributs et méthodes, qui sont eux mêmes des objets, avec leur propres propriétés et méthodes...
Cette façon de voir les choses est extremement puissante, car elle nous permet d'enchainer les opérations sur les élements différents. Regardons le code suivant :
typeof(length(c(3.14, -4)))
Ce code revient à demander l'attribut type
de l'attribut length
du vecteur c(3.14, -4)
. Et ce code fonctionne ! Il nous affiche le bon résultat, qui est "integer"
(comme vous pouvez aussi le trouver à l'aide du schéma précédent).
Remarque pratique : En réalité R ne créer pas directement tous les objets imbriqués les uns dans les autres, sinon il y en aurait une infinité. Il ne créer que les objets que l'on utilise directement, et "simule" les autres. Il ne vas les créer que si on les utilise réellement. Décrire précisement comment R gère les objets "sous le capot" nous emporterais bien trop loin des objectifs de ce cours d'introduction !
Décorticons une fonction
Attaquons nous maintenant à comprendre la structure d'une objet en apparence plus complexe : une fonction. Que ce passe-il quand nous écrivons le code suivant ? Quelle boite R d'attributs et méthodes R va il creer ?
carre = function(x){
return x^2
}
R va alors créer un objet qui aura plus ou moins le schéma suivant :
Là encore, le code entier de notre fonction est stocké dans un attribut interne (comme pour nos vecteurs). Quand on veut appeler notre fonction en écrivant son nom suivit de parentheses (carre(5)
par exemple), R va alors chercher le code contenu dans l'attribut interne de la fonction, et l'exécuter. On peut afficher directement l'attribut d'une fonction en écrivant le nom de la fonction, sans utiliser les parenthèses pour éxécuter ce code :
print(carre)
La console nous affiche bien le code de la fonction contenue dans notre variable !
Nous ne détaillerons pas ici les différentes méthodes qui sont associées à un objet fonction. Nous en présenterons certaines dans les parties suivantes, lors du chapitre consacré aux conceptes avancés de manipulation de fonctions.
Conclusion
Nous avons vu dans ce chapitre ce que sont réellement les objets : un objet est toujours une collection d'attributs et de variables, qui décrivent un même concept. Tous les élements que nous avons vu jusque là (vecteurs, listes, fonctions, etc) sont en réalité des objets de cette forme.