Aller au contenu

La programmation orientée objet (POO)

Introduction

Pour écrire un programme, comme pour construire une maison ou s'habiller, il y a plusieurs manières de faire.

Il y a ainsi plusieurs paradigmes de programmation, qui sont des styles et des philosophies différentes sur la manière de programmer :

  • le paradigme impératif est celui qu'on a utilisé le plus souvent jusque là. Il consiste à utiliser des séquences de plusieurs instructions, avec des boucles pour les répétitions. C'est le plus ancien historiquement parce que c'est le plus proche du fonctionnement de la machine. On peut l'utiliser avec la plupart des langages de programmation.

  • le paradigme fonctionnel, qu'on reverra cette année, est basé entre autre sur l'importance des fonctions. Les langages fonctionnels connus sont Ocaml, Haskell ou Lisp.

  • le paradigme orienté objet, qu'on va voir ici. Il est très utilisé par les entreprises qui développent des logiciels, et est intégré dans des langages comme Java, C++ ou Python.

Définitions

La Programmation Orientée Objet est construite autour des objets, qui sont des valeurs dont le type est une classe. Par exemple on pourrait avoir une classe Chat, et chaque chat en particulier serait un objet de type Chat.

Une classe définit des variables et des fonctions qu'on va associer aux objets de la classe :

  • les variables propres aux objets qu'on appelle les attributs. Par exemple, l'âge du chat, son poids...

  • les fonctions propres aux objets qu'on appelle les méthodes, et qui permettent d'interagir avec un objet. Par exemple un chat pourra avoir une méthode nourrir(quantite) qui prend en paramètre la quantité de croquettes qu'on veut lui donner, ou une méthode anniversaire() qui augmente son attribut age de un.

En python

Pour accéder aux attributs et méthodes d'un objet, on utilise le point . entre le nom de l'objet et le nom de la méthode ou attribut. En fait, en python vous avez déjà utilisé cela très souvent !

Quand vous avez une liste ma_liste et que vous écrivez ma_liste.append(2), vous utilisez le fait que ma_liste est un objet de la classe list et qu'elle dispose donc de la méthode append.

Voici comment on définit une nouvelle classe en python :

class Chat:
    def __init__(self,age,nom):
        self.age = age
        self.nom = nom
        self.energie = 20
    def nourrir(self,quantite):
        self.energie = self.energie + quantite
    def anniversaire(self):
        self.age = self.age + 1
  • la classe est définie avec le mot-clé class et son nom par convention commence par une majuscule.
  • on doit définir une méthode __init__ qui sert à initialiser un nouvel objet. C'est dans cette méthode qu'on peut initialiser les attributs de l'objet (ici, age,nom, energie).
  • toutes les méthodes doivent avoir comme premier paramètre self : c'est un paramètre spécial qui fait référence à l'objet qui a appelé cette méthode ou attribut. Donc pour faire références aux attributs, on doit écrire self. devant.

Et pour créer un objet :

mon_chat = Chat(8,"Crevette")
mon_chat.nourrir(5)
mon_chat.anniversaire()

  • pour créer un objet, on utilise le nom de la classe comme une fonction, en donnant les paramètres demandés par la fonction __init__ de la classe.
  • le paramètre self n'est jamais renseigné quand on appelle une méthode : en effet, si on appelle mon_chat.anniversaire(), ce sera mon_chat qui remplacera self dans la méthode anniversaire.

En python, tout est un objet, même les entiers :

>>> a = 3
>>> type(a)
<class 'int'>
>>> dir(a)
['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'as_integer_ratio', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']
  • la fonction dir permet d'obtenir la liste des méthodes d'un objet. Les méthodes entourées de double underscore '__' ne sont par convention pas faites pour êtres appelées directement.

À quoi ça sert ?

Quel est l'intérêt de faire tout ça ? Il y a plusieurs objectifs à la programmation orientée objet :

  • Regrouper les variables et fonctions à propos d'une même entité dans un même endroit (par exemple, la position et la vitesse d'une balle, ainsi que le code qui permet de la faire bouger et rebondir dans un jeu) permet de rendre le code plus clair si les classes sont bien conçues.

  • Un principe important est l'encapsulation : dans beaucoup de langages, mais pas dans python, les attributs pourront être privés et ne seront alors accessibles que par les méthodes de la classe. Cela permet :

    • de rendre le code plus sûr, car les données d'un objet ne seront pas modifiées n'importe où mais seulement à travers l'utilisation des méthodes de la classe;
    • tant que la signature des méthodes (leurs paramètres et ce qu'elles renvoient) restent les mêmes, on peut changer la manière dont la classe est implémentée sans que tout le code qui utilise cette classe n'ait besoin d'être modifié. C'est en quelque sorte une boîte noire qu'on peut modifier à l'intérieur (par exemple pour la rendre plus performante) sans avoir besoin de changer la façon dont elle est utilisée.

D'autres fonctionnalités de la programmation orientée objet comme l'héritage et le polymorphisme ne sont pas au programme de NSI.