La programmation orientée objet.

Présentation

La programmation orientée objet est un paradigme de programmation extrèmement courant actuellement (seul le C n'est pas orienté objet dans les 10 langages les plus utilisés). Le simula 67 en 1967 est parfois considéré comme le premier langage orienté objet. Pour annecdote Le C++ (= C augmenté) rajoute la programmation objet au C (il fait beaucoup plus depuis).

Simula - logo

En programmation orientée objet on identifie les éléments et concepts qui vont constituer notre programme (par exemple pour le pong : la balle, les palets,...) puis on décrit les propriétés et interactions qui les caractérisent (la balle va se déplacer, la balle va rebondir sur les palets, la balle doit être représentée à l'écran,...).

Puis on passe au codage, pour chaque élément trouvé précédemment on crée une classe avec des attributs (variables) et des méthodes (fonctions) associées qui va le représenté dans la machine. Vous pouvez voir ses classes sont des types personnalisés utilisables dans le corps de votre programme.

L'an passé, il était recommandé de décomposer le programme en plein de petites fonctions, cela permettait une meilleure maintenabilité, un premier avantage de la programmation objet et de de grouper les fonctions.

Cependant vos fonctions étaient dépendantes du choix des variables faites pour modéliser le problème, si vous changez cette modélisation il fallait corriger toutes les fonctions dépendantes (c'est un long travail surtout si on utilise des variables globales et il faut comprendre l'ensemble du code), en programmation objet un changement de modélisation entraînera des modifications dans une partie précise du programme (là où est définie la classe). Le corps du programme lui ne sera pas modifié.

Par exemple si vous voulez faire rebondir une balle dans un pong, il suffit dans le programme principal de demander le rebond, l'implémentation de l'algorithme se fera dans la classe qui correspond à la balle, on peut très bien d'ailleurs confier cette partie du travail à une personne et laisser dans l'ignorance les programmeurs du corps principal du programme.

L'an dernier nous avons vu que les problématiques du réseau était séparé en couche, le programmeur n'a pas besoin de savoir le fonctionnement de la couche physique, en programmation objet c'est pareil, un programmeur utilisera des objets sans savoir comment ils sont fait.

Dans le programme une variable issue d'une classe est appelée instance de la classe.

Exemple

Analysons le Pong, il y a :

  1. Une balle qui va rebondir sur des obstacles. Pour cette balle on doit savoir :
    • Sa position.
    • Sa taille.
    • Sa couleur.
    • Sa vitesse.
    • Sa position l'étape d'après.
    • Si elle touche un obstacle.
    • Changer sa direction dans le cas positif précédent.
    • Savoir si elle est sortie de la zone de jeu.
    • La dessiner à l'écran
  2. Un palet à droite, qui bouge avec l'ordinateur, il faut connaître :
    • Sa position
    • Ses dimensions
    • Sa couleur.
    • Sa position l'étape suivante en rapport avec la balle.
    • Le dessiner à l'écran.
  3. Un palet à gauche, qui bouge avec le joueur.
  4. Deux obstacles immobiles, un en haut et l'autre en bas de l'écran. Pour chacun on doit savoir :

Et on peut créer quatre classes, une pour chaque élément.

Remarque

Vous pouvez remarquer que pour l'instant il n'y a pas de code, on analyse le problème et on définit ce qu'on veut, la programmation (l'implémentation) vient plus tard, avec son lot de compromis (par exemple dans la correction du Pong la balle n'est pas ronde mais carrée.

Remarque

Est il possible de choisir une autre décomposition ?

Oui, la décomposition précédente est perfectible ! Par exemple on peut considérer la fenêtre comme un objet qui affiche le jeu toutes les 1/60 secondes, on peut définir une classe jeu qui va expliquer comment gagner, qui va définir la position des objets au départs, le nombre de joueurs... On peut d'ailleurs dire que chaque joueur est aussi un objet (ce qui est pratique pour changer la façon de jouer de l’ordinateur par exemple ou pour faire un jeu à deux joueurs). Vous êtes (vous ou votre équipe) libre de vos choix, mais après il faut les assumer ! Choisir les bonnes classes demande de l'expérience (on se rend compte que les choix faits à un projet précédent n'étaient pas optimaux et on se corrige) et de la réflexion.

Faire une décomposition théoriquement parfaite peut entraîner des difficultés de compréhension ou des performances moindres

Remarque

La programmation objet offre un mécanisme d'hérédité (qui n'est malheureusement pas au programme), en géométrie un carré est un ca particulier de rectangle et aussi un cas particulier de losange. Donc toutes les propriétés vraies pour le rectangle sont vraies pour le carré et si on a fait les démonstrations pour le rectangle il n'y a plus besoin de les faire pour le carré.

En python (en programmation objet) on peut faire la même choses, dire qu'une classe est un cas particulier d'une autre et donc hérédité de ses caractéristiques. Dans l'exemple du Pong les quatre classes héritent d'une classe que l'on peut appelé sprite par exemple et suffit de définir les collisions sur les sprites pour les avoir automatiquement sur les classes descendante par exemple.

La chose est d'ailleurs tellement pratique que nous allons utiliser parfois l'hérédité.

Q.C.M.

Un paradigme de programmation est :





Q.C.M.

cochez les paradigmes de programmation :





Q.C.M.

En programmation fonctionnelle pure :





Q.C.M.

En programmation objet :





Q.C.M.

La fonction test_ordre_liste permet de savoir si une liste est ordonnée par ordre croissant, la fonction est faite selon quel paradigme :


			



Q.C.M.

La fonction test_ordre_liste permet de savoir si une liste est ordonnée par ordre croissant, la fonction est faite selon quel paradigme :


			




Présentation du problème.

Utiliser et manipuler des dates est une chose courante en informatique (comme dans la vraie vie), de base Python possède le module datetime qui s'en occupe, nous allons en faire une version simplifiée.

Que souhaitons nous pouvoir faire avec des dates ?

  1. La définir
  2. Connaître le numéro jour du mois associé (le 1 le 2...).
  3. Connaître le mois associée.
  4. Connaître l'année associée.
  5. Connaître le jour de la semaine associée (lundi, mardi,...).
  6. L'afficher.
  7. Augmenter la date de n jours.
  8. Comparer deux dates.

Quand on réfléchit à une classe on peut faire un petit shéma :

Les différentes parties seront expliquées plus tard. Pour exemple, on va commencer par définir une classe année, puis vous ferez la classe date.

Classe année.

Avant de commencer voici quatre petites fonctions pratique pour la suite.


			

Définition classe.

Quand on définit une classe en utilise mot réservé class puis on donne le nom de la classe, par convention on utilise une majuscule pour commencer le nom de la classe.


			
Remarque

Entre les parenthéses on peut donner le nom de la classe parente et bénéficier des mécanismes très prtiques d'hérédité mais ce n'est pas au programme de terminale.

Constructeur

Le constructeur est la méthode la plus importante, elle est obligtoire. Elle se fait en appelle de la méthode __init__.

Définition

Une méthode est une fonction liée à une classe, son appel se fait de la manière suivante nom_instance.nom_méthode(parametres).


			
Remarque
  1. Par défaut l'année est celle en cours.
  2. Remarquez le self en premier paramètre, cela est obligatoire self représente l'instance de l'objet, c'est à dire la vriable que vous allez utiliser.
  3. self._annee est un attibut c'est à dire une variable qui pourra être utilisée dans les méthodes suivantes, ce n'est donc pas une variable locale qui disparait une fois la fonction finie.
  4. Par convention le _ de self._annee signifie que l'attribu est privé, il ne doit pas être modifié autrement que dans la classe elle même. Malheureusement Python ne gére pas vraiment les attributs privés et on peut donc faire des modifications à l'extérieur. On peut aussi utiliser deux _, self.__annee est alors plus difficile à modifier mais ce n'est pas la convention utilisée.
  5. On peut évidement avoir autant d'attribus que l'on veut et on peut aussi vouloir laisser les utilisateurs modifier les attributs (attributs publiques) à écrire self.annee alors.

Pour utiliser la classe il faut créer une instance cela se fait de la manière suivante :


			

D'autres méthodes

Les méthodes doivent être dans la classe, attention à la tabulation.


			
Remarque
  1. En python si a est une instance de Annee alors a._annee et a.donne_annee() donne le même résultat, pourquoi alors préférer la deuxième méthode ? Si demain, pour améliorer le programme, on décide de ne plus conserver l'année sous quatre chiffres mais plus que sur deux (en réalité c'est une mauvaise idée de la faire mais c est pour un exemple), alors il est nécessaire de changer les méthodes de la classe mais aussi toutes les parties du programme ou on utilisera a._annee et cela va demander du temps. Si le programme utilise la méthode alors les modifications seront uniquement dans la classe.
  2. Même remarque que précédemment pour la méthode change annee.
  3. Dans les méthodes, vous pouvez utilisez les attributs privés donc self.donne_annee() ou self._annee vont très bien (le deuxième étant un "micro rien" plus rapide). Cependant modifier la modélisation demandera de changer la méthode, donc la version de l'exemple est plus maintenable.
  4. Pour ajouter des années, deux versions sont proposées : une, impérative, va modifier l'instance avec des risques de bugs, l'autre non mais va créer une nouvelle instance (donc double place en mémoire).

Surcharger les opérateurs

Python permet de donner, pour une classe créée, un sens aux opérateurs de bases, vous pouvez trouver la liste des opérateurs ici. Dans la partie ci-dessous nous allons donner un sens à l'égalité, à inférieur, et à la transformtion en string.

Création d'une classe date.

Pour c'est exemple, on fait le choix de représenter une date à l'aide de trois entiers jour, mois, année.

Remarque

Ce choix de représentation peut différer, on peut par exemple définir une date comme un nombre qui représente le nombre de jours écoulés depuis le 1 Janvier 2000 (choix qui permet de simplifier les calculs), on peut aussi choisir d'incorporer le jour de la semaine (cela crée une redondance). Après on peut aussi se demander si on veut le mois sous forme de numéro ou sous forme d'une chaîne de caractères "Janvier", "Février" et pour l'année 12 à la place de 2012 (mais ici on voit les problèmes arriver si on quitte le siècle).

Base

exercice
  1. Faire le constructeur de la classe.
  2. Faire des méthodes donne_jour, donne_mois, donne_annee puis change_jour, change_mois, dchange_annee.
  3. Faire une méthode ajoute_annee(self,nb) qui ajoute nb annees à la date.
  4. Faire une méthode ajoute_mois(self, nb) qui ajoute nb mois à la date.
  5. Faire une méthode ajoute_jour(self, nb) qui ajoute nb(>=0) mois à la date.

Surcharge des opérateurs

exercice
  1. Surcharger la mise en string.
  2. Surcharger l'égalité de date.
  3. Surccharger l'opérateur supérieur.

Méthode plus complexes

exercice
  1. Faire une méthode ajoute_annee(self,nb) qui ajoute nb annees à la date.
  2. Faire une méthode ajoute_mois(self, nb) qui ajoute nb mois à la date.
  3. Faire une méthode ajoute_jour(self, nb) qui ajoute nb(>=0) mois à la date.
  4. Surcharger l'opérateur soustraction pour que le résultat donne le nombre de jours de différence entre les deux dates.
  5. Faire une fonction qui donnera le jour de la semaine qui correspond à la date. (Lundi, Mardi,....).