La Programmation Orientée Objet en Dart

Dart est un langage orienté objet. Il prend en charge les fonctionnalités de programmation orientées objet (POO) telles que les classes, les interfaces, etc. Une classe en termes de POO est un modèle pour la création d’objets. Cette façon de programmer permet une meilleure structuration du code et augmente sa réutilisabilité ainsi que pleins d’autres avantages.

Salut, je suis ravis de te retrouver pour une nouvelle partie de ton apprentissage sur Flutter. Nous attaquons aujourd’hui la Programmation Orientée Objet en Dart, un des chapitres les plus importants pour bien programmer en Dart. Je t’invite à voir les bases de Dart, un autre chapitre aussi important sur quoi, ce chapitre s’appuie.

##Petit rappel
- C'est quoi une classe ?
Une classe est une entité logicielle qui représente, dans un système informatique, une entité existant dans la vraie vie (Ex: un chat, un client, etc.). Une classe est aussi un modèle ou une maquette à partir de quoi l'on pourra créer des objets. L'opération qui consiste à créer un objet à partir d'une classe s'appelle l'instanciation. Ainsi, on peut instancier plusieurs objets à partir d'une classe.

- C'est quoi un objet ?
Un objet est un élément concret créé à partir d'une classe. Cet objet est une occurence d'un ensemble d'entité de la vraie vie qu'une classe est chargée de matérialiser dans un système informatique (Ex: l'élève avec le matricule 001 est un objet de la classe "Eleve"). 

NB : nous nous efforçons d’utiliser des termes simples pour expliquer ce concept dans cet article. Si vous êtes intéressés par un cours d’initiation au concept d’orienté objet, nous vous donnons rendez-vous dans un prochain article qui traitera ce sujet plus en détails.

Les classes en Dart

La déclaration d’une classe en Dart se fait de la façon suivante :

Dart
class ClasseName {
//Les attributs
  type var1;
  type var2;
  
//Le constructeur
  classeName(){}
  
//Les méthodes
  void fonction1(){...}
  int fonction1(){...}

//Le accesseurs
  //Les getters
  type get getVar1 { return var1; }
  ...
  
  //Les setters  
  set setVar1(type var1) { this.var1 = var1; }  
  ...
}

Une classe est donc constituée de trois types d’éléments. Les constructeurs, les attributs et les méthodes.

  • Les attributs sont des variables qui peuvent être de tout type. Elles permettent de décrire une classe et sont utilisées pour stocker des informations dans la classe.
  • Les méthodes sont des fonctions, comme vu dans l’article Débute avec le langage Dart. Elles sont utilisées pour effectuer des actions particulières sur les attributs déclarées dans la classe.
  • Quant-aux constructeurs, ce sont des « méthodes particulières » qui permet de « construire » ou « fabriquer » un objet. C’est la première « méthode » qui s’exécute quand on instancie une classe.
  • Les accesseurs qui sont des méthodes qui s’appliquent exclusivement sur les attributs de la classe, contrairement au méthodes qui peuvent être utilisées pour produit d’autre types d’actions.

Exemples de classe :

Dart
//Déclaration de la classe chat
class Chat{
  String? nom;
  String? couleur;
  int? age;
  
  Chat(String? nom, String? couleur, int? age){
    this.nom = nom;
    this.couleur = couleur;
    this.age = age;
  }
  
  void mioler(){
    print("$nom miaou");
  }
  
  void manger(String nourriture){
    print("Miam, je mange $nourriture");
  }
  
  void marcher(){...}
}

Ainsi, le code ci-dessus nous montre comment déclarer. Nous traiterons les différentes parties de ce code en détail.

Notre classe Chat est composée :

  • D’attributs (nom, couleur et âge) : qui sont des variables qui permettent de décrire comment un chat est représenté dans notre système. Ainsi, les chats qui seront instanciés à partir de notre classe auront tous un nom, une couleur et un age.
  • De méthodes ou fonctions (mioler, manger et marcher) : qui permettent d’effectuer un traitement particulier sur notre objet. Les méthodes sont utilisées de façon générale pour décrire ou donner un comportement que peuvent avoir les objets d’une classe dans notre système. Dans notre cas, nos chats issus de notre classe peuvent tous mioler, manger et marcher par exemple.
  • Un constructeur : le constructeur nous permet de former ou fabriquer un objet. C’est cette méthode particulière qui se chargera de donner une forme particulière aux objet issus de notre classe. Une classe en Dart peut avoir plusieurs constructeurs en son sein. Dans notre exemple, notre constructeur est
Dart
Chat(String? nom, String? couleur, int? age){
    this.nom = nom;
    this.couleur = couleur;
    this.age = age;
  }

Ainsi voici un exemple qui montre comment créer un objet en Dart :

Dart
//Création d'un objet chat à partir de la classe Chat
void main(){
  Chat milou = Chat("Milou", "Gris", 5);
  milou.manger("du lait"); //Sortie "Miam, je mange du lait"
  milou.mioler(); //Sortie "Milou miaou" 
}

Nous nous intéresserons plus en détails aux constructeurs dans la suite.

Les constructeurs

En Dart on peut déclarer un constructeur de deux façons.

  • Le constructeur basique
Dart
//Exemple 1
classeName(){...}
  
//Exemple 2
classeName(type[?] var1, type[?] var2,...){...} 
//[?] n'est pas obligatoire.

NB : utiliser le symbole ? marque que le paramètre peut contenir la valeur null. Si vous ne précisez pas le symbole dans la déclaration cela veut dire que le paramètre ne sera jamais null. Voir l’article sur le Null-safety en Dart pour plus de détails.

  • Le constructeur nommé
Dart
//Exemple 1
classeName.foo(){...}

//Exemple 2
classeName.foo(type[?] var1, type[?] var2,...){...} 
//[?] n'est pas obligatoire.

Ainsi, avec les constructeurs nommés en Dart, on peut déclarer plusieurs constructeurs dans une même classe qui remplisse différentes fonction au sein de la même class, contrairement aux constructeurs basiques.

Exemples :

Dart
//Déclaration de la classe chat
class Chat{
  String? nom;
  String? couleur;
  int? age;
  
  //Constructeur basique sans paramètres #1
  Chat();
  //Ou
  //Constructeur basique avec paramètres #2
  Chat(String? nom, String? couleur, int? age){
    this.nom = nom;
    this.couleur = couleur;
    this.age = age;
  }
  
  //Constructeur nommé avec paramètres #3
  Chat.nom(String? nom){
    this.nom = nom;
  }

  //Constructeur nommé sans paramètres #4
  Chat.getInstance(){}
}

La construction et l’appel d’un objet de cette classe sera fera comme suit :

Dart
//Création d'un objet chat à partir de la classe Chat
void main(){
  //Utilisation du constructeur #1
  Chat c1 = Chat();
  
  //Utilisation du constructeur #2
  Chat milou = Chat("Milou", "Gris", 5);
  
  //Utilisation du constructeur #3
  Chat c2 = Chat.nom("c2");
  
  //Utilisation du constructeur #3
  Chat c3 = Chat.getInstance();
}

NB : comme pour les fonctions, on peut aussi utiliser les différents types de paramètres pour déclarer nos paramètres de constructeurs. Je vous invite à voir la section Les fonctions en Dart de l’article Débute avec le langage Dart.

Encapsulation et notion de visibilité

L'encapsulation est un mécanisme consistant à rassembler les données et les méthodes au sein d'une structure en cachant l'implémentation de l'objet, c'est-à-dire en empêchant l'accès aux données par un autre moyen que les services proposés. 
source : DataScientest
Illustration d’encapsulation

Ce mécanisme est mis en oeuvre à travers la notion de visibilité. La notion de visibilité désigne la portée qu’a un « littéral » (variable, attribut ou méthode) dans un programme informatique. En d’autres termes, il s’agit de savoir jusqu’où une variable, un attribut ou une méthode peut être accessible (en lecture et en écriture). Ce principe, en POO, est utilisé pour mettre en place une certaine sécurité sur les attributs et méthodes d’une classe.

Il existe plusieurs type de visibilité en POO notamment la visibilité

  • Public
  • Private
  • Package
  • Protected

Le langage Dart ne met en oeuvre que deux types de visibilités : Public et Private.

Dart
class Chat {
  String? nom; //Déclaration d'un attribut public
  String? _couleur; //Déclaration d'un attribut private
  
  void mioler(){} //Déclaration d'une méthode public
  
  void _manger(String nourriture){...} //Déclaration d'une méthode private
}

Comme vous pouvez le voir. Par défaut, les attributs et méthodes déclarés sont public. Pour rendre un attribut ou une méthode privée, vous devez préfixer le nom du « littéral » d’un _ (UNDERSCORE).

Il est important de préciser que cette notion s’applique aussi bien aux attributs, aux méthodes qu’aux classes elles-même ! On peut donc rendre une classe privée de la sorte

Dart
class _Chat{...}

Une fois marqué privé, le littéral ne pourra être accessible que dans l’étendu de son conteneur (Ex : la classe ou le fichier). Pour y accéder en dehors, vous pouvez utiliser les accesseurs.

Les accesseurs (getters & setters)

En POO, les accesseurs désignent des méthodes qui permettent d’accéder aux attributs privés d’une classe en lecture et écriture. Il y en a de deux types, les getters et les setters.

  • Les getters

Les getters sont des fonctions qui servent essentiellement à lire la valeur d’un attribut privé d’une classe. La déclaration en Dart se fait comme suit :

Dart
class Chat {
  ...
  
  String? _couleur; //Déclaration d'un attribut private
  
  //Déclaration d'un getter
  String? get couleur => _couleur; 
  //Ou
  String? get couleur { return _couleur; }
  
  ...
}
  • Les setters

Les setteurs sont aussi des fonctions qui servent essentiellement à assigner une valeur aux attributs privés. La déclaration en Dart se fait comme suit :

Dart
class Chat {
  ...
  
  String? _couleur; //Déclaration d'un attribut private
  
  //Déclaration d'un setter
  set couleur(String? couleur) => _couleur = couleur; 
  //Ou
  set couleur(String? couleur) { _couleur = couleur; }
  
  ...
}
  • Utilisation ou appel d’un getter ou setter
Dart
class Chat {
  ...
  
  String? _couleur; //Déclaration d'un attribut private
  
  String? get couleur => _couleur; 

  set couleur(String? couleur) { _couleur = couleur; }
  
  ...
}

void main(){
  Chat minou = Chat();
  
  print(minou._couleur); //Erreur
  
  print(minou.couleur); //Appel le getter
  
  minou.couleur = "Gris"; //Appel le setter
  
  print(minou.couleur);
  //Sortie
  Gris
}

L’héritage

L’héritage est le mécanisme par lequel on créé une classe B à partir d’une autre classe existante A. On dit alors que la classe B hérite de la classe A. Ainsi, la classe A est appelé « classe parente » ou « super classe » et la classe B est la « classe enfant » ou « sous classe ». L’héritage en Dart se fait en utilisant le mot clé extends.

Dart
//Classe parente
class Animal{...}

//Classe enfant
class Chat extends Animal{...}

NB : Dart ne supporte pas l’héritage multiple.

Ainsi, la classe Chat hérite de Animal. Chat hérite alors de tous les attributs et méthodes public de Animal.

Dart
class Animal{
  String? nom;
  
  void respirer(){ print("$nom respire."); }
}

//Classe enfant
class Chat extends Animal{}

void main(){
  Chat minou = Chat();
  minou.nom = "Minou";
  minou.respirer();
  //Sortie
  Minou respire.
}

Notion de polymorphisme

Le nom de polymorphisme vient du grec et signifie qui peut prendre plusieurs formes. Cette caractéristique est un des concepts essentiels de la programmation orientée objet. Alors que l'héritage concerne les classes (et leur hiérarchie), le polymorphisme est relatif aux méthodes des objets.
Source : CommentCaMarche.net

En terme clair, le polymorphisme est la faculté qu’une méthode a de s’exécuter différemment selon le contexte dans lequel elle est appelée. Exemple :

Dart
class FormeGeometrique {
  double calculAir(){...}
}

class Carre extends FormeGeometrique{
  double cote;
  
  Carre(this.cote);
  
  @override
  double calculAir(){ return cote * cote; }
}

class Cercle extends FormeGeometrique{
  double rayon;
  
  Cercle(this.rayon);
  
  @override
  double calculAir(){ return 3.14 * rayon * rayon; }
}


void main(){
  Carre c1 = Carre(4);
  print(c1.calculAir()); //Sortie 16;
  
  Cercle c2 = Cercle(2); 
  print(c2.calculAir()); //Sortie 12,56
}

Méthode et attributs statiques

Le mot-clé static s’applique sur les données membres d’une classe (attributs et méthodes). Un attribut statique conserve sa valeur jusqu’à la fin de l’exécution du programme. Les attributs et méthodes statiques sont référencés par le nom de la classe. Exemple :

Dart
class Constantes {
  static int version = 12;
  static void direBonjour(){
    print("Bonjour");
  }
}


void main(){
  print(Constantes.version); //Sortie 12
  Constantes.direBonjour(); //Sortie Bonjour
}

Super et this

Super et this sont deux mots clés utilisés dans le contexte des classes en POO.

  • Super : est utilisé, à l’intérieur d’une classe, pour accéder aux attributs, méthodes et constructeur publiques de la classe parentes.
Dart
class Animal {
  String nom;
  
  Animal(this.nom);
  
  void crier()=> print("Je crie");
}

class Chat extends Animal{
  //Initialisation de la variable nom en appelant le constructeur de Animal
  Chat(String nom) : super(nom);
  
  @override
  void crier(){
    super.crier();
    print("Je miole");
  }
}

class Chien extends Animal{
  //Initialisation de la variable nom en appelant le constructeur de Animal
  Chien(String nom) : super(nom);
  
  @override
  void crier(){
    super.crier();
    print("J'aboie");
  }
}

void main(){
  Chat minou = Chat("Minou");
  minou.crier();
  //Sortie
  Je crie
  Je miole
  
  
  Chien flash = Chien("flash");
  flash.crier();
  //Sortie
  Je crie
  J'aboie
}
  • This : le mot clé this fait référence à l’instance actuelle de la classe.
Dart
class Chat {
  String nom;
  //Initialisation de la variable nom en appelant le constructeur de Animal
  Chat(String nom){
    this.nom = nom;
  }
  
  @override
  void crier(){
    super.crier();
    print("Je miole");
  }
}

Ici, le nom du paramètre nom et le nom du champ nom de la classe sont identiques. Par conséquent, pour éviter toute ambiguïté, le champ de la classe est préfixé par le mot-clé this. L’exemple suivant explique la même chose.

Ceci marque la fin de cet article. J’espère que vous avez trouvé plaisir le lire. Pour toutes questions, remarques ou suggestions vous pouvez me contacter ou laisser cela en commentaire. Allez, on se dit à très bientôt pour un nouvel article. Peace 😉!

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *