Blog ENI : Toute la veille numérique !
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
  1. Livres et vidéos
  2. Java et Eclipse
  3. Concepts de base de la POO
Extrait - Java et Eclipse Développez une application avec Java et Eclipse (2e édition)
Extraits du livre
Java et Eclipse Développez une application avec Java et Eclipse (2e édition)
2 avis
Revenir à la page d'achat du livre

Concepts de base de la POO

Introduction

La Programmation Orientée Objet, ou POO, correspond à un paradigme - la représentation d’un système ou d’un univers, pour simplifier - particulier de la programmation informatique dont l’objet est le concept central. Les concepts fondamentaux de la POO sont présentés dans ce chapitre en s’attachant à l’essentiel et en se focalisant sur le langage Java. Ils sont pour la plupart mis en œuvre et approfondis dans le projet, facilitant ainsi leur compréhension.

L’intérêt de la programmation orientée objet est de fournir des moyens de modélisation des différents univers nécessaires à vos applications.

Ce paradigme permet aussi de minimiser et rationaliser les impacts dus à des modifications plus ou moins grandes des applications au cours de leur cycle de vie, d’étendre plus facilement leurs fonctionnalités, d’isoler les responsabilités du code, de manipuler des abstractions sans forcément connaître leurs types concrets, et donc plus globalement de proposer des logiciels plus robustes et ouverts au changement.

Quand on crée un programme en langage objet, on cherche à modéliser de manière informatique différentes notions pour pouvoir les réutiliser ailleurs. Il n’est pas question ici de modéliser tout...

Objet

Dans le paradigme objet, tout est objet. Une fenêtre, un bouton mais aussi une facture, un client, etc. Plus concrètement, un objet peut être considéré comme une entité possédant des caractéristiques et des possibilités d’action. Par exemple, une voiture a une forme et une couleur et peut rouler et s’arrêter.

Un chat a également plusieurs caractéristiques : le nombre de pattes, une couleur particulière de fourrure, un nom, et plusieurs comportements : manger, ronronner, courir sur les murs, se cacher, se jeter entre les jambes…

Dans le paradigme objet, Filou, votre chat, est représenté par un objet, une instance particulière de la classe Chat. Cet objet a donc des propriétés spécifiques à votre chat : son nom, sa couleur, son âge, pour commencer. Il a également des propriétés communes à tous les chats, comme quatre pattes, deux yeux, le sang chaud...

Un chat - et tous les chats - est également un mammifère et plus génériquement un animal, et encore plus génériquement un être vivant. Le paradigme objet va essayer de formaliser ces différentes catégorisations et caractéristiques pour réaliser une abstraction, une modélisation informatique du sujet de notre intérêt.

Quand...

Classe

La génération spontanée n’existe pas. La voiture que vous conduisez, la chaise sur laquelle vous êtes assis(e) ont été fabriquées. Tous ces objets proviennent d’une « fabrique » ou usine. Pour une première introduction de la notion de classe, ce dernier terme peut être retenu. Il existe ainsi des usines pour avions, bateaux, jouets, etc.

Une autre particularité des classes est le principe de regroupement. On évite dans une même usine de fabriquer à la fois des voitures et des bateaux, ou des jouets et des ordinateurs. Les objets fabriqués sont donc liés à des catégories qui sont les classes en POO. Une classe est une sorte d’usine qui fabrique des catégories d’objets qui ont des caractéristiques communes (les bateaux ont une coque, les voitures une carrosserie, les chats un nom).

Pour déclarer une classe Chat avec Java, il suffit d’utiliser le mot-clé class.

class Chat {   
    // ... à suivre 
}
 

Notez les crochets ouvrants et fermants : tout ce qui se trouve entre ces crochets fait partie de la même classe, et tout ce qui fait partie de la classe Chat doit se trouver entre ces crochets. Le même principe s’applique pour les méthodes.

Pour créer (on dit aussi instancier) un nouvel objet de type Chat...

Attribut

Revenons à nos moutons, ou plutôt à nos chats.

La première version de la classe Chat qui a été proposée n’est pas très intéressante. Le concept d’un Chat a juste été formalisé. Pour aller plus loin, la classe est ensuite complétée selon les besoins de chacun. En supposant qu’il s’agisse de caractériser les chats par leur nom, couleur et âge. Il faut alors ajouter ces caractéristiques (appelées également propriétés ou attributs) à la classe.


class Chat {  
    nom;  
    couleur;  
    dateDeNaissance; 
 

La classe en l’état génère des erreurs car les propriétés ne sont pas encore typées. 

Les attributs définissent l’aspect structurel d’une classe, ses données.

Un chat a (possède) donc un nom, une couleur et un âge, parce que c’est l’objectif de la modélisation.

Et en fait non. Il n’y a pas d’attribut âge, seulement une date de naissance.

Pourquoi ?

Imaginez que ce logiciel tourne merveilleusement bien pendant plusieurs années. Si à chaque date anniversaire, il faut mettre à jour l’âge de tous les chats concernés, cela risque d’être bien compliqué....

Type de données

Tout attribut doit impérativement être typé : long pour la date de naissance et chaîne de caractères pour les autres dans notre exemple. Le premier type est dit simple. On parle de types élémentaires ou primitifs. Vous aurez l’occasion lors du développement d’utiliser des types plus riches, notamment des types classe, comme la classe String.


class Chat { 
    String nom;  
    String couleur; 
    long dateDeNaissance;  
}
 

Dans les applications se pose souvent le problème de conversion (casting) des données. Par exemple, les demandes de saisie via les boîtes de dialogue retournent par défaut des valeurs de type String. Pour un traitement de dates ou de numériques, il faut alors convertir ces valeurs sous peine de provoquer l’arrêt brutal de l’application.

Qui peut le plus peut le moins. Considérez le code suivant :


      int nombreEntier = 5; 
      double nombreDecimal = 0;   
      nombreDecimal = nombreEntier;   
   
      nombreDecimal = 5.5;   
      nombreEntier = (int) nombreDecimal;
 

Un double (qui peut être un nombre signé très grand avec des décimales) peut recevoir un entier. On parle de cast implicite....

Méthode

Les méthodes représentent les actions et les comportements que peuvent mener des objets issus d’une classe. Elles définissent l’aspect dynamique ou comportemental de la classe. Les chats peuvent dormir, manger, miauler, etc.

Voici quelques-unes de ces méthodes.


class Chat {   
      // propriétés  
      String nom;  
      String couleur;    
      long dateDeNaissance;  
  
      // méthodes   
      void dormir(){  
            return ; 
      } 
 
      void manger(Object deLaNourriture){  
      }   
 
      String miauler(){  
            String leMiaulement = "miaou"; 
            return leMiaulement;  
      }  
}
 

Les méthodes qui ne renvoient aucune valeur correspondent à des procédures. Elles sont toujours précédées du mot-clé void.

Les méthodes qui renvoient une valeur correspondent à...

Accessibilité

La POO ajoute le principe d’accessibilité (visibilité) qui consiste à contrôler l’accès aux classes et à leurs membres (propriétés et méthodes).

Le terme d’accessibilité servant également à désigner les techniques d’utilisation d’une application pour des utilisateurs handicapés, le terme de visibilité sera préféré dans cette section.

Les niveaux de visibilité sous Java sont les suivants :

  • Classes :

  • public : les classes avec une visibilité publique sont accessibles par tout le monde (toutes les classes de tous les packages).

  • <rien> : le fait de ne pas mettre de visibilité à la classe rend celle-ci inaccessible en dehors des classes de son propre package.

  • private : ce niveau ne concerne que les classes internes. Ce sont des classes définies à l’intérieur d’une classe et qui ne sont utilisées que par celle-ci.

  • Membres :

  • public : les membres (propriétés et méthodes) sont accessibles à l’extérieur de la classe. Pour protéger les propriétés de modifications non autorisées ou malencontreuses, il est cependant fortement recommandé de ne jamais les déclarer publiques.

  • protected : les membres sont accessibles aux classes descendantes (voir la notion d’héritage plus loin) mais aussi avec Java aux autres classes du même package.

  • <rien> : les attributs et méthodes sont accessibles uniquement pour les classes appartenant au même package si aucune visibilité n’est fournie.

  • private : c’est le niveau le plus élevé...

Encapsulation

Le fait de regrouper au sein d’une même classe propriétés et méthodes et de définir le niveau d’accessibilité constitue l’encapsulation, un des concepts essentiels de la POO.

Une classe va travailler sur les données privées qui lui sont propres, avec la garantie que rien d’autre ne va interférer avec leur traitement. Un développeur saura lui très rapidement quelle partie du code en est responsable (ou fautive), puisqu’une seule classe a été habilitée à modifier la donnée en question. Seuls sont visibles les membres publics, ce qui renforce la sécurité des données et la maintenance des programmes. De plus, l’implémentation des méthodes est masquée. Finalement, avec le concept d’encapsulation, la classe ne présente qu’un accès composé des membres publics. Ce qui est privé est caché aux yeux du monde extérieur à la classe.

En reprenant l’exemple précédent, pour modifier le nom d’un chat il faut obligatoirement utiliser la méthode setNom(), ce qui permet d’effectuer éventuellement des contrôles et traitements supplémentaires. En partant de ce principe, on peut l’appliquer à d’autres cas tels que le débit d’un compte...

Constructeur

Pour qu’une classe puisse instancier des objets, il lui faut un constructeur. Il s’agit d’une méthode particulière, du même nom que la classe et écrite sans valeur de retour, qui permet de créer des objets. Si un constructeur est omis, Java l’insérera au moment de la compilation du programme en créant un constructeur basique du type NomDeLaClasse(), sans paramètre (il s’agit alors du constructeur par défaut). Il est possible de déclarer plusieurs constructeurs dans une classe, comme dans l’exemple suivant.


public class Chat {   
  // propriétés   
  ...  
  // constructeurs  
 
  private Chat() { 
   super();  
  } 
 
  private Chat(long laDateDeNaissance) {  
   this();  
   dateDeNaissance = laDateDeNaissance;  
  } 
 
  public Chat(String leNom, String laCouleur, 
                long laDateDeNaissance){  
    this(laDateDeNaissance) ;  
    nom = leNom;  
    couleur = laCouleur;  
  }  
  // méthodes  
  ...  
}
 

Le premier constructeur Chat() est privé. Il ne peut pas être...

Héritage

Les habitudes de la pensée humaine utilisent très souvent les notions de hiérarchies et de classification, comme par exemple pour la zoologie ou un arbre généalogique.

La POO essaye de restituer cette manière de penser grâce à la notion d’héritage, grâce à laquelle des classes « enfants » récupèrent des propriétés et des comportements de leur classe « parente », en se gardant la possibilité de pouvoir les compléter et/ou les modifier.

En reprenant l’exemple de Filou, un Mammifère est un concept plus général à celui de Félin qui est un concept plus général de Chat, dont Filou est une instance particulière. En termes de programmation objet, il est possible de créer une hiérarchie de classes transposant cette manière de penser. Il existera donc un objet Filou construit depuis une classe Chat héritant de la classe Félin qui héritera de la classe Mammifère.

images/03-01-heritage.png

D’une manière plus générale, tous les félins, canidés et cétacés héritent des caractéristiques communes de la classe Mammifere : animaux à sang chaud, la femelle allaite ses petits, etc. Filou fait partie d’une grande famille. Il va hériter de toutes les propriétés et méthodes de ses classes ancêtres, en laissant de côté les éléments spécifiques des canidés et des cétacés. Pour en tenir compte, il faut revoir la définition de la classe Chat et ajouter celles des classes parentes. Pour faire simple, voici un exemple d’héritage limité aux classes Felin et Chat.


public class Chat extends Felin {  
  private String nom;  
  private long dateDeNaissance;      
  public Chat(String lEspece, String laCouleur,  
           String leNom, long laDateDeNaissance) { 
    super(lEspece, laCouleur);  
    nom = leNom;  
    dateDeNaissance...

Interfaces

Une interface est un moyen de créer un contrat pour les classes et donc les objets, contrat qui permettra de définir des comportements obligatoires pour toutes les classes implémentant cette interface.

L’intérêt principal d’une interface est de factoriser des comportements communs pour une utilisation standardisée. Si, au cours du cycle de développement de votre application, vous vous apercevez qu’un de vos objets peut avoir une meilleure implémentation (comme une voiture avec un meilleur moteur), et si vous utilisez une interface pour communiquer avec cet objet, vous pourrez modifier votre code beaucoup plus facilement en substituant votre ancienne classe avec la nouvelle qui possédera exactement la même interface.

Reprenons l’exemple de la voiture : chaque modèle de voiture est unique. Chaque modèle est construit suivant un schéma différent, avec des technologies différentes. Néanmoins, l’interface de chaque modèle de voiture est toujours plus ou moins la même : un volant, des pédales pour le frein et l’accélérateur, les commandes de clignotants... Le volant est toujours devant vous, les pédales sont à la même place quelle que soit la voiture que vous utilisez, comme les commandes de clignotants... Félicitations ! Vous connaissez bien...

Polymorphisme

Le polymorphisme est une notion importante en POO, et veut dire étymologiquement « qui peut prendre plusieurs formes ».

Le polymorphisme est la capacité d’un code à choisir dynamiquement à l’exécution la méthode à exécuter, et implique qu’un même appel de méthode puisse avoir des comportements différents, soit en fonction des paramètres de cet appel, soit en fonction de l’objet avec lequel l’appel est effectué.

Java comporte deux types de polymorphismes principaux : la surcharge et la redéfinition.

Le mécanisme de polymorphisme peut également être obtenu à l’aide d’interfaces.

Depuis Java 1.5, il existe également un polymorphisme dit paramétrique, avec l’utilisation de génériques, qui sera étudié au chapitre suivant.

1. Par surcharge

Le polymorphisme de surcharge correspond à la capacité d’un objet à choisir au moment de l’exécution (dynamiquement) la méthode qui correspond le mieux à ses besoins parmi ses propres méthodes ou celles des méthodes mères.

Une autre particularité de ce polymorphisme est que toutes les méthodes concernées portent le même nom. Au sein d’une classe, elles se différencient obligatoirement et uniquement par le nombre de paramètres ou le type de ces derniers.

Rappel : une signature de méthode ne peut être implémentée qu’une seule fois dans une même classe.

La classe Chat est modifiée pour mettre en évidence ce mécanisme de surcharge. 


public class Chat {  
  ...  
  public void manger(String nourriture){  
    System.out.println("Je mange de " + nourriture);  
  } 
 
 public void manger() { 
    System.out.println("Je vais aller chasser ma nourriture");   
 } 
  
  public void manger(List<Object>...

Principes SOLID

Afin d’obtenir une modélisation et un développement plus fiables et plus robustes en programmation orientée objet, il est possible de mettre en œuvre cinq principes de base rassemblés sous l’acronyme SOLID (en anglais). Ces principes liés entre eux permettent de démarrer un développement sur de bases saines qui permettront de faciliter des modifications ultérieures.

1. Single Responsibility

Le principe de responsabilité unique postule que toutes les classes d’une application doivent être responsables d’une des fonctionnalités de la modélisation, et que cette fonctionnalité doit être contenue dans la classe. De cette manière, chaque classe n’a qu’une seule raison de changer, ce qui permet de garder le contrôle de sa complexité, d’améliorer sa lisibilité et d’obtenir un meilleur travail en équipe comme premiers avantages immédiats.

Un contre-exemple de ce principe est une classe rassemblant plusieurs fonctionnalités, comme une classe Commande qui modélise les données, permet l’accès direct à la base de données et gère elle-même l’impression. Cela crée une classe complexe, dont les différentes fonctionnalités se retrouvent liées entre elles, et qui sera sensible à tout changement, car il n’est alors pas possible de garantir qu’une modification de la partie d’impression n’impactera pas l’accès aux données.

Plutôt que d’utiliser un outil multifonctions dont chaque élément est moyen...

Quelques autres principes utiles

La littérature abonde en divers principes applicables pour la POO. Quelques-uns d’entre eux sont détaillés dans cette section.

1. DRY (Don’t Repeat Yourself)

Le principe DRY (« Ne vous répétez pas » en français) a pour but de limiter la répétition des informations et des traitements dans une application et postule que chaque élément d’information dans un système doit avoir une représentation unique, non ambiguë et faisant autorité.

Il considère le copier-coller excessif de code comme une marque d’instabilité dans l’application.

Le credo personnel de l’auteur suivant ce principe est : créez une fois, copiez la deuxième fois, factorisez la troisième fois.

2. KISS (Keep It Simple, Stupid)

Le principe KISS (« Rester simple et stupide ») privilégie la simplicité dans la conception pour arriver le plus rapidement possible à l’objectif désiré.

Il s’agit ici d’éviter les complexités superflues du code, notamment vis-à-vis des optimisations qui ne devraient être réalisées que lorsqu’une version simple de l’application est opérationnelle.

Une application simple est plus facile à comprendre et à maintenir, pas forcément...