Afficher la pageAnciennes révisionsLiens de retourExporter en PDFHaut de page Cette page est en lecture seule. Vous pouvez afficher le texte source, mais ne pourrez pas le modifier. Contactez votre administrateur si vous pensez qu'il s'agit d'une erreur. {{ :suivant.png?nolink&30|}} {{ :retour.png?nolink&30|}} [[:python:accueilpython|{{ :iconemaison.jpg?nolink&30|Sommaire Python et microPython}}]] ===== Programmation Orientée Objet (illustrée en Python & C#) ===== [Mise à jour le : 4/7/2023] {{ :python:poo:objectorientedprogramming-horizontal.jpg?nolink|}} * **Sources** * **Documentation** sur Python.org : <html><a href="https://docs.python.org/fr/3.5/library/functions.html" target="_blank">fonctions natives</a></html> (built-in) * **Outils** * IDE : <html><a href="https://visualstudio.microsoft.com/fr/" target="_blank">Visual Studio</a></html> * UML for Python : <html><a href="https://pynsource.com/" target="_blank">pynsource</a></html> * **Lectures connexes** * **Real Python** * <html><a href="https://realpython.com/learning-paths/object-oriented-programming-oop-python/" target="_blank">Object-Oriented Programming (OOP) With Python</a></html> * **Mots-clés** : objet, état, comportement, instance de classe, classe, attribut, constructeur, méthode, méthode spéciale, propriété, association, héritage, polymorphisme. <callout type="warning" icon="true">Les mots ci-dessous sont dits "réservés". Ils ne peuvent pas être utilisés comme nom de variable. Les mots __soulignés__ sont une nouveauté de Python 3. Les mots en **gras** sont utilisés dans cette page.</callout> | and | continue | finally | is | raise | | as | **def** | for | lambda | return | | assert | del | from | __None__ | __True__ | | __async__ | elif | global | __nonlocal__ | try | | __await__ | else | if | not | while | | break | except | import | or | with | | **class** | __False__ | in | **pass** | yield | * <html><a href="https://docs.python.org/fr/3.5/library/functions.html" target="_blank">Fonctions natives</a></html>** (built-in)**((Fonctions toujours disponibles.)) utilisées dans les exemples : **print**(), **str**() ---- ==== Introduction ==== {{ :python:poo:renaultclio.png?nolink&120|Renault Clio}} Dans les langages informatiques, il est possible de stocker et de manipuler des **objets**((En informatique, un objet est un conteneur symbolique et autonome qui contient des informations et des mécanismes concernant un sujet, manipulés dans un programme. Le sujet est souvent quelque chose de tangible appartenant au monde réel. C'est le concept central de la programmation orientée objet (POO).<html><a href="https://fr.wikipedia.org/wiki/Objet_(informatique)#:~:text=En%20informatique%2C%20un%20objet%20est,programmation%20orient%C3%A9e%20objet%20(POO)." target="_blank">Wikipédia</a></html>)) en mémoire comme autant d'ensembles de couples **attribut**/**valeur**. Un objet possède donc un **nom** qui le rend unique, un **état** caractérisé par la valeur de ses attributs et des **comportements** lui permettant de changer d'état. <html><p style="text-align:center;font-size:large;font-weight:bold">OBJET = ÉTAT + COMPORTEMENTS</p></html> //Exemple// \\ Pour décrire la voiture ci-dessus sous la forme d'un objet logiciel (très simplifié), on peut : \\ - la **nommer** maRenaultClio, \\ - préciser son **état** par "couleur <- rouge", "année <- 2019", vitesse <- 30 etc, \\ - lui affecter les **comportements** "avancer", "reculer", etc. <callout type="primary" icon="true">Une Renault Clio est une voiture au même titre qu'une Ford Fiesta, une Fiat Tipo ou une Citroën C3. Une nouvelle structure de données voit le jour en Orienté Objet : la **classe**.</callout> ==== 1. La classe ==== <callout type="info" icon="true">Une **classe** permet de définir un ensemble d'objets ayant des caractéristiques communes. C'est un "**moule à objets**". Les **objets** sont des **instances** de classe.</callout> {{ :python:poo:classeetobjet.png?nolink | }} On représente une classe avec un symbole du langage **UML**((Le Langage de Modélisation Unifié, de l'anglais **Unified Modeling Language**, est un langage de modélisation graphique à base de pictogrammes conçu pour fournir une **méthode normalisée** pour visualiser la conception d'un système. Il est couramment utilisé en développement logiciel et en **conception orientée objet**.)) comme ci-dessous. {{ :python:poo:objetpoo.png?nolink |}} === 1.1 Déclarer une classe === <tabs> * [[#tab-python_1|Python]] * [[#tab-csharp_1|C#]] <pane id="tab-python_1"> //Mot-clé// \\ * En **Python**, on utilise le mot-clé //**class**// pour déclarer une classe. //Syntaxe// <code python *.py> class nomClass: ''' docstring''' pass # Évite qu'une erreur ne se produise lorsque la classe est vide </code> //Exemple// \\ Création d'une classe “Monstre” vide ! {{ :python:poo:monstrevide.png?nolink|Réalisé avec PynSource}} <code python *.py> class Monstre: pass # classe sans attributs ni méthodes </code> </pane> <pane id="tab-csharp_1"> //Mot-clé// \\ * En **C#**, on utilise le mot-clé //**class**// pour déclarer une classe. //Syntaxe// <code csharp *.cs> class nomClass { // corps de la classe } </code> //Exemple// \\ Création d'une classe “Monstre” vide ! {{ :python:poo:vsclasse0.png?nolink|Réalisé avec Visual Studio}} <code csharp *.cs> class Monstre { } </code> </pane> </tabs> \\ <callout type="info" icon="true">Définir une classe consiste à déclarer les **attributs**((Les attributs représentent l'**état** de l'objet.)) et les **méthodes**((Les méthodes sont les **fonctions** membres des classes. Elles représentent le **comportement** de l'objet. Ici on se limite aux méthodes d'instance.)) caractérisant les **objets**, **instances**((En programmation orientée objet, on appelle instance d'une classe un objet avec un **comportement** et un **état**, tous deux définis par la classe.)) de la classe.</callout> ===1.2 Les attributs=== <callout type="info" icon="true">En **p**rogrammation **o**rientée **o**bjet (**POO**), un **attribut** fait référence à une **variable** ou une donnée qui est associée à une classe ou à un objet. Les attributs représentent les caractéristiques d'un objet et définissent son état. \\ Un attribut peut être associé à l'objet (attribut d'**instance**) ou à la classe (attribut **statique**).</callout> ==1.2.1 Les attributs d'instance== Les attributs d'instance ou attributs sont des **variables d'instance** accessibles depuis toutes les méthodes de la classe où elles sont définies. <tabs> * [[#tab-python_2a|Python]] * [[#tab-csharp_2a|C#]] <pane id="tab-python_2a"> //Mots-clés// \\ En Python, contrairement à d'autres langages comme C#, il n'est pas nécessaire d'avoir déclaré à l'avance les attributs de l'objet (ou leur type). Il sont créés automatiquement la première fois qu'on leur donne une valeur dans le constructeur <nowiki>__</nowiki>**init**<nowiki>__</nowiki>. Le mot-clé **self** est la référence à l'instance en cours. Cet argument doit être placé en premier. self va correspondre à l'instance créé par la classe (voir le paragraphe "Créer et manipuler des objets"). \\ \\ __Remarque__ : En python les **attributs sont publics par défaut**. Il faut les faire précéder de <nowiki>__</nowiki> pour les rendre privés et ainsi respecter le principe d'encapsulation de l'OO. \\ \\ //Syntaxe// <code python *.py> def __init__(self,argument_1,...,argument_n): # __init__ est ce qui se rapproche le plus d'un constructeur self.__attribut_1 = argument_1 # ou self.attribut_1 pour que attribut_1 soit public ... self.__attribut_n = argument_n # ou self.attribut_n pour que attribut_n soit public </code> {{ :python:poo:monstre2constructeur.png?nolink|Réalisé avec Pynsource}} //Exemple// \\ Création d'une classe "Monstre" contenant trois attributs, la position du monstre en x et y sur l'écran et son nombre de points de vie (pointsDeVie). <code python *.py> class Monstre: def __init__(self, nom, x=0, y=0, pv=100): self.__nom = nom self.__pointsDeVie = pv self.__pos_x = x self.__pos_y = y </code> </pane> <pane id="tab-csharp_2a"> //Mot-clé// \\ * **Le C# incite à respecter le principe d'encapsulation**. Les **attributs** sont **privés par défaut**. On peut les déclarer publics avec le mot-clé **//public//** pour y accéder directement à partir d'un objet, mais ce n'est pas une bonne pratique. Le respect du principe d'encapsulation se fait avec des getters et des setters. \\ //Syntaxe// <code csharp *.cs> class nomClasse { private type nomAttribut_1; # le mot private est optionnel ... private type nomAttribut_n; } </code> //Exemple// \\ Création d'une classe “Monstre” contenant trois attributs, la position du monstre en x et y sur l'écran et son nombre de points de vie (pointsDeVie). {{ :python:poo:vsclasse1.png?nolink|Réalisé avec Visual Studio}} <code csharp *.cs> class Monstre { private int nom; private int pointsDeVie; private int pos_x; // position du monstre en (x,y) private int pos_y; } </code> </pane> </tabs> ==1.2.2 Les attributs statiques== Un attribut statique est une **variable définie au niveau de la classe plutôt qu'au niveau de l'instance (objet)**. Cela signifie que sa valeur est partagée par toutes les instances de la classe et peut être accédée et modifiée sans avoir besoin d'instancier la classe. <tabs> * [[#tab-python_2b|Python]] * [[#tab-csharp_2b|C#]] <pane id="tab-python_2b"> //Exemple// \\ L'attribut de l'exemple ci-dessous est utilisé pour compter le nombre de monstres en vie. \\ \\ <code python *.py> class Monstre __nombreDeMonstres = 0 # Cet attribut est statique car non précédé de self. </code> </pane> <pane id="tab-csharp_2b"> //Mot-clé// \\ En C#, on utilise le mot-clé **static** pour déclarer un attribut de classe. \\ \\ //Exemple// \\ L'attribut de l'exemple ci-dessous est utilisé pour compter le nombre de monstres en vie. \\ \\ <code csharp *.cs> private static int nombreDeMonstres = 0; </code> </pane> </tabs> \\ === 1.3 Les méthodes === <callout type="info" icon="true">En **P**rogrammation **O**rientée **O**bjet (**POO**), une **méthode** est une **fonction ou un sous-programme membre d'une classe**. Une méthode peut être: \\ - un **constructeur** qui se définit comme une méthode spéciale utilisée pour initialiser les objets créés à partir de cette classe. Il ne renvoie aucune valeur; \\ - une **méthode d'instance**, n'agissant que sur un seul objet (instance de la classe) à la fois; \\ - une **méthode statique** ou **méthode de classe** appartient à la classe plutôt qu'à une instance. Elle est indépendante de toute instance de la classe (objet) et peut être appelée directement à partir de la classe. \\ </callout> <tabs> * [[#tab-python_3|Python]] * [[#tab-csharp_3|C#]] <pane id="tab-python_3"> <callout type="warning" title="self" color="red" icon="true">**Il n'est pas possible de surcharger une méthode en Python**. À la place, on peut donner des valeurs par défaut aux arguments. Contrairement aux autres langages, l'objet sur lequel la méthode agit doit être nommé explicitement en premier paramètre. </callout> == 1.3.1 Le constructeur == // Mot-Clé// \\ En Python, comme on l'a vu dans le paragraphe "Attributs" ci-dessus, le constructeur a pour nom <nowiki>__</nowiki>**init**<nowiki>__</nowiki> et nécessite **//self//** comme premier paramètre. // Synthaxe (Rappel)// \\ <code python *.py> def __init__(self,argument_1,...,argument_n): # __init__ est ce qui se rapproche le plus d'un constructeur self.__attribut_1 = argument_1 # ou self.attribut_1 pour que attribut_1 soit public ... self.__attribut_n = argument_n # ou self.attribut_n pour que attribut_n soit public </code> {{ :python:poo:monstre2constructeur.png?nolink|Réalisé avec Pynsource}} //Exemple// \\ Création d'une classe "Monstre" contenant trois attributs, la position du monstre en x et y sur l'écran et son nombre de points de vie (pointsDeVie). <code python *.py> class Monstre: # Remarque : la classe est déclarée 1 fois def __init__(self, nom, x=0, y=0, pv=100): self.__nom = nom self.__pointsDeVie = pv self.__pos_x = x # ou self.pos_x pour que pos_x soit publique self.__pos_y = y </code> == 1.3.2 Méthode d'instance == // Mot-clé// \\ Les méthodes d'instance étant des **routines membres de classe**, elles sont définies par le mot-clé **//def//**. //Syntaxe// <code python *.py> class nomClass: def nomMethode_1(self,argument_1,...,argument_n): pass # Évite qu'une erreur ne se produise lorsque la méthode est vide </code> //Exemple// \\ Création d'une classe “Monstre” contenant trois attributs (+ le constructeur) et trois méthodes. {{ :python:poo:monstre2.png?nolink|Réalisé avec PynSource}} <code python monstres.py> class Monstre: # Les attributs est la méthode __init__ ont été présentées ci-dessus. def seDeplace(self, x, y): self.__pos_x = x self.__pos_y = y def estTouche(self, degats): self.__pointsDeVie -= degats def __str__(self): # Redéfinition de la méthode print return str(self.__nom) + ' : pos_x=' + str(self.__pos_x) + ' pos_y=' + str(self.__pos_y)+' points de vie=' + str(self.__pointsDeVie) </code> <callout type="warning" color="red" icon="true">Le mot-clé **self** est **INDISPENSABLE** dès qu'il s'agit de faire référence à l'objet lui-même. Toutes les méthodes portant sur l'**objet** se doivent de le recevoir en paramètre. \\ Remarque : pour simplifier sa déclaration dans cette première approche, la classe Monstre ne répond pas au concept de MASQUAGE des données. Voir le paragraphe "Concept d'ENCAPSULATION".</callout> == 1.3.3 Méthode de classe == //Mot-clé// \\ En Python, le décorateur **@staticmethod** est utilisé pour définir une méthode statique dans une classe. //Syntaxe// <code python *.py> @staticmethod def nomMethode(argument_1,...,argument_n): pass # Évite qu'une erreur ne se produise lorsque la méthode est vide </code> //Exemple// \\ Création d'une méthode statique getNombreDeMonstresEnVie() dans la classe Monstre pour connaître le nombre de monstres en vie. {{ :python:poo:monstre2static.png?nolink|Réalisé avec PynSource}} <code python *.py> class Monstre: __nombreDeMonstres = 0 # Attribut statique # Modification du constructeur pour incrémenter __nombreDeMonstres def __init__(self, nom, x=0, y=0, pv=100): self.__nom = nom self.__pointsDeVie = pv # exemple self.__posx self.__pos_x = x # Un attributs (où variable d'instance) self.__pos_y = y # est rendu privé par : __ Monstre.__nombreDeMonstres += 1 @staticmethod def getNombreDeMonstresEnVie(): print(f"Le nombre de Monstres en vie est :{Monstre.__nombreDeMonstres}") # Les autres méthodes ont été présentées précédemment. </code> </pane> <pane id="tab-csharp_3"> == 1.3.1 Constructeur == //Mot-clé// \\ Le constructeur se doit d'être public, c'est-à-dire accessible à partir d'un objet. Pour cela, on utilise le mot-clé **//public//**. //Syntaxe// <code csharp *.cs> class nomClass { public nomClasse(parametre_1,...,parametre_n) { private Attribut_1 = parametre_1; ... private Attribut_n = parametre_n; } } </code> //Exemple// \\ Constructeurs de la classe “Monstre” contenant trois attributs, la position du monstre en x et y sur l'écran et son nombre de points de vie (pointsDeVie). {{ :python:poo:vsclasse2.png?nolink|Réalisé avec Visual Studio}} <code csharp *.cs> class Monstre { private string nom; private int pointsDeVie; private int pos_x; // position du monstre en (x,y) private int pos_y; // Constructeur surchargé public Monstre(string lenom) { nom = lenom; pos_x = 0; pos_y = 0; pointsDeVie = 100; } public Monstre(string lenom, int x=0, int y=0, int pv = 100) { nom = lenom; pos_x = x; pos_y = y; pointsDeVie = pv; } } </code> <callout type="info" icon="true"> **Surcharger une méthode** revient à en **créer une nouvelle** dont la **signature** se différencie de la précédente uniquement** par la liste et la nature des arguments**.</callout> == 1.3.2 Méthode d'instance == //Mot-clé// \\ Les méthodes d'instance appartenant à l'interface de la classe sont rendues publiques avec le mot-clé **public**. //Syntaxe// <code csharp *.cs> class nomClass { public(ou private) type nomMethode(parametre_1,...,parametre_n) { // Corps de la méthode d'instance } } </code> //Exemple// \\ Création d'une classe “Monstre” contenant trois attributs (+ le constructeur surchargé) et trois méthodes. {{ :python:poo:vsclasse3.png?nolink|Réalisé avec Visual Studio}} <code csharp *.cs> class Monstre { // Les attributs est le constructeur ont été présentés ci-dessus. public void SeDeplace(int x, int y) { pos_x = x; pos_y = y; } public void EstTouche(int degats) { pointsDeVie -= degats; } public void Affiche() { Console.WriteLine($"{nom} : x={pos_x}, y={pos_y}, points de vie={pointsDeVie}"); } } </code> == 1.3.3 Méthode de classe (ou statique) == //Mot-clé// \\ Une méthode de classe est créée avec le mot-clé **static**. //Syntaxe// <code csharp *.cs> class nomClass { public(ou private) static type nomMethode(parametre_1,...,parametre_n) { // Corps de la méthode de classe } } </code> //Exemple// \\ Création d'une méthode statique getNombreDeMonstresEnVie() dans la classe Monstre pour connaître le nombre de monstres en vie. \\ \\ {{ :python:poo:vsclasse4.png?nolink|Réalisé avec Visual Studio}} <code csharp *.cs> class Monstre { private string nom; private int pointsDeVie; private int pos_x; // position du monstre en (x,y) private int pos_y; private static int nombreDeMonstres = 0; public static void getNombreDeMonstresEnVie() { Console.WriteLine($"Nombre de monstres en vie : {Monstre.nombreDeMonstres}"); } public Monstre(string lenom) { nom = lenom; pos_x = 0; pos_y = 0; pointsDeVie = 100; Monstre.nombreDeMonstres += 1; } public Monstre(string lenom, int x=0, int y=0, int pv = 100) { nom = lenom; pos_x = x; pos_y = y; pointsDeVie = pv; Monstre.nombreDeMonstres += 1; } // Les autres méthodes ont été présentées ci-dessus. } </code> </pane> </tabs> \\ ==== 2. Instancier (créer) et manipuler des objets ==== <callout type="info" icon="true">Instancier une classe consiste à **créer un objet sur son modèle**. Entre classe et objet, il y a, en quelque sorte, le même rapport qu'entre type et variable.</callout> === 2.1 Instanciation (création) d'un objet et initialisation de ses attributs === <tabs> * [[#tab-python_4|Python]] * [[#tab-csharp_4|C#]] <pane id="tab-python_4"> //Mot-clé// \\ La création d'une variable de type objet (en abrégé d'un objet) est identique à celle des types standards du langage python : elle passe par une simple **affectation**. \\ __Rappel__: La méthode spéciale <nowiki>__init__</nowiki> est appelée lors de la création de l'objet. Les valeurs passées en paramètre sont affectées aux attributs de l'objet par l'intermédiaire des arguments de <nowiki>__init__</nowiki>. \\ \\ //Syntaxe// <code python *.py> nomObjet = nomClasse(paramètre_1, ...,paramètre_n) </code> {{ :python:poo:dracula.png?nolink&100|}} //Exemple// <code python *.py> # Simplification de la classe Monstre vue précédemment # Limitée à trois attributs publics class Monstre: def __init__(self, x=0, y=0, pv=100): self.pos_x = x self.pos_y = y self.pointsDeVie = pv # Création de l'objet <dracula> de type <Monstre> dracula = Monstre(10,10,200) </code> //Illustration// {{ :python:poo:poodracula3.png?nolink |Etat de l'objet de la classe Monstre}} <callout type="warning" icon="true">Les attributs n'étant pas privés dans cet exemple, il est possible d'y accéder indépendamment des méthodes de la classe. Ceci n'est pas souhaitable, car on ne répond plus au concept d'**ENCAPSULATION**, un des trois piliers de la POO avec l'**héritage** et le **polymorphisme**.</callout> </pane> <pane id="tab-csharp_4"> //Mot-clé// \\ //Syntaxe// \\ <code csharp *.cs> nomClasse nomObjet = nomClasse(paramètre_1, ...,paramètre_n) </code> {{ :python:poo:dracula.png?nolink&100|Réalisé avec Visual Studio}} //Exemple// \\ <code csharp *.cs> // Simplification de la classe Monstre vue précédemment class Monstre { private int pointsDeVie; private int pos_x; // position du monstre en (x,y) private int pos_y; public Monstre(int x=0, int y=0, int pv = 100) { pos_x = x; pos_y = y; pointsDeVie = pv; } } public class Jeu { public static void Main() { Monstre dracula = new Monstre(10,10,200); // Création de l'objet <dracula> de type <Monstre> } } </code> //Illustration// {{ :python:poo:draculavs.jpg?nolink |Etat de l'objet de la classe Monstre}} </pane> </tabs> === 2.2 Manipulation d'un objet=== <callout type="info" icon="true">Les objets sont manipulés par l'intermédiaire de leurs **méthodes**.</callout> <tabs> * [[#tab-python_5|Python]] * [[#tab-csharp_5|C#]] <pane id="tab-python_5"> //Syntaxe// \\ <code python *.py> nomObjet.nomMethode(paramètre_1, ...,paramètre_n) </code> //Exemple// \\ <code python *.py> class Monstre: def __init__(self, x=0, y=0, pv=100): self.__pos_x = x self.__pos_y = y self.__pointsDeVie = pv def seDeplace(self, x, y): self.__pos_x = x self.__pos_y = y def estTouche(self, degats): self.__pointsDeVie -= degats def __str__(self): # Redéfinition de la méthode print return 'pos_x=' + str(self.__pos_x) + ' pos_y=' + str(self.__pos_y)+' points de vie=' + str(self.__pointsDeVie) # Jeu dracula = Monstre(10, 10, 200) dracula.seDeplace(20, 30) dracula.estTouche(6) print(f'Dracula est touché : {dracula}') # Résultat - Dracula est touché : posx=20 posy=30 points de vie=194 </code> // Illustration// \\ Objet dracula après l'exécution de : dracula.seDeplace(20, 30) {{ :python:poo:poodracula4.png?nolink |Etat de l'objet de la classe Monstre}} </pane> <pane id="tab-csharp_5"> //Syntaxe// \\ <code csharp *.cs> nomObjet.nomMethode(paramètre_1, ...,paramètre_n) </code> //Exemple// \\ <code csharp *.cs> class Monstre { private int pointsDeVie; private int pos_x; // position du monstre en (x,y) private int pos_y; public Monstre(int x = 0, int y = 0, int pv = 100) { pos_x = x; pos_y = y; pointsDeVie = pv; } public void SeDeplace(int x, int y) { pos_x = x; pos_y = y; } public void EstTouche(int degats) { pointsDeVie -= degats; } public void Affiche() { Console.WriteLine($"x={pos_x}, y={pos_y}, points de vie={pointsDeVie}"); } } public class Jeu { public static void Main() { Monstre dracula = new Monstre(10,10,200); dracula.SeDeplace(20, 30); dracula.EstTouche(6); Console.Write("Dracula est touché "); dracula.Affiche(); // Résultat - Dracula est touché x=20, y=30, points de vie=194 } } </code> // Illustration// \\ Objet dracula après l'exécution de : dracula.SeDeplace(20, 30) {{ :python:poo:draculavs1.jpg?nolink |}} </pane> </tabs> ==== 3. Principe d'encapsulation ==== <callout type="info" title="Encapsulation" icon="true">En programmation, l’**encapsulation** désigne le principe de **regrouper des données brutes avec un ensemble de routines** permettant de les lire ou de les manipuler. Ce principe est souvent accompagné du **masquage** de ces données brutes afin de s’assurer que l’utilisateur ne contourne pas l’interface qui lui est destinée. <html><a href="https://fr.wikipedia.org/wiki/Encapsulation_(programmation)" target="_blank">Wikipédia</a></html></callout> <tabs> * [[#tab-python_6|Python]] * [[#tab-csharp_6|C#]] <pane id="tab-python_6"> {{ :python:poo:monstre.png?nolink&150|Réalisé avec PynSource}} <callout type="warning" icon="true">En Python, pour **MASQUER** les attributs, c'est-à-dire les rendre **privés** afin qu'ils ne soient accessibles qu'aux méthodes de la classe, on fait précéder leur nom d'un double soulignement <nowiki>__</nowiki>.</callout> //Exemple// \\ Les **attributs** de la classe "Monstre" sont **masqués**. Il n'est pas possible d'y accéder directement. Il faut passer par les methodes de la classe ou des méthodes d'accès appelées **accesseur**(ou **getteur**) et **mutateur**(ou **setteur**). \\ <code python monstres.py> class Monstre: # Simplifiée def __init__(self, x=0, y=0, pv=100): self.__pos_x = x # Les attributs (où variables d'instances) self.__pos_y = y # sont rendus privés lorsqu'ils sont précédés de __ self.__pointsDeVie = pv def getpos_x(self): # Accesseur (getteur) return self.__pos_x def getpos_y(self): # Accesseur (getteur) return self.__pos_y def getpointsDeVie(self): # Accesseur (getteur) return self.__pointsVie def seDeplace(self, x, y): self.__pos_x = x self.__pos_y = y def estTouche(self, degats): self.__pointsDeVie -= degats def __str__(self): # Redéfinition de la méthode print return 'posx=' + str(self.__pos_x) + ' posy=' + str(self.__pos_y)+' points de vie=' + str(self.__pointsDeVie) </code> * **Accesseur (getteur)** Les méthodes //getpos_x()//, //getpos_y()//, //getpointVie()// sont appelées **accesseurs** ou getteur. Elles exposent les attributs privés de l'objet. Il s'agit de fonction permettant de consulter la valeur des attributs de l'objet. Le nom de ces fonctions commence par get. * **Mutateur (setteur)** Pour accéder aux attributs privés de l'objet dans le but de les modifier, on construit des **mutateurs** ou setteurs. //Exemple// \\ Mutateur accédant aux points de vie dans la classe Monstre. <code python *.py> # A ajouter dans la classe Monstre def setpointsDeVie(self, pv): # Mutateur (setter) self.__pointsDeVie = pv # Accès à l'attribut __pointsVie dracula.setpointsDeVie(400) </code> </pane> <pane id="tab-csharp_6"> <callout type="warning" icon="true">En C#, les attributs sont **MASQUES (PRIVES) par défaut**. Ils ne sont accessibles qu'aux méthodes de l'objet ou à des **propriétés**. \\ Une propriété (property) est un membre d'une classe qui expose une valeur ou un comportement spécifique de l'objet de cette classe. Elle peut être utilisée pour lire ou écrire une valeur d'une manière encapsulée, en fournissant un accès contrôlé aux données internes de la classe.</callout> //Mots-clés// \\ Les mots-clés **get** et **set** spécifient les accesseurs (accessors) de la propriété, permettant de lire et écrire la valeur, respectivement. //Syntaxe// <code csharp *.cs> public type NomPropriete { get; set; } </code> //Exemple// \\ Les attributs de la classe “Monstre” sont masqués. Pour y accéder sans passer par les méthodes de l'objet, on définit les **propriétés** //Pos_x//, //Pos_y// et //PointDeVie//. {{ :python:poo:vsclasse5.png?nolink|Réalisé avec Visual Studio}} <code csharp *.cs> class Monstre { private int pointsDeVie; private int pos_x; // position du monstre en (x,y) private int pos_y; public int Pos_x { get { return pos_x; } } public int Pos_y { get { return pos_y; } } public int PointDeVie { get { return pointsDeVie; } set { pointsDeVie = value; } } public void SeDeplace(int x, int y) { pos_x = x; pos_y = y; } public void EstTouche(int degats) { pointsDeVie -= degats; } public void Affiche() { Console.WriteLine($"{nom} : x={pos_x}, y={pos_y}, points de vie={pointsDeVie}"); } } </code> </pane> </tabs> ==== 4. Association entre classes ==== <callout type="info" title="Association de classes" icon="true">//"La manière privilégiée pour permettre à **deux classes de communiquer par envoi de messages** consiste à ajouter aux attributs primitifs de la première un **attribut référent** de la seconde. La classe peut donc, tout à la fois, contenir des attributs et se constituer en nouveau type d'attribut. C'est grâce à ce mécanisme de typage particulier que, partout dans son code, la première classe pourra faire appel aux méthodes disponibles de la seconde."// //Bersini//</callout> {{ :python:poo:uml_classes_en.svg.png?nolink |}} === 4.1 Association unidirectionnelle === == 4.1.1 Exemple 1: Lien d'association == <tabs> * [[#tab-python_7a|Python]] * [[#tab-csharp_7a|C#]] <pane id="tab-python_7a"> Lecture du **diagramme de classes** : chaque objet O1 est associé à un objet O2. La réciproque n'est pas vraie. \\ O1 peut envoyer un message à O2 par le biais de l'association car : \\ * il possède un objet (référent) de type O2 dans ses champs * cet objet est initialisé avec O2 lors de la création de O1 {{ :python:poo:pyassociation1.png?nolink|Réalisé avec PynSource}} <code python exemple1.py> class O2: def jeTravaillePourO2(self): print("Je travaille pour O2") class O1: # lien d'association de O1 --> O2 def __init__(self, O2): self.__lienO2 = O2 def jeTravaillePourO1(self): print("Je travaille pour O1 et demande à O2 de travailler") self.__lienO2.jeTravaillePourO2() # Programme objO2 = O2() objO1 = O1(objO2) objO1.jeTravaillePourO1() </code> //Résultat// \\ Je travaille pour O1 et demande à O2 de travailler \\ Je travaille pour O2 \\ \\ </pane> <pane id="tab-csharp_7a"> Lecture du **diagramme de classes** : chaque objet O1 est associé à un objet O2. La réciproque n'est pas vraie. \\ O1 peut envoyer un message à O2 par le biais de l'association car : \\ * il possède un objet (référent) de type O2 dans ses champs * cet objet est initialisé avec O2 lors de la création de O1 {{ :python:poo:vsassociation1.png?nolink |Réalisé avec Visual Studio}} <code csharp *.cs> class O1 { // Champs private O2 lienO2; // Lien d'association (fort et permanent) avec un objet O2 // Constructeur public O1(O2 objO2) // Passage de l'objet O2 à lier avec l'objet O1 { lienO2 = objO2; } // Méthode d'instance public void jeTravaillePourO1() { Console.WriteLine("Je travaille pour O1 et demande à O2 de travailler"); lienO2.jeTravaillePourO2(); } } class O2 { public void jeTravaillePourO2() { Console.WriteLine("Je travaille pour O2"); } } public class Program { public static void Main() { O2 objO2 = new O2(); // Création de l'objet O2 O1 objO1 = new O1(objO2); // Création de l'objet O1 initialisé avec O2 objO1.jeTravaillePourO1(); // L'objet O1 envoie un message à l'objet O2 } </code> //Résultat// \\ Je travaille pour O1 et demande à O2 de travailler \\ Je travaille pour O2 </pane> \\ </tabs> == 4.1.2 Exemple 2 - Lien de composition == <tabs> * [[#tab-python_7b|Python]] * [[#tab-csharp_7b|C#]] <pane id="tab-python_7b"> {{ :python:poo:estomacdracula.png?nolink|Réalisé avec PynSource}} Lecture du **diagramme de classes** : Dracula se** compose** de différents organes dont un estomac. Si Dracula disparaît, son estomac aussi. {{ :python:poo:pycomposition.png?nolink |Réalisé avec PynSource}} <code python *.py> class Estomac: pass class Monstre: def __init__(self, nom, x=0, y=0, pv=100): self.__nom = nom self.__pos_x = x # Un attributs (où variable d'instance) self.__pos_y = y # est rendu privé par : __ self.__pointsDeVie = pv # exemple self.__posx self.__estomac = Estomac() Monstre.__nombreDeMonstres += 1 # Programme dracula = Monstre("Dracula", verreDeSang) </code> </pane> <pane id="tab-csharp_7b"> {{ :python:poo:estomacdracula.png?nolink|Réalisé avec Visual Studio}} Lecture du **diagramme de classes** : Dracula se** compose** de différents organes dont un estomac. Si Dracula disparaît, son estomac aussi. {{ :python:poo:umlestomac.png?nolink |Réalisé avec Visual Studio}} <code csharp *.cs> namespace Enfer { class Estomac {// On définira ici les particularité de l'estomac} class Monstre { private Estomac estomac; public Monstre(string lenom, int x = 0, int y = 0, int pv = 100) { // ... estomac = new Estomac(); // Chaque monstre possède un estomac // ... // Si le montre disparaît, l'estomac aussi } } //... public class Program public static void Main() { Monstre dracula = new Monstre("Dracula", 10, 10, 200); } } } </code> </pane> </tabs> == 4.1.3 Exemple 3 - Association unidirectionnel avec passage d'un paramètre - lien de dépendance == **Dracula boit un verre de sang** ! A chaque gorgé, Dracula boit 10cl de sang. Le contenu du verre diminue de 10cl. <tabs> * [[#tab-python_7c|Python]] * [[#tab-csharp_7c|C#]] <pane id="tab-python_7c"> {{ :python:poo:vampireverre.png?nolink&150|Réalisé avec PynSource}} **Lecture du diagramme de classe** : l'objet verreDeSang est passé **provisoirement** à l'objet Dracula à l'aide de sa méthode boit. Contrairement à l'exemple précédent l'objet verreDeSang n'est pas associé à un lien permanent dans la classe Monstre. En effet, Dracula n'aura pas toujours un verre à la main ! {{ :python:poo:pyassociation2.png?nolink |Réalisé avec PynSource}} <code python exemple3.py> class Verre: def __init__(self, vol): self.__volume = vol def remplit(self, vol): self.__volume += vol def vide(self, vol): self.__volume = self.__volume - vol def getvolume(self): # Accesseur (getteur) return self.__volume class Monstre: # Le code est limité à l'ajout de la méthode boit dans la version précédente # Ici le lien d'association est faible et provisoire verre n'existe plus à la sortie de la méthode boit def boit(self, vol, verre): verre.vide(vol) # Programme # Un vampire sert un verre de sang à Dracula verreDeSang = Verre(50) print(f'Le verre contient {verreDeSang.getvolume()} cl de sang') # 1 dracula = Monstre("Dracula", verreDeSang) # Dracula voit le verre et se déplace dracula.seDeplace(20, 30) # mais reçoit un projectile dracula.estTouche(6) print(f'{dracula} : touché !') # 2 Monstre.getNombreDeMonstresEnVie() # 3 # Toujours en vie il boit dracula.boit(10, verreDeSang) print(f'Il reste {verreDeSang.getvolume()} cl dans le verre de sang') #4 </code> //Résultat// \\ 1. Le verre contient 50 cl de sang \\ 2. Dracula : pos_x=20 pos_y=30 points de vie=94 : touché ! \\ 3. Le nombre de monstres en vie est : 1 \\ 4. Il reste 40 cl dans le verre de sang </pane> \\ \\ <pane id="tab-csharp_7c"> {{ :python:poo:vampireverre.png?nolink&150|Réalisé avec Visual Studio}} **Lecture du diagramme de classe** : l'objet verreDeSang est passé **provisoirement** à l'objet Dracula à l'aide de sa méthode boit. Contrairement à l'exemple précédent l'objet verreDeSang n'est pas associé à un lien permanent dans la classe Monstre. En effet, Dracula n'aura pas toujours un verre à la main ! {{ :python:poo:VSAssociation2.png?nolink |Réalisé avec Visual Studio}} <code csharp exemple3.cs> namespace Enfer { class Verre { private int volume; public Verre(int vol) { volume = vol; } public void Remplit(int vol) { volume += vol; } public void Vide(int vol) { volume -= vol; } public int Volume { get { return volume; } } } class Monstre { /* Le code est limité à l'ajout de la méthode boit dans la version précédente Ici le lien d'association est faible et provisoire verre n'existe plus à la sortie de la méthode boit */ public void boit(int vol, Verre verre) { verre.Vide(vol); } } public class Program { public static void Main() { // Un vampire sert un verre de sang à Dracula Verre verreDeSang = new Verre(50); Console.WriteLine($"Le verre contient {verreDeSang.Volume}cl de sang"); Monstre dracula = new Monstre("Dracula", 10, 10, 200); dracula.Affiche(); // Dracula voit le verre et se déplace dracula.SeDeplace(20, 30); // mais reçoit un projectile dracula.EstTouche(6); dracula.Affiche("est touché"); // Toujours en vie il boit dracula.boit(10, verreDeSang); Console.WriteLine($"Il reste {verreDeSang.Volume}cl dans le verre de sang"); } } } </code> //Résultat// \\ 1. Le verre contient 50 cl de sang \\ 2. Dracula : pos_x=20 pos_y=30 points de vie=94 : touché ! \\ 3. Le nombre de monstres en vie est : 1 \\ 4. Il reste 40 cl dans le verre de sang </pane> </tabs> <callout type="info" title="Communication possible entre objets" icon="true">//"Deux objets pourront communiquer si les deux classes correspondantes possèdent entre elles une liaison de type **association** (exemple 1), **composition** (exemple 2) ou **dépendance** (exemple 3)., la force et la durée de la liaison allant décroissant avec le type de liaison. La communication sera dans les deux premiers cas possibles, quelle que soit l'activité entreprise par le premier objet, alors que dans le troisième cas elle se déroulera uniquement durant l'exécution des seules méthodes du premier objet, qui recevront de façon temporaire un référent du second."// "Bersini</callout> ===4.2 Association bidirectionnelle=== {{ :micropython:diagclasse.png?nolink&150|}} Lecture du **diagramme de classes** : chaque oeuvre est **associée** à un ensemble d'exemplaires (1..n) et chaque exemplaire est associé à une et une seule oeuvre. <tabs> * [[#tab-python_8|Python]] * [[#tab-csharp_8|C#]] <pane id="tab-python_8"> //Exemple 1// </pane> <pane id="tab-csharp_8"> </pane> </tabs> ==== 5. Héritage et polymorphisme ==== === 5.1 Héritage === <callout type="info" icon="true">L'héritage est l'un des principes clés de la POO et permet de créer de nouvelles classes (appelées classes dérivées ou sous-classes) à partir d'une classe existante (appelée classe de base ou superclasse).</callout> <tabs> * [[#tab-python_8|Python]] * [[#tab-csharp_8|C#]] <pane id="tab-python_8"> //Syntaxe// <code python> class nomClasse(nomClasseMère) # nomClasse hérite de nomClasseMère pass # Évite qu'une erreur ne se produise lorsque la méthode est vide </code> </pane> <pane id="tab-csharp_8"> //Mot-clé// \\ //Syntaxe// \\ <code csharp *.cs </code> //Exemple//\\ <code csharp *.cs </code> </pane> </tabs> \\ \\ === 5.2 Polymorphisme === <callout type="primary" icon="true">Le polymorphisme est un concept clé de la programmation orientée objet (POO) qui permet à des objets d'une même hiérarchie de classes de se comporter différemment en fonction du contexte. En d'autres termes, il permet à des objets de types différents d'être traités de manière uniforme en utilisant des méthodes communes, tout en offrant des implémentations spécifiques pour chaque type d'objet.</callout> <tabs> * [[#tab-python_9|Python]] * [[#tab-csharp_9|C#]] <pane id="tab-python_9"> //Exemple// <code python expoly.py> # Dans l'exemple défini plus haut # Redéfinition de la méthode str héritée de object def __str__(self): return "Elève : " + self.__prenom + " " + self.__nom + " " + str(self.__age) + " " + "ans" </code> </pane> <pane id="tab-csharp_9"> //Mot-clé// \\ //Syntaxe// \\ <code csharp *.cs </code> //Exemple//\\ <code csharp *.cs </code> </pane> </tabs> \\ \\ ==== Résumé ==== * La POO repose sur l'abstraction, l'**encapsulation**, l'**héritage** et le **polymorphisme**. * Une **classe** permet de définir un ensemble d'objets ayant des caractéristiques communes. C'est un "**moule à objets**". Les **objets** sont des **instances** de classe. * Définir une classe consiste à définir les attributs et les méthodes caractérisant les objets instances de la classe. * Les **attributs** sont déclarés **privés** afin d'en interdire l'accès à l'extérieure de la classe. C'est ce qu'on appelle l'**encapsulation**. * A finir ==== Pour aller plus loin ==== * **Real Python** * <html><a href="https://realpython.com/python3-object-oriented-programming/" target="_blank">Object-Oriented Programming (OOP) in Python 3</a></html> * <html><a href="https://betterprogramming.pub/what-is-pythons-self-argument-anyway-21d1c3bc421f" target="_blank">What Is Python’s “Self” Argument, Anyway?</a></html> * <html><a href="https://realpython.com/python-getter-setter/" target="_blank">Getters and Setters: Manage Attributes in Python</a></html> * <html><a href="https://realpython.com/inheritance-composition-python/" target="_blank">Inheritance and Composition: A Python OOP Guide</a></html> * <html><a href="https://realpython.com/oop-in-python-vs-java/" target="_blank">Object-Oriented Programming in Python vs Java</a></html> python/poo/poo.txt Dernière modification : 2023/07/04 11:24de phil