Explorez la programmation orientée objet avec des classes et des objets

Dans ce tutoriel, vous allez créer une application console et voir les fonctionnalités orientées objet de base qui font partie du langage C#.

Prérequis

Créer votre application

Dans une fenêtre de terminal, créez un répertoire nommé classes. Vous y créerez votre application. Sélectionnez ce répertoire et tapez dotnet new console dans la fenêtre de console. Cette commande crée votre application. Ouvrez Program.cs. Il doit se présenter comme suit :

// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");

Dans ce tutoriel, vous allez créer des types qui représentent un compte bancaire. Les développeurs définissent généralement chaque classe dans un fichier texte différent. Cela simplifie la gestion au fur et à mesure qu’un programme augmente en taille. Créez un fichier nommé BankAccount.cs dans le répertoire Classes .

Ce fichier contient la définition d’un compte bancaire. La programmation orientée objet organise le code en créant des types sous la forme de classes. Ces classes contiennent le code qui représente une entité spécifique. La classe BankAccount représente un compte bancaire. Le code implémente des opérations spécifiques à travers des méthodes et des propriétés. Dans ce tutoriel, le compte bancaire prend en charge le comportement suivant :

  1. Il contient un numéro à 10 chiffres qui identifie le compte bancaire de manière unique.
  2. Il contient une chaîne qui stocke le nom du ou des détenteurs.
  3. Le solde peut être récupéré.
  4. Il accepte les dépôts.
  5. Il accepte les retraits.
  6. Le solde initial doit être positif.
  7. Les retraits ne peuvent pas entraîner un solde négatif.

Définir le type de compte bancaire

Vous pouvez commencer par créer les éléments de base d’une classe définissant ce comportement. Créez un fichier à l’aide de la commande File:New . Nommez-le BankAccount.cs. Ajoutez le code suivant à votre fichier BankAccount.cs :

namespace Classes;

public class BankAccount
{
    public string Number { get; }
    public string Owner { get; set; }
    public decimal Balance { get; }

    public void MakeDeposit(decimal amount, DateTime date, string note)
    {
    }

    public void MakeWithdrawal(decimal amount, DateTime date, string note)
    {
    }
}

Avant de poursuivre, examinons ce que vous venez de créer. La déclaration namespace permet d’organiser logiquement votre code. Ce tutoriel étant relativement petit, vous allez placer tout le code dans un même espace de noms.

public class BankAccount définit la classe ou le type que vous créez. Tout ce qui suit la {} déclaration de classe définit l’état et le comportement de la classe. Il y a cinq membres de la BankAccount classe. Les trois premières sont des propriétés. Les propriétés sont des éléments de données qui peuvent avoir un code qui applique la validation ou d’autres règles. Les deux dernières sont des méthodes. Les méthodes sont des blocs de code qui effectuent une fonction unique. La lecture des noms de chacun des membres doit fournir suffisamment d’informations pour vous permettre (ou à tout autre développeur) de comprendre ce que fait la classe.

Ouvrir un nouveau compte

La première fonctionnalité à implémenter est l’ouverture d’un compte bancaire. Quand un client ouvre un compte, il doit fournir un solde initial, ainsi que des informations sur le ou les détenteurs du compte.

La création d’un objet du BankAccount type signifie définir un constructeur qui affecte ces valeurs. Un constructeur est un membre qui porte le même nom que la classe. Il est utilisé pour initialiser des objets de ce type de classe. Ajoutez le constructeur suivant au BankAccount type. Placez le code suivant au-dessus de la déclaration de MakeDeposit:

public BankAccount(string name, decimal initialBalance)
{
    this.Owner = name;
    this.Balance = initialBalance;
}

Le code précédent identifie les propriétés de l’objet en cours de construction en incluant le this qualificateur. Ce qualificateur est généralement facultatif et omis. Vous pouvez également avoir écrit :

public BankAccount(string name, decimal initialBalance)
{
    Owner = name;
    Balance = initialBalance;
}

Le this qualificateur n’est requis que lorsqu’une variable ou un paramètre local a le même nom que ce champ ou cette propriété. Le this qualificateur est omis tout au long du reste de cet article, sauf s’il est nécessaire.

Les constructeurs sont appelés quand vous créez un objet à l’aide de new. Remplacez la ligne Console.WriteLine("Hello World!"); dans Program.cs par le code suivant (remplacez <name> par votre nom) :

using Classes;

var account = new BankAccount("<name>", 1000);
Console.WriteLine($"Account {account.Number} was created for {account.Owner} with {account.Balance} initial balance.");

Nous allons exécuter ce que vous avez créé jusqu’à présent. Si vous utilisez Visual Studio, sélectionnez Démarrer sans débogage dans le menu Débogage. Si vous utilisez une ligne de commande, tapez dotnet run le répertoire dans lequel vous avez créé votre projet.

Avez-vous remarqué que le numéro de compte est vide ? L’heure est venue de traiter ce point. Le numéro de compte doit être assigné une fois l’objet construit. Mais ce ne devrait pas être à l’appelant de le créer. Le code de la classe BankAccount devrait savoir comment assigner de nouveaux numéros de compte. Un moyen simple consiste à commencer par un nombre à 10 chiffres. Incrémentez-le chaque fois qu’un compte est créé. Enfin, stockez le numéro de compte actuel quand un objet est construit.

Ajoutez une déclaration de membre à la BankAccount classe. Placez la ligne de code suivante après l’accolade ouvrante { au début de la BankAccount classe :

private static int accountNumberSeed = 1234567890;

Il s’agit accountNumberSeed d’un membre de données. Celui-ci est private, ce qui signifie qu’il est uniquement accessible par code dans la classe BankAccount. Il s’agit d’un moyen de séparer les responsabilités publiques (comme avoir un numéro de compte) de l’implémentation privée (comment les numéros de compte sont générés). Il est également static, ce qui signifie qu’il est partagé par tous les BankAccount objets. La valeur d’une variable non statique est unique pour chaque instance de l’objet BankAccount. Ajoutez les deux lignes suivantes au constructeur pour affecter le numéro de compte. Placez-les après la ligne qui indique this.Balance = initialBalance:

this.Number = accountNumberSeed.ToString();
accountNumberSeed++;

Tapez dotnet run pour afficher les résultats.

Créer des dépôts et des retraits

La classe de votre compte bancaire doit accepter les dépôts et les retraits pour fonctionner correctement. Nous allons implémenter des dépôts et des retraits en créant un journal de chaque transaction du compte. Le suivi de chaque transaction présente quelques avantages par rapport à la mise à jour du solde sur chaque transaction. L’historique peut être utilisé pour auditer toutes les transactions et gérer les soldes au quotidien. Le calcul de l’équilibre à partir de l’historique de toutes les transactions si nécessaire garantit que les erreurs dans une seule transaction qui sont corrigées seront correctement reflétées dans le solde lors du calcul suivant.

Commençons par créer un type pour représenter une transaction. La transaction est un type simple qui n’a aucune responsabilité. Il a besoin de quelques propriétés. Créez un fichier nommé Transaction.cs. Ajoutez-lui le code suivant :

namespace Classes;

public class Transaction
{
    public decimal Amount { get; }
    public DateTime Date { get; }
    public string Notes { get; }

    public Transaction(decimal amount, DateTime date, string note)
    {
        Amount = amount;
        Date = date;
        Notes = note;
    }
}

Nous allons maintenant ajouter une List<T> d’objets Transaction à la classe BankAccount. Ajoutez la déclaration suivante après le constructeur dans votre fichier BankAccount.cs :

private List<Transaction> allTransactions = new List<Transaction>();

Maintenant, nous allons calculer correctement le Balance. Le solde actuel est disponible en additionnant les valeurs de toutes les transactions. Comme le code est actuellement, vous ne pouvez obtenir que le solde initial du compte. Vous devrez donc mettre à jour la Balance propriété. Remplacez la ligne public decimal Balance { get; } dans BankAccount.cs par le code suivant :

public decimal Balance
{
    get
    {
        decimal balance = 0;
        foreach (var item in allTransactions)
        {
            balance += item.Amount;
        }

        return balance;
    }
}

Cet exemple montre un aspect important des propriétés. Vous calculez à présent le solde quand un autre programmeur en demande la valeur. Votre calcul énumère toutes les transactions et retourne la somme en tant que solde actuel.

Implémentez ensuite les méthodes MakeDeposit et MakeWithdrawal. Ces méthodes appliquent les deux règles finales : le solde initial doit être positif et tout retrait ne doit pas créer de solde négatif.

Ces règles introduisent le concept d’exceptions. La méthode standard d’indication qu’une méthode ne peut pas terminer son travail correctement consiste à lever une exception. Le type d’exception et le message associé décrivent l’erreur. Ici, la MakeDeposit méthode lève une exception si le montant du dépôt n’est pas supérieur à 0. La MakeWithdrawal méthode lève une exception si le montant du retrait n’est pas supérieur à 0 ou si l’application du retrait entraîne un solde négatif. Ajoutez le code suivant après la déclaration de la allTransactions liste :

public void MakeDeposit(decimal amount, DateTime date, string note)
{
    if (amount <= 0)
    {
        throw new ArgumentOutOfRangeException(nameof(amount), "Amount of deposit must be positive");
    }
    var deposit = new Transaction(amount, date, note);
    allTransactions.Add(deposit);
}

public void MakeWithdrawal(decimal amount, DateTime date, string note)
{
    if (amount <= 0)
    {
        throw new ArgumentOutOfRangeException(nameof(amount), "Amount of withdrawal must be positive");
    }
    if (Balance - amount < 0)
    {
        throw new InvalidOperationException("Not sufficient funds for this withdrawal");
    }
    var withdrawal = new Transaction(-amount, date, note);
    allTransactions.Add(withdrawal);
}

L’instruction throwlève une exception. L’exécution du bloc actuel se termine et le contrôle est transféré au premier bloc correspondant catch trouvé dans la pile des appels. Vous ajouterez un bloc catch pour tester ce code un peu plus tard.

Le constructeur devrait obtenir une modification lui permettant d’ajouter une transaction initiale au lieu de mettre directement le solde à jour. Étant donné que vous avez déjà écrit la méthode MakeDeposit, appelez-la à partir de votre constructeur. Le constructeur terminé doit être semblable à ce qui suit :

public BankAccount(string name, decimal initialBalance)
{
    Number = accountNumberSeed.ToString();
    accountNumberSeed++;

    Owner = name;
    MakeDeposit(initialBalance, DateTime.Now, "Initial balance");
}

DateTime.Now est une propriété qui retourne la date et l'heure actuelles. Testez ce code en ajoutant quelques dépôts et retraits dans votre Main méthode, en suivant le code qui crée un nouveau BankAccount:

account.MakeWithdrawal(500, DateTime.Now, "Rent payment");
Console.WriteLine(account.Balance);
account.MakeDeposit(100, DateTime.Now, "Friend paid me back");
Console.WriteLine(account.Balance);

Ensuite, testez que vous interceptez les conditions d’erreur en essayant de créer un compte avec un solde négatif. Ajoutez le code suivant après le code précédent que vous venez d’ajouter :

// Test that the initial balances must be positive.
BankAccount invalidAccount;
try
{
    invalidAccount = new BankAccount("invalid", -55);
}
catch (ArgumentOutOfRangeException e)
{
    Console.WriteLine("Exception caught creating account with negative balance");
    Console.WriteLine(e.ToString());
    return;
}

Vous utilisez les instructions et catch lestry instructions pour marquer un bloc de code qui peut lever des exceptions et intercepter ces erreurs attendues. Vous pouvez utiliser la même technique pour tester le code qui lève une exception pour un solde négatif. Ajoutez le code suivant avant la déclaration de invalidAccount votre Main méthode :

// Test for a negative balance.
try
{
    account.MakeWithdrawal(750, DateTime.Now, "Attempt to overdraw");
}
catch (InvalidOperationException e)
{
    Console.WriteLine("Exception caught trying to overdraw");
    Console.WriteLine(e.ToString());
}

Enregistrez le fichier et tapez dotnet run pour effectuer un essai.

Test : consigner toutes les transactions

Pour terminer ce tutoriel, vous pouvez écrire la méthode GetAccountHistory qui crée une string pour l’historique des transactions. Ajoutez cette méthode au type BankAccount :

public string GetAccountHistory()
{
    var report = new System.Text.StringBuilder();

    decimal balance = 0;
    report.AppendLine("Date\t\tAmount\tBalance\tNote");
    foreach (var item in allTransactions)
    {
        balance += item.Amount;
        report.AppendLine($"{item.Date.ToShortDateString()}\t{item.Amount}\t{balance}\t{item.Notes}");
    }

    return report.ToString();
}

L’historique utilise la StringBuilder classe pour mettre en forme une chaîne qui contient une ligne pour chaque transaction. Vous avez vu le code de mise en forme de chaîne précédemment dans ces tutoriels. Vous pouvez observer le nouveau caractère \t. Celui-ci insère une tabulation pour mettre en forme la sortie.

Ajoutez cette ligne pour effectuer un essai dans Program.cs :

Console.WriteLine(account.GetAccountHistory());

Exécutez votre programme pour afficher les résultats.

Étapes suivantes

Si vous êtes bloqué, vous pouvez voir la source de ce didacticiel dans notre dépôt GitHub.

Vous pouvez poursuivre le didacticiel de programmation orienté objet .

Vous pouvez en savoir plus sur ces concepts dans les articles suivants :