Programmation orientée objet #
Introduction à la programmation orientée objet (POO) #
Définition de la POO #
La programmation orientée objet (POO) est un concept essentiel en programmation informatique. Dans le jargon, on dit de la POO qu’elle est un paradigme, c’est-à-dire une approche, une méthode.
Objectifs et avantages #
Le but de la POO consiste à définir et faire interagir entre eux des “objets” pour faciliter l’écriture et la maintenance du code.
Les éléments de base de la POO #
Classe #
Une classe est un plan ou un modèle qui permet de créer des objets. Elle contient des attributs (qui représentent les données) et des méthodes (qui définissent les comportements). Une classe est réutilisable et facilite la standardisation du code dans un projet de programmation.
Objet #
un objet représente un concept, une idée ou toute entité du monde physique.
Exemples : une voiture, une personne, une page d’un livre…
L’objet possède en interne une structure et un comportement, et il sait interagir avec ses pairs.
Constructeur #
Un constructeur est une méthode spéciale d’une classe, appelée automatiquement lors de la création d’un objet. Il permet de définir les valeurs initiales des attributs et ne retourne rien. Par exemple, le constructeur d’une classe Voiture peut initialiser la marque et le modèle.
Méthodes, fonctions et static
#
Une méthode est une fonction interne à une classe. Elle détermine le comportement d’un objet et peut retourner une valeur.
Par exemple, la méthode afficherInfos() d’un objet Client peut afficher son nom et son adresse.
Toutes les méthodes sont des fonctions, mais toutes les fonctions ne sont pas des méthodes.
| Critère | Fonction | Méthode |
|---|---|---|
| Définition | Bloc de code qui exécute une action, souvent indépendant | Fonction associée à une classe ou un objet |
| Appartenance | N’appartient pas nécessairement à une classe | Appartient à une classe (ou à un objet) |
| Appel | Peut être appelée seule, directement par son nom (selon le langage) | Nécessite un objet ou la classe pour être appelée |
| Utilisation principale | Réaliser un traitement général, souvent réutilisable partout | Décrire un comportement ou une action propre à un objet |
| Accès aux attributs | N’a pas accès aux attributs d’un objet (sauf si on lui passe en paramètre) | Peut accéder aux attributs de l’objet via this (ou self en Python) |
| Type | Concept plutôt “général” | Spécifique à la POO (Programmation Orientée Objet) |
Une méthode statique appartient à la classe et n’a pas besoin d’un objet pour être appeler.
Cela facilite :
-
Les méthode utilitaires
-
La création de petites librairies génériques
Exemple :
Math.sqrt(16); // méthode statique, utilisation sans instance
Les grands principes de la POO #
Encapsulation #
L’encapsulation consiste à contrôler l’accès aux membres d’une classe (attributs et méthodes). Un attribut private n’est accessible que dans la classe, un membre protected dans la classe et ses sous-classes, et un membre public est accessible partout. Ce principe protège les données et garantit une meilleure sécurité du code.
| Modificateur | Accessible dans la classe | Sous-classes | Extérieur |
|---|---|---|---|
| private | Oui | Non | Non |
| protected | Oui | Oui | Non |
| public | Oui | Oui | Oui |
Exemple :
// Attributs avec différents niveaux de visibilité
public nom: string; // Accessible partout
protected age: number; // Accessible dans la classe + classes enfants
private password: string; // Accessible uniquement dans cette classe
```
#### Héritage
L’héritage permet à une classe d’hériter des attributs et méthodes d’une autre, appelée classe parente.
Ainsi, la classe fille peut réutiliser le code existant et spécialiser son comportement.
Le mot-clé final empêche une classe d’être héritée.
Exemple :
```ts
import { Person } from "./person";
export class Student extends Person {
public sayHello(): void {
throw new Error("Method not implemented.");
}
}
Polymorphisme #
Le polymorphisme permet d’utiliser des méthodes portant le même nom, mais ayant des comportements différents selon :
-
Le type de l’objet (redéfinition).
-
La signature de la méthode (surcharge).
Exemple :
class Animal {
faireSon(): void {
console.log("L’animal fait un son.");
}
}
class Chien extends Animal {
faireSon(): void {
console.log("Le chien aboie : Ouaf !");
}
}
class Chat extends Animal {
faireSon(): void {
console.log("Le chat miaule : Miaou !");
}
}
const animaux: Animal[] = [new Chien(), new Chat()];
animaux.forEach(a => a.faireSon());
// Ouaf !
// Miaou !
Abstraction #
Les interfaces et classes abstraites #
Interface #
Une interface définit un ensemble de méthodes communes à plusieurs classes, sans les implémenter. Elle sert de contrat entre classes et favorise la standardisation et la modularité.
Par exemple, l’interface NameStrategy peut imposer la méthode transform() à plusieurs classes.
import { Person } from "./../person";
export interface NameStrategy {
/
* Transform some string into others
* @param person: Person
* @returns string
*/
transform(person: Person): string
}
Pour implémenter une interface à une classe, le code est le suivant :
import type { Person } from "../person";
import type { NameStrategy } from "./name-strategy";
export class NameFirstStrategy implements NameStrategy {
transform(person: Person): string {
return person.getName() + ' ' + person.getFirstname()
}
}
Les types génériques associé à une interface #
Le paramètre générique <T> indique qu’un type quelconque peut être accepté.
Il permet de rendre une interface ou une classe flexible et réutilisable, sans imposer un type fixe à l’avance.
Objectif :
-
Adapter le code à différents types
-
Garantir la cohérence du contrat entre les classes
Une interface générique, comme IBuilder<T>, ne contient pas de code exécuté mais définit un contrat que les classes doivent respecter.
Exemple d’interface générique :
public interface IBuilder<T> {
T build();
}
Cette interface impose seulement :
-
Une méthode
build() -
Qui retourne un objet du type générique
<T>
Quand une classe implémente l’interface générique, elle précise le type concret.
Exemple :
PersonBuilder<Person> → ici, T = Person
Ce que cela implique :
-
La classe doit obligatoirement implémenter
build() -
Elle peut être abstraite pour servir de base à d’autres classes
-
L’interface reste sans code, uniquement structurelle
Les bonnes pratiques pour les interfaces :
-
Préférer plusieurs petites interfaces cohérentes plutôt qu’une seule avec trop de méthodes
-
Une interface = contrat
-
Une classe abstraite peut avoir des attributs et du code, contrairement à une interface
Classe Abstraite #
Une classe abstraite est une classe non instanciable, servant de modèle pour d’autres classes. Elle peut contenir à la fois des méthodes implémentées et des méthodes abstraites (sans corps). Elle fournit une base commune pour plusieurs classes dérivées.
Exemple :
export abstract class Person {
protected name: string = '';
protected firstname: string = '';
protected email: string = '';
protected phone: string = '';
public birthdate: Date = new Date();
public gender: string = '';
constructor() {}
public abstract sayHello(): void;
}
Différence entre Classe Abstraite et Interface #
| Aspect | Classe Abstraite | Interface |
|---|---|---|
| Contenu | Méthodes avec ou sans corps | Méthodes sans corps uniquement |
| Héritage | Une seule classe abstraite par classe | Plusieurs interfaces possibles |
| Usage | Sert de base commune avec comportements partiels | Sert de contrat entre classes |
Principes SOLID #
En programmation orientée objet, SOLID est un acronyme mnémonique qui regroupe cinq principes de conception destinés à produire des architectures logicielles plus compréhensibles, flexibles et maintenables.
S — Single Responsibility Principle
→ Une classe doit avoir une seule responsabilité.
O — Open/Closed Principle
→ Une classe doit être ouverte à l’extension mais fermée à la modification.
L — Liskov Substitution Principle
→ Une sous-classe doit pouvoir remplacer sa classe mère sans altérer le comportement du programme.
Exemple : un Carré hérite d’un Rectangle, mais doit garantir que largeur = longueur.
I — Interface Segregation Principle
→ Il vaut mieux plusieurs petites interfaces qu’une seule interface trop lourde.
D — Dependency Inversion Principle
→ Les modules de haut niveau ne doivent pas dépendre de modules bas niveau, mais d’abstractions.