Programmation Orientée Objet

La programmation orientée objet (POO) est un paradigme de programmation informatique. Il consiste en la définition et l’interaction de briques logicielles appelées objets ; un objet représente un concept, une idée ou toute entité du monde physique, comme une voiture, une personne ou encore une page d’un livre.

Il possède une structure interne et un comportement, et il sait interagir avec ses pairs.

Il s’agit donc de représenter ces objets et leurs relations ; l’interaction entre les objets via leurs relations permet de concevoir et réaliser les fonctionnalités attendues, de mieux résoudre des problèmes. Dès lors, l’étape de modélisation revêt une importance majeure et nécessaire pour la POO. C’est elle qui permet de transcrire les éléments du réel sous forme virtuelle.

source : Wikipédia

L’objet

Un objet est en quelque sorte un « type personnalisé » (il faut entendre type au même titre qu’un nombre, une chaîne de caractères, …). Il permet de stocker des données dans des champs et les gérer au travers des méthodes.

Ces éléments (champs et méthodes) sont appelés ses membres et on note le membre Membre  d’un objet Objet  : Objet.Membre .

  • Les attributs (ou champs) : ils sont à l’objet ce que les variables sont à un programme.
  • Les méthodes sont des procédures ou fonctions destinées à traiter les données. Elles servent d’interface entre les données et le programme.

Le fait de réunir dans un même objet les attributs et les méthodes se nomme l’encapsulation. Cela permet de cacher à l’utilisateur d’un objet certaines données ou procédures internes.

Remarque : en Python, tout est objet, même les modules. Par exemple le module  math  est un objet comprenant l’attribut  pi , la méthode sqrt() , …

 

Un peu de géométrie dans l’espace

Le point

Soit M un point de l’espace et de coordonnées cartésiennes x_M, y_M et z_M  dans un repère R\left(O, \vec{x}, \vec{y}, \vec{z}\right).

On peut considérer le point comme un objet géométrique, possédant 3 attributs : ses coordonnées dans R.

 

Définition de classe Python :

Remarque : self  désigne l’objet lui même. On utilise ce terme dans tout ce qui est défini à l’intérieur de la classe (arguments et méthodes) pour éviter les ambiguïtés lorsque l’on s’intéresse aux membres de l’objet ( self.x  = « c’est mon x »).

Si on souhaite que la fonction print()  affiche des informations plus explicites sur le point, on peut déclarer, à l’intérieur de la déclaration de classe, la méthode __repr__()  (il s’agit d’un nom réservé, d’où les deux « _ ») :

Ce qui aura pour conséquence le comportement suivant :

Remarque : toutes les méthodes ou attributs commençant par « __ » (deux tirets-bas) sont privés, c’est à dire qu’ils ne peuvent être utilisés que depuis l’intérieur même de l’objet.

On peut créer ses propres membres privés (en plus des noms déja réservés) pour mieux contrôler la modification des données de l’objet.

Question 1)  Écrire une fonction distance(P1, P2)  admettant 2 objets Points  comme arguments et renvoyant la valeur de la distance entre ces deux points.

Le vecteur

De même que pour le point, on peut définir un vecteur par ses 3 coordonnées dans le repère R.

Définition de classe Python :

Parmi les noms de méthodes spéciales (elles sont toutes ), il en existe une permettant d’utiliser le symbole « + » pour faire une somme de deux objets de même type : la  méthode __add__().

Question 2) Écrire la méthode __add__(self, v)  , acceptant comme argument le vecteur lui-même ( self ) et un autre vecteur v , et en renvoyant le vecteur somme.

Le résultat doit permettre de faire ça (après avoir implémenté une méthode __repr__()  comme pour le point) :

Question 3) Faire de même avec les opérateurs suivants :

nom de la méthode expression Python écriture mathématique
différence : __sub__() : u - v \vec{u}-\vec{v}
norme : __abs__() : abs(u) \|\vec{u}\|
négation : __neg__() : -u -\vec{u}
produit par un scalaire : __mul__() : k*u k\times\vec{u}
(dans ce dernier cas, les arguments étant de types différents, il faut aussi définir la fonction « réfléchie » __rmul__() , pour pouvoir faire k*u  et u*k )

Question 4) Implémenter deux méthodes prod_scal(self, u)  et prod_vect(self, u)  réalisant les produits scalaires et vectoriels.

Droite

Il existe plusieurs manières de définir une droite : Point+Vecteur, deux Points, …

Question 5) Créer une classe Droite() , dont le constructeur peut admettre différents types d’arguments (pourvu que ceux-ci puissent permettre la définition univoque de l’objet).

Pour tester si un objet est une instance d’une classe donnée, on peut utiliser la fonction isinstance(objet, nom_de_la_classe) , qui renvoie alors True  ou False .

 

Pour passer à une fonction des arguments de nombre ou de type non prédéterminés, on peut utiliser l’opérateur « * » :

  • l’argument *args  dans la définition de la fonction va récupérer tous les arguments donnés lors de l’appel de la fonction dans un tuple nommé args .

(exemple  :  def mafct(*args)  → mafct("a", 2)  → args = ("a", 2) )

  • l’argument **kargs  dans la définition de la fonction va récupérer tous les arguments donnés lors de l’appel de la fonction dans un dictionnaire nommé kargs .

(exemple : def mafct(**kargs)  →  mafct(k="z", p=3)  → kargs = {'k': "z", 'p': 3} )

  • et on peut combiner les deux :

(exemple :  def mafct(*args, **kargs)  → mafct("a", p="3")

→  args = ("a",)  et  kargs = {p : "3"} )

 

Question 6) Améliorer la fonction distance()  pour qu’elle admette également un Point  et une Droite  comme arguments, et en calcule la distance relative !

Question 7) Plus dur : étendre les possibilités de la fonction distance()  au calcul de la distance entre deux droites…

Héritage, surcharge, polymorphisme

Nous souhaitons modéliser des cartes à jouer (d’un jeu de 32 cartes) et nous intéressons à leur rang.

Remarque : le texte situé juste après la déclaration d’une classe ou d’une méthode constitue un message d’aide que l’on peut obtenir avec la fonction help() . L’ensemble des messages d’aides au sein d’une classe constitue une sorte de mode d’emploi de la classe, évitant à l’utilisateur d’aller chercher des informations dans le code.

La valeur donnée ici par la fonction obtenir_rang()  est le rang par défaut du jeu de carte Français, et n’est pas valable pour d’autres règles de jeu comme la Belote, la Manille, …

Pour différencier les cartes selon la règle du jeu auquel elles servent, nous pouvons créer d’autres classes. Mais pour conserver les membres de la classe Carte , ces nouvelles classes devront hériter de cette dernière.

Pour représenter cet ensemble nous pouvons utiliser un diagramme de classe (langage UML).

En Python, pour indiquer qu’une classe hérite d’une autre, il suffit de l’indiquer dans sa déclaration :

Question 8) Continuer l’implémentation de la classe Carte_de_Belote .

Il faut bien entendu obtenir :

Nous remarquons que les deux sous-classes Carte_de_Belote  et Carte de manille  possèdent comme leur classe parente des méthodes  obtenir_rang() . La valeur renvoyée est pourtant la bonne, la méthode exécutée est comme nous le souhaitions celle de la sous classe Carte_de_Belote . Cette fonctionnalité s’appelle la surcharge (overloading) de fonction. On parle également de polymorphisme.

Cela permet de remplacer la méthode de la classe « mère » par une méthode spécifique à la classe « fille ». Ou bien de la compléter, car la méthode de la classe « mère » est encore appelable. La méthode suivante, déclarée dans la classe Carte_de_Belote , appelle la méthode de la classe Carte  :

Héritage multiple

Une classe peut hériter de plusieurs sous-classes, lui permettant de disposer des membres de chacune de ses classes « mères » .

Remarque : en cas d’ambigüité sur les noms de membre (si plusieurs classes « mère » possèdent des membres de même nom), ce sont les premiers déclarés qui seront retenus.

Question 9) Implémenter les classes définissant des cartes de jeu de Belote et de Manille en respectant le diagramme ci-dessous.

 

Programmation orientée objet vs. Programmation procédurale

Le programme Python geoometrie.py est écrit en programmation procédurale.

Cliquer pour voir/cacher le code de geoometrie.py


La programmation procédurale est constituée de procédures et fonctions sans liens particuliers agissant sur des données dissociées, et cela peut mener rapidement à des difficultés en cas de modification de la structure des données. Dans le cas de geoometrie.py ce type de programmation impose de différencier les types de donnée ( isinstance()) car les données des objets Circle  et Rectangle  ne sont pas structurées de la même manière.

 

Question 10) Tester et analyser le programme fourni. Identifier les attributs et les méthodes communs à la classe  Forme_geometrique , ou au contraire spécifiques à ses sous-classes (choisir parmi la liste ci-dessous et compléter le diagramme de classes ci-dessous).

 

Question 11) Le réécrire en utilisant de la programmation orientée objet, et en respectant le diagramme de classe.

Question 12) Ajouter une classe permettant de gérer de la même manière des polygones réguliers

 

Sources :

Vous aimerez aussi...

Laisser un commentaire

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

*

code