Dans la jungle du tas, il n'y a pas que des variables solitaires.
Il y a des familles. Des hiérarchies. Des lignées qui se disputent la mémoire.
Bienvenue dans la Programmation Orientée Objet.
Où chaque classe a ses secrets, et chaque héritage cache un couteau.
5. Classes et objets
Définition d'une classe
Un moule à objets. Un contrat. Un plan pour fabriquer des entités qui vivront, respireront, et mourront dans le tas.
public class Tueur
{
// L'intérieur. Le caché.
// Ce qui fait de lui ce qu'il est.
}
Champs, propriétés, méthodes
La trinité du pouvoir.
public class Espion
{
// Champ - la donnée brute, souvent privée
private string _nomDeCode; // Tu ne dois pas savoir
// Propriété - l'interface contrôlée
public string Identite { get; set; } // Ce que je veux bien te montrer
// Méthode - l'action
public void RemplirRapport(string details)
{
// Le travail se fait ici
Console.WriteLine($"Rapport : {details}");
_nomDeCode = "Ombre"; // On peut toucher aux champs privés
}
}
Encapsulation
L'art de cacher ce qui doit l'être. Le premier principe. Le plus violé.
public class CoffreFort
{
private int _codeSecret = 4729; // Privé. Inaccessible.
private int _tentatives = 0;
// Propriété avec validation
public decimal Montant
{
get => _montant;
set
{
if (value >= 0) // Garde-fou
_montant = value;
else
Console.WriteLine("Vol détecté.");
}
}
private decimal _montant;
// Méthode publique pour interagir avec le privé
public bool TenterCode(int code)
{
_tentatives++;
return code == _codeSecret; // Retourne un bool, garde le secret
}
}
Constructeurs
L'accouchement d'un objet. Le moment où la mémoire s'alloue et où tout commence.
public class Contrat
{
public string Cible { get; }
public decimal Prix { get; }
public DateTime Echeance { get; }
// Constructeur - donne vie avec des valeurs initiales
public Contrat(string cible, decimal prix, DateTime echeance)
{
// Validation au berceau
if (string.IsNullOrEmpty(cible))
throw new ArgumentException("Une cible doit avoir un nom.");
Cible = cible;
Prix = prix;
Echeance = echeance;
Console.WriteLine($"Nouveau contrat : {cible} - {prix:C}");
}
// Surcharge - un autre chemin vers la vie
public Contrat(string cible) : this(cible, 50000, DateTime.Now.AddDays(7))
{
// Prix et échéance par défaut
// 'this' appelle le constructeur principal
}
}
// Utilisation
var contrat1 = new Contrat("Le Baron", 100000, DateTime.Now.AddDays(3));
var contrat2 = new Contrat("Le Rat"); // Utilise les valeurs par défaut
6. Propriétés avancées
Propriétés automatiques
La paresse élégante. Le compilateur fait le sale boulot.
public class Arme
{
// Propre, concis
public string Modele { get; set; } = "Glock 17"; // Avec valeur par défaut
public int Munitions { get; set; } = 12;
// Propriété calculée - une illusion maintenue
public bool EstVide => Munitions <= 0; // Expression-bodied
}
Accesseurs get / set personnalisés
Quand la propriété simple ne suffit plus.
public class Portefeuille
{
private decimal _argent = 0;
public decimal Argent
{
get
{
Console.WriteLine($"Quelqu'un regarde l'argent : {_argent:C}");
return _argent;
}
set
{
if (value < 0)
{
Console.WriteLine("Tentative de dette détectée.");
return;
}
decimal difference = value - _argent;
if (difference > 10000)
Console.WriteLine("Alerte : gros dépôt suspect!");
_argent = value;
}
}
}
Propriétés en lecture seule
Une promesse gravée dans le marbre.
public class Dossier
{
// Initialisation soit dans le constructeur...
public string Numero { get; } // Pas de set !
// ...soit directement
public DateTime DateCreation { get; } = DateTime.Now;
public Dossier(string numero)
{
Numero = numero; // Une seule chance de définir
// Après ça, Numero est immuable
}
// Expression-bodied property
public bool EstExpire => DateCreation.AddYears(10) < DateTime.Now;
}
Initialisateurs d'objets
La construction rapide. Trop rapide parfois.
// Au lieu de...
var espion1 = new Espion();
espion1.Nom = "Bond";
espion1.Matricule = "007";
espion1.EstActif = true;
// ...on peut faire
var espion2 = new Espion
{
Nom = "Bourne",
Matricule = "Delta",
EstActif = true
// Attention : le constructeur s'exécute D'ABORD
// Puis les propriétés sont settées
};
// Avec une classe anonyme (danger : pas de type défini)
var cible = new
{
Nom = "Drax",
Localisation = "Venise",
Dangerosite = 9
};
7. Héritage et polymorphisme
Héritage simple
L'ADN du code. Un parent, un enfant. Des gènes partagés, des secrets révélés.
public class Personne // Classe mère
{
public string Nom { get; set; }
public DateTime DateNaissance { get; set; }
public virtual void SePresenter()
{
Console.WriteLine($"Je m'appelle {Nom}.");
}
}
public class Tueur : Personne // Tueur hérite de Personne
{
public string Specialite { get; set; }
// Redéfinition
public override void SePresenter()
{
base.SePresenter(); // Appelle la version parent
Console.WriteLine($"Spécialité : {Specialite}.");
}
// Nouvelle méthode spécifique
public void ExecuterContrat(string cible)
{
Console.WriteLine($"Contrat sur {cible} exécuté.");
}
}
Méthodes virtuelles
Des portes laissées entrouvertes pour que les enfants les poussent.
public class Agent
{
public virtual string ObtenirIdentite()
{
return "Identité classifiée";
}
// Méthode scellée - pas de redéfinition possible
public sealed void TerminerMission()
{
Console.WriteLine("Mission terminée.");
}
}
public class AgentDouble : Agent
{
// On peut redéfinir le virtuel
public override string ObtenirIdentite()
{
return "L'identité change selon le jour";
}
// Mais pas le scellé
// public override void TerminerMission() { } // ERREUR
}
Classes abstraites
Des fantômes. Des modèles qui ne vivront jamais seuls.
public abstract class Operation // 'abstract' = on ne peut pas l'instancier
{
// Propriété abstraite - doit être implémentée
public abstract string Code { get; }
// Méthode abstraite - doit être implémentée
public abstract void Executer();
// Méthode concrète - déjà implémentée
public void Preparer()
{
Console.WriteLine($"Préparation de l'opération {Code}");
}
}
public class Infiltration : Operation // Doit implémenter les abstractions
{
public override string Code => "NuitNoire";
public override void Executer()
{
Console.WriteLine("Infiltration en cours...");
// Code spécifique
}
}
// var op = new Operation(); // ERREUR : abstraite!
var infiltration = new Infiltration(); // OK
8. Interfaces
Définition et implémentation
Des contrats sans chair. Des promesses que d'autres tiendront.
public interface IAssassin // Convention : 'I' au début
{
// Pas de champs, pas d'implémentation
// Juste des signatures
string NomDeCode { get; }
void ExecuterCible(string cible);
decimal DemanderPrix();
}
public interface IInfiltrable
{
bool PeutInfiltrer(string lieu);
}
// Une classe peut implémenter plusieurs interfaces
public class Mercenaire : IAssassin, IInfiltrable
{
public string NomDeCode { get; set; }
public void ExecuterCible(string cible)
{
Console.WriteLine($"{NomDeCode} élimine {cible}");
}
public decimal DemanderPrix()
{
return 50000m;
}
public bool PeutInfiltrer(string lieu)
{
return lieu != "Fort Knox"; // Même les mercenaires ont des limites
}
}
Différences classe abstraite / interface
Le choix entre un parent exigeant et plusieurs patrons.
// CLASSE ABSTRACITE :
// - Peut avoir des implémentations
// - Héritage simple uniquement
// - Peut avoir des champs
// - Modélise "EST UN"
// INTERFACE :
// - Pas d'implémentation
// - Implémentation multiple
// - Pas de champs (avant C# 8)
// - Modélise "PEUT FAIRE"
Injection par interface
La puissance du découplage. Travailler avec des fantômes.
public class ContratManager
{
private readonly IAssassin _assassin;
// On injecte une interface, pas une classe concrète
public ContratManager(IAssassin assassin)
{
_assassin = assassin; // Peut être n'importe quel IAssassin
}
public void FaireExecuterContrat(string cible)
{
decimal prix = _assassin.DemanderPrix();
Console.WriteLine($"Prix : {prix:C}");
_assassin.ExecuterCible(cible);
// On ne sait pas qui exécute, ni comment
// On sait juste qu'il respecte le contrat
}
}
// Usage
IAssassin tueur = new Mercenaire { NomDeCode = "L'Ombre" };
var manager = new ContratManager(tueur);
manager.FaireExecuterContrat("Le Traître");
// Demain, on pourra injecter un autre type d'assassin
// Sans changer ContratManager
La fin de l'héritage.
Tu sais maintenant créer des familles d'objets.
Des hiérarchies qui s'étendent dans l'ombre.
Des contrats que certains remplissent, d'autres non.
Mais rappelle-toi :
L'héritage est un couteau à double tranchant.
Trop profond, et tu te noies dans la complexité.
Trop large, et tout devient public.
Une interface, c'est une promesse.
Une classe abstraite, c'est un destin partagé.
Un constructeur, c'est une naissance avec conditions.
Le prochain chapitre parlera des exceptions.
Des choses qui se brisent.
Des erreurs qu'on attrape, ou qu'on laisse tomber.
Dans le monde des objets, tout peut échouer.
Apprends à gérer l'échec.
Ou prépare-toi à planter proprement.