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,.

Exemples : une voiture, une personne, une page d’un livre…

L’objet possède en interne une structure 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 et la classe

Un objet est un élément d’une classe (en quelque sorte un « type personnalisé » ; il faut entendre type au même titre qu’un nombre, une chaîne de caractères, …).

Définir une classe revient à définir une nouvelle structure de données, qui s’ajoute à celles définies par le langage.

 

Définition de classe

Avant de créer un objet, il faut avoir défini sa classe.

Une classe possède un nom et des membres, c’est à dire des attributs (ou champs) et des méthodes.

Chaque  membre nommé membre d’un objet objet est accessible via l’expression : objet.membre.

  • Les attributs (ou champs) : ils sont à l’objet ce que les variables sont à un programme.

les attributs sont donc typés (exemple int, float, bool, str, … ou n’importe quel autre classe !)

  • 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.

les méthodes acceptent donc des arguments et peuvent renvoyer des valeurs

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’attributAttribut Un attribut est un identifiant (un nom) décrivant une information stockée dans une base. Les attributs correspondent aux colonnes si la relation à laquelle il appartient est représentée sous forme de table. pi , la méthode sqrt() , …

 

 

En Python, on définit une classe grâce à l’instruction class  :

Le bloc de définition d’une classe contient :

  • des attributs de classe (voir plus bas …)
  • des méthodes spéciales (leurs noms, réservés par le langage Python commence et finit par « __ » : deux tirets « bas »)
  • des méthodes personnalisées

 

Instanciation d’objet

Une fois la classe définie, on peut créer autant d’objets de cette classe (on parle d’instance) que l’on veut :

L’opération qui permet de créer l’objet s’appelle la construction.

A chaque construction d’un objet, la méthode __init__()  de la classe est exécutée.

Cette méthode permet en générale de définir les différents attributs de l’objet :

… et la construction de l’objet permet de leur attribuer des valeurs :

 

 

Attributs d’objet – Attributs de classe

Chaque objet peut contenir ses propres valeurs d’attributs :

Mais on peut également attribuer des attributs à la classe elle-même, de sorte que tous les objets de cette classe puissent les utiliser.

Accès à  un attribut d’une classe :

Accès à un attribut d’un objet :

 

 

 

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 \(\mathrm{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 \(\mathrm{R}[latex].

 

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.

É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__().

É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) :

Faire de même avec les opérateurs suivants :
nom de la méthode expression Python écriture mathématique
différence : __sub__() : u - v [latex]\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 )
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, …

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"} )

 

Améliorer la fonction distance()  pour qu’elle admette également un Point  et une Droite  comme arguments, et en calcule la distance relative !
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 :

Continuer l’implémentationimplémentation Réalisation d'un produit informatique à partir de documents, mise en œuvre, développement. Codage d'un algorithme dans un langage informatique. 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.

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.

 

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).

 

Le réécrire en utilisant de la programmation orientée objet, et en respectant le diagramme de classe.
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