Encapsulation en Programmation Orientée Objet

L'encapsulation en programmation orientée objet consiste à regrouper les données et les méthodes qui les manipulent au sein d'une même entité, tout en contrôlant l'accès externe. Cela va au-delà de la simple dissimulation des détails internes.

Dissimulation des attributs en Python

En Python, on peut rendre des attributs privés en les préfixant avec deux undersocres. Ce mécanisme applique une transformation de noms lors de la définition de la classe, où les noms comme __attribut deviennent _NomClasse__attribut.

class CompteBancaire:
    __solde = 0

    def __init__(self, titulaire):
        self.titulaire = titulaire
        self.__fonds = 1000  # Transformé en _CompteBancaire__fonds

    def _calculer_frais(self):  # Transformé en _CompteBancaire__calculer_frais
        print('Calcul des frais')

    def afficher_solde(self):
        self.__fonds = self.__solde + 100  # Accès interne uniquement

# Les attributs transformés peuvent être accédés de l'extérieur avec le nom transformé
# instance._CompteBancaire__fonds est valide, mais cela contourne l'encapsulation.

Points importants à noter :

  • Cette transformation ne bloque pas véritablement l'accès externe ; elle le rend seulement plus difficile.
  • La transformation se produit une seule fois lors de la définition de la classe, pas lors des affectations ultérieures.
  • Dans l'héritage, une méthode privée empêche la substitution par les classes dérivées.
class A:
    def __methode_privee(self):
        print('Méthode de A')

    def executer(self):
        self.__methode_privee()

class B(A):
    def __methode_privee(self):
        print('Méthode de B')

instance = B()
instance.executer()  # Appelle _A__methode_privee, affichant 'Méthode de A'

Encapsulation au-delà de la dissimulation

L'objectif principal de l'encapsulation est de définir des interfaces claires pour interagir avec les attributs cachés. Cela permet d'ajouter des contrôles ou de simplifier l'utilisation.

Exemple de données encapsulées avec validation :

class Employe:
    def __init__(self, nom, age):
        self.definir_infos(nom, age)

    def afficher_infos(self):
        print(f'Nom : {self._Employe__nom}, Âge : {self._Employe__age}')

    def definir_infos(self, nom, age):
        if not isinstance(nom, str):
            raise TypeError('Le nom doit être une chaîne')
        if not isinstance(age, int):
            raise TypeError('L\'âge doit être un entier')
        self.__nom = nom
        self.__age = age

employe = Employe('Marie', 30)
employe.afficher_infos()
employe.definir_infos('Marie', 31)

Exemple de méthodes encapsulées pour isoler la complexité :

class DistributeurATM:
    def __inserer_carte(self):
        print('Insertion de la carte')
    def __authentifier(self):
        print('Authentification')
    def __saisir_montant(self):
        print('Saisie du montant')
    def __imprimer_recu(self):
        print('Impression du reçu')
    def __retirer_argent(self):
        print('Retrait')

    def effectuer_retrait(self):
        self.__inserer_carte()
        self.__authentifier()
        self.__saisir_montant()
        self.__imprimer_recu()
        self.__retirer_argent()

atm = DistributeurATM()
atm.effectuer_retrait()

En Python, la convention d'attributs avec un seul underscore indique un usage interne, mais ils restent accessibles. Pour un contrôle strict, des méthodes spéciales comme __getattr__ peuvent être utilisées.

Propriétés (property)

Les propriétés permettent d'accéder à des méthodes comme à des attributs, en exécutant du code lors de la lecture ou de l'écriture.

Exemple avec un calcul d'IMC :

class Personne:
    def __init__(self, nom, poids, taille):
        self.nom = nom
        self.poids = poids
        self.taille = taille

    @property
    def imc(self):
        return self.poids / (self.taille ** 2)

personne = Personne('Luc', 70, 1.75)
print(personne.imc)

Exemple avec un cercle :

import math

class Cercle:
    def __init__(self, rayon):
        self.rayon = rayon

    @property
    def aire(self):
        return math.pi * self.rayon ** 2

    @property
    def perimetre(self):
        return 2 * math.pi * self.rayon

cercle = Cercle(5)
print(cercle.aire)
print(cercle.perimetre)

Les propriétés peuvent inclure des setters et des deleteurs pour contrôler les modifications :

class Configuration:
    def __init__(self, valeur):
        self._valeur = valeur

    @property
    def parametre(self):
        return self._valeur

    @parametre.setter
    def parametre(self, valeur):
        if not isinstance(valeur, str):
            raise TypeError('La valeur doit être une chaîne')
        self._valeur = valeur

    @parametre.deleter
    def parametre(self):
        raise TypeError('Suppression interdite')

config = Configuration('défaut')
print(config.parametre)
config.parametre = 'nouvelle_valeur'
# del config.parametre lèverait une exception

Encapsulation et extensibilité

L'encapsulation permet de modifier l'implémentation interne sans affecter le code externe, tant que les interfaces restent inchangées.

class Piece:
    def __init__(self, nom, proprietaire, largeur, longueur):
        self.nom = nom
        self.proprietaire = proprietaire
        self.__largeur = largeur
        self.__longueur = longueur

    def calculer_surface(self):  # Interface externe
        return self.__largeur * self.__longueur

# Utilisation
piece = Piece('Salon', 'Alice', 5, 6)
print(piece.calculer_surface())  # Affiche 30

# Modification interne pour calculer le volume sans changer l'interface
class Piece:
    def __init__(self, nom, proprietaire, largeur, longueur, hauteur):
        self.nom = nom
        self.proprietaire = proprietaire
        self.__largeur = largeur
        self.__longueur = longueur
        self.__hauteur = hauteur

    def calculer_surface(self):  # Même nom, mais logique modifiée pour le volume
        return self.__largeur * self.__longueur * self.__hauteur

piece = Piece('Salon', 'Alice', 5, 6, 3)
print(piece.calculer_surface())  # Affiche 90, mais l'appel reste identique

Étiquettes: Python POO encapsulation propriété Héritage

Publié le 13 juillet à 22h06