Outils pour utilisateurs

Outils du site


nsi:terminales:classes

Ceci est une ancienne révision du document !



Warning: Undefined array key "pos" in /home/goupillf/wiki.goupill.fr/lib/plugins/mdpage/src/DokuWiki/Plugin/Mdpage/MarkdownRendererTrait.php on line 100

Warning: Undefined array key "pos" in /home/goupillf/wiki.goupill.fr/lib/plugins/mdpage/src/DokuWiki/Plugin/Mdpage/MarkdownRendererTrait.php on line 100

Warning: Undefined array key "pos" in /home/goupillf/wiki.goupill.fr/lib/plugins/mdpage/src/DokuWiki/Plugin/Mdpage/MarkdownRendererTrait.php on line 100

Warning: Undefined array key "pos" in /home/goupillf/wiki.goupill.fr/lib/plugins/mdpage/src/DokuWiki/Plugin/Mdpage/MarkdownRendererTrait.php on line 100

Warning: Undefined array key "pos" in /home/goupillf/wiki.goupill.fr/lib/plugins/mdpage/src/DokuWiki/Plugin/Mdpage/MarkdownRendererTrait.php on line 100

Warning: Undefined array key "pos" in /home/goupillf/wiki.goupill.fr/lib/plugins/mdpage/src/DokuWiki/Plugin/Mdpage/MarkdownRendererTrait.php on line 100

Warning: Undefined array key "pos" in /home/goupillf/wiki.goupill.fr/lib/plugins/mdpage/src/DokuWiki/Plugin/Mdpage/MarkdownRendererTrait.php on line 100

Warning: Undefined array key "pos" in /home/goupillf/wiki.goupill.fr/lib/plugins/mdpage/src/DokuWiki/Plugin/Mdpage/MarkdownRendererTrait.php on line 100

Warning: Undefined array key "pos" in /home/goupillf/wiki.goupill.fr/lib/plugins/mdpage/src/DokuWiki/Plugin/Mdpage/MarkdownRendererTrait.php on line 100

Warning: Undefined array key "pos" in /home/goupillf/wiki.goupill.fr/lib/plugins/mdpage/src/DokuWiki/Plugin/Mdpage/MarkdownRendererTrait.php on line 100

Warning: Undefined array key "pos" in /home/goupillf/wiki.goupill.fr/lib/plugins/mdpage/src/DokuWiki/Plugin/Mdpage/MarkdownRendererTrait.php on line 100

Warning: Undefined array key "pos" in /home/goupillf/wiki.goupill.fr/lib/plugins/mdpage/src/DokuWiki/Plugin/Mdpage/MarkdownRendererTrait.php on line 100

Warning: Undefined array key "pos" in /home/goupillf/wiki.goupill.fr/lib/plugins/mdpage/src/DokuWiki/Plugin/Mdpage/MarkdownRendererTrait.php on line 100

Warning: Undefined array key "pos" in /home/goupillf/wiki.goupill.fr/lib/plugins/mdpage/src/DokuWiki/Plugin/Mdpage/MarkdownRendererTrait.php on line 100

Warning: Undefined array key "pos" in /home/goupillf/wiki.goupill.fr/lib/plugins/mdpage/src/DokuWiki/Plugin/Mdpage/MarkdownRendererTrait.php on line 100

Warning: Undefined array key "pos" in /home/goupillf/wiki.goupill.fr/lib/plugins/mdpage/src/DokuWiki/Plugin/Mdpage/MarkdownRendererTrait.php on line 100

Classes - Programmation orientée objet (POO)

Lisp

Java

Apparu en 1960 avec LISP. Très répandu aujourd'hui. En Java, presque tout est objet.

Paradigme de programmation

Programmation procédurale

Le programme est découpé en fonctions ou procédures – fonctions qui ne retournent rien – réalisant chacune un morceau de l'ensemble.

Les fonctions sont alors séparées des données traitées, elles sont toutes au même niveau, et les données subissent passivement.

Programmation orientée objet

Les données sont structurées en objets qui définissent des méthodes – fonction d'un objet – pouvant leur être appliquées, et des attributs.

Les objets sont responsables de l'exécution des fonctions, ils ne sont plus passifs. Les fonctions sont organisées selon les objets auxquelles elles appartiennent.

Dans l'exercice on a vu que dans un projets grandissant, on avait des problèmes d'organisation :

  • beaucoup de fonctions, il faut allonger leurs noms pour bien les distinguer,
  • pas facile de contrôler les données sur lesquelles agissent les fonctions.

Pour le dire simplement, la POO permet d'organiser mieux le programme ce qui permet de faciliter le développement de gros programmes.

Notion de classe

Un objet est défini par une classe. La classe est la notice qui détaille comme on fabrique l'objet et comment on l'utilise.

  • La classe définit des méthodes (fonctions dans une classe) qui définissent le fonctionnement de la classe et son interface – comment l'extérieur peut agir sur un objet.
  • Elle a aussi des attributs, c'est à dire des données qui la caractérisent. Par exemple, objet Humain aurait les attributs genre, age

Cela rappelle la définition abstraite des structures de données – Listes, Piles et Files – consistant à préciser toutes les propriétés désirées. Une classe est une implémentation qui énonce naturellement les propriétés désirées.

## Définir une classe

Dans l'exercice on rencontre les problèmes :

* Pas de façon simple pour regrouper toutes les données concernant le triangle, * pas de façon simple de savoir qu'une variable contient un triangle, et un triangle bien formé, * multiplication de fonctions en vrac.

Les classes vont permettre de simplifier ces problèmes :

* La classe définit un modèle qui définit la façon de créer et d'utiliser des objets. Une classe `Triangle` permettra de définir exactement les données contenues dans un objet de ce type.

* Dès lors qu'un objet est de type `Triangle`, on est certain qu'il respecte les contraintes qu'on a fixé dans la classe.

* Chaque classe définit ses propres fonctions de sorte que le code est mieux organisé.

### Comment procéder

*Remarque :* par convention, le nom d'une classe utilise *CamelCase* : première lettre en majuscule, majuscule à chaque changement de mot : `Triangle`, `TriangleIsocele`…

On peut suivre les étapes suivantes : * Identifier les objets et leurs attributs : quelles sont les données caractérisant les objets * Identifier les opérations : comment utilise-t-on l'objet, notamment depuis l’extérieur ? * Définir la visibilité : quels attributs / méthodes sont visibles par les autres ? * Implémentation

*On reviendra plus tard sur la notion de visibilité.*

### Exercice 2

Reprenons le cas du triangle.

* Quels sont ses attributs ? * Quelles sont les opérations possibles ?

Pour représenter ces choix de façons claire et explicite, on utilise un diagramme UML - *Unified Modeling Language*

Triangle
:————-
Attributs
+ réel : x1
+ réel : y1
+ réel : x2
+ réel : y2
+ rée l: x3
+ réel : y3
+ texte : nom
Méthodes
+ construire(réels: x1, y1, x2, y2, x3, y3, texte:nom)
+ réel : aire()
+ réel : périmètre()

Dans un programme complexe, le diagramme UML permettra d'indiquer les dépendances entre les différentes classes.

## Classe en Python

Voici un modèle </markdown>

triangle.py
class Triangle: # on utilise PascalCase = UpperCamelCase
    """
    Commentaires utiles pour la lisibilité et la documentation
    """
    x1: float # ceci n'est pas requis en Python
    y1: float # mais peut rendre le code plus clair
    x2: float
    y2: float
    x3: float
    y3: float
    nom: string
 
    def __init__(self, x1, y1, x2, y2, x3, y3, nom):
        """
        Constructeur - Gère l'initialisation de l'objet
        """
        self.x1 = x1 # x1 est la valeur fournie
                     # self.x1 est l'attribut x1 de l'objet
        self.y1 = y1
        # etc.
 
    def aire(self) -> float :
        """
        Documentation...
        -> float n'est pas requis en Python. Il permet seulement de préciser ce qui est
        attendu en sortie.
        """
 
        # formule de maths...
        return abs((self.x2 - self.x1)*(self.y3 - self.y1) - (self.y2 - self.y1)*self(x3 - self.x1))/2
 
    # puis les autres méthodes...

Exemple d'utilisation

monTriangle = Triangle(0,0,5,2,3,4,'T1')
monTriangle.aire() # retourne 7.0
type(monTriangle)  # retourne Triangle, ce qui s'affiche <class : Triangle> 

Instanciation

Parallelogramme et Triangle sont des modèles. Quand on définit la classe Triangle, on ne crée aucun objet de type Triangle. On se contente de dire comment faire pour en créer un ou pour l'utiliser.

Quand on écrit monTriangle = Triangle(...), on crée un objet de type Triangle.

On dit que monTriangle est une instance de la classe Triangle.

Autrement dit : On peut définir ce qu'est une voiture et comment on fait une voiture dans un livre. Ce livre n'en est pas pour autant une voiture. Quand on suit le livre et qu'on fabrique la voiture, on obtient une réalisation de voiture, c'est une instance de voiture.

Une classe peut avoir plusieurs instances : on peut créer plusieurs objets Triangle en suivant le guide que constitue la classe Triangle.

Autrement dit : On peut fabriquer plusieurs voitures, identiques ou pas.

Exercice 3

  • Dans un fichier triangle.py définissez la classe Triangle - complétez ce qui manque dans le modèle donné plus haut.
  • En fin de fichier, ajoutez le code de test suivant et exécutez :
if __name__ == '__main__':
    monTriangle = Triangle(0,0,5,2,3,4,'T1')
    a = monTriangle.aire()
    p = monTriangle.perimetre()
 
    print("L'aire du triangle est de {}".format(a))
    print("Le périmètre du triangle est de {}".format(p))

Très important :

  • L'instanciation monTriangle = Triangle(...) provoque l'exécution du constructeur.

    En Python, il s'agit de la méthode __init__(...)

  • En tant qu'objet de type Triangle, monTriangle a des attributs et des méthodes.

    • monTriangle.x1 désigne l'attribut x1̀ de Triangle`
    • monTriangle.aire désigne la méthode aire de monTriangle.
  • Dans la signature et le corps de la méthode aire, on peut voire apparaître le mot-clé self. La méthode aire ne peut être appelée que via une instance, par exemple monTriangle.aire(). self désigne alors l'instance elle-même.

tA.aire() # dans ce cas, self = tA
tB.aire() # dans ce cas, self = tB

D'autres langages préfèrent utiliser le mot-clé this.

  • En Python, lors de la définition de la méthode aire, on est obligé de préciser l'argument self en première position.

    Au moment de l'appel, on ne précise pas self. On se contente de par ex tA.aire(), et self est automatiquement mis égal à tA. Tout se passe comme si on avait appelé Triangle.aire(tA).

L'objet est reponsable de l'action

Sans classe, on faisant aireTriangle(triangle)triangle subissait la fonction passivement.

Maintenant, triangle est un objet doté de méthodes et on écrit triangle.aire(). L'objet est responsable du calcul.

Chaque type d'objet définit ses propres méthodes. Le code devient plus clair : triangle.aire(), rectangle.aire(). C'est léger, lisible, bien organisé.

Exercice 4

On peut considérer que le diagramme UML de la classe Parallelogramme est identique à ceci près qu'il faut ajouter un quatrième point.

  • Dans un fichier parallelogramme.py créez une classe Parallelogramme
  • En fin de fichier, ajoutez le code de test suivant et exécutez :
if __name__ == '__main__':
    monPlg = Parallelogramme(0,0,5,2,8,6,3,4,'P1')
    a = monPlg.aire()
    p = monPlg.perimetre()
 
    print("L'aire du plg. est de {}".format(a))
    print("Le périmètre du plg. est de {}".format(p))

On peut voir ici que le code est presque identique à celui du cas Triangle. Quand on appelle la méthode aire(), aucune confusion n'est possible. monPlg est un objet Parallelogramme, donc monPlg.aire() appelle la méthode aire de Parallelogramme.

  • Que faudrait-il faire pour être certain qu'un objet Paralllelogramme soit toujours valide ?

    Comprendre : comment vérifier qu'il est valide au moment où on le crée et vérifier qu'il reste valide dans le cas où on modifie ses attributs.

Public / privé

plg = Parallelogramme(0,0,1,0,1,1,1,0,'P')
plg.x1 = 10

Lors de la création plg est bien valide. Mais on modifie plg.x1 de sorte qu'il ne l'est plus...

Une solution est d'empêcher la modification de l'attribut x1 (et des autres) en le rendant privé.

Un attribut privé ne peut-être lu et modifié que depuis l'intérieur de la définition de la classe.

Remarque : En Python, il n'y a pas vraiment de gestion de l'accès public / privé. On convient de nommer les attributs / méthodes privé(e)s en les faisant précéder par _.

Dans l'exemple, ce serait donc _x1.

Dans le diagramme UML, on mettra - au lieu de +.

Remarque : Certains invitent à utiliser __ (deux underscore au lieu d'un). Ne vous y fiez pas trop. Ce choix crée des difficultés et ne constitue pas une protection.

La notion public / privé n'est pas explicite dans le programme. Ne vous en souciez donc pas trop, on pourra travailler en ne considérant que rien n'est privé.

Classes et sous-classes

  • Éviter tout bloc de code trop compliqué : découper au maximum en fonctions.
  • Idem pour les classes : Utiliser une classe spécifique pour tout type d'item.

Exercice 5

  • Dans un fichier point.py, définir une classe Point ayant les attributs x, y.
  • Ajouter la méthode distance(self, autrePoint) de sorte que pour les Point A et B, l'expression A.distance(B) donne la distance AB.
  • Modifier les classes Triangle et Parallelogramme pour qu'elles utilisent la classe Point au lieu des paires de coordonnées x, y.

Important

>>> p1 = Point(2,3)
>>> p2 = Point(2,3)
>>> p1 == p2 # la réponse est False !

Les deux points ont les mêmes coordonnées mais ce sont néanmoins deux points différents.

En écrivant Point(2,3) on crée un instance en mémoire. Quand on réécrit Point(2,3) on crée une nouvelle instance en mémoire même si elle a les mêmes attributs. Ce sont donc deux instances différentes.

Exercice 6 - problème de dépendance

Supposons que l'on veuille ajouter une fonction qui détermine si un point est contenu dans un triangle. Selon vous, quel est le meilleur choix ?

  • dans la classe Point, définir la méthode estDansTriangle(self, triangle)
  • dans la classe Triangle, définir la méthode contientPoint(self, point)
  • en dehors des deux classes, définir une fonction triangleContientPoint(triangle, point)

Dépendance en UML

Le diagramme UML permet de faire apparaître que la classe Triangle utilise la classe Point.

Méthodes magiques

Quand on définit x = 5 et y = 12, on peut faire avec x et y un certains nombres d'opérations qui seront comprises automatiquement par le langage.

  • On peut additionner x + y
  • On peut comparer x <= y
  • On peut afficher print(x), ce qui provoque le transtypage de x, int -> str

Si on souhaite pouvoir faire ce genre d'opérations avec notre objet, c'est possible !

Le cas le plus courant est l'affichage. On aimerait que le transtypage str(monPoint) produise une chaîne de caractère comme (15 ; 45) pour le point de coordonnées x = 15 et y = 45.

Il faut utiliser la méthode __str__(self)

# Dans la classe Point
def __str__(self):
    """
    transtypage -> str
    """
    return "({} ; {})".format(self.nom, self.x, self.y)

Exercice 7

Ajoutez une fonction de transtypage vers str dans la classe Triangle.

Pensez à tester.

Exercice 8

  • Proposez une implémentation pour les piles en utilisant des classes.
  • Même chose pour les files.
  • Dans les deux cas, prévoyez une méthode __str__

Exercice 9 ++

On peut aller plus loin en ajoutant des vecteurs, en définissant le produit scalaire...

Exercice 10 ++

Implémentation d'une relation (BDD) avec les classes

nsi/terminales/classes.1624464313.txt.gz · Dernière modification : de goupillwiki