Prozkoumání objektově orientovaného programování pomocí tříd a objektů

V tomto kurzu vytvoříte konzolovou aplikaci a zobrazíte základní objektově orientované funkce, které jsou součástí jazyka C#.

Požadavky

  • Doporučujeme Visual Studio pro Windows. Bezplatnou verzi si můžete stáhnout ze stránky pro stažení sady Visual Studio. Visual Studio obsahuje sadu .NET SDK.
  • Editor editoru Visual Studio Code můžete použít také s jazykem C# DevKit. Budete muset nainstalovat nejnovější sadu .NET SDK samostatně.
  • Pokud dáváte přednost jinému editoru, musíte nainstalovat nejnovější sadu .NET SDK.

Vytvořte si svoji aplikaci

Pomocí okna terminálu vytvořte adresář s názvem Třídy. Sestavíte tam svou aplikaci. Přejděte do daného adresáře a zadejte dotnet new console do okna konzoly. Tento příkaz vytvoří aplikaci. Otevřete soubor Program.cs. Měl by vypadat takto:

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

V tomto kurzu vytvoříte nové typy, které představují bankovní účet. Vývojáři obvykle definují každou třídu v jiném textovém souboru. To usnadňuje správu při rostoucí velikosti programu. V adresáři Třídy vytvořte nový soubor s názvem BankAccount.cs.

Tento soubor bude obsahovat definici bankovního účtu. Objektově orientované programování uspořádá kód vytvořením typů ve formě tříd. Tyto třídy obsahují kód, který představuje konkrétní entitu. Třída BankAccount představuje bankovní účet. Kód implementuje konkrétní operace prostřednictvím metod a vlastností. V tomto kurzu bankovní účet podporuje toto chování:

  1. Má 10místné číslo, které jednoznačně identifikuje bankovní účet.
  2. Obsahuje řetězec, do kterého se uloží název nebo jména vlastníků.
  3. Zůstatek lze načíst.
  4. Přijímá vklady.
  5. Přijímá výběry.
  6. Počáteční zůstatek musí být kladný.
  7. Výběry nemohou vést k zápornému zůstatku.

Definování typu bankovního účtu

Můžete začít vytvořením základů třídy, která definuje toto chování. Pomocí příkazu File:New vytvořte nový soubor. Pojmenujte ho BankAccount.cs. Do souboru BankAccount.cs přidejte následující kód:

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)
    {
    }
}

Než začneme, podívejme se, co jste vytvořili. Deklarace namespace poskytuje způsob, jak logicky uspořádat kód. Tento kurz je relativně malý, takže veškerý kód vložíte do jednoho oboru názvů.

public class BankAccount definuje třídu nebo typ, kterou vytváříte. Vše uvnitř { deklarace třídy, } které následuje za deklarací třídy, definuje stav a chování třídy. Existuje pět členůBankAccount třídy. První tři jsou vlastnosti. Vlastnosti jsou datové prvky a mohou mít kód, který vynucuje ověření nebo jiná pravidla. Poslední dvě jsou metody. Metody jsou bloky kódu, které provádějí jednu funkci. Čtení názvů jednotlivých členů by mělo poskytnout dostatek informací pro vás nebo jiného vývojáře, aby porozuměli tomu, co třída dělá.

Otevření nového účtu

První funkcí, která se má implementovat, je otevřít bankovní účet. Když zákazník otevře účet, musí zadat počáteční zůstatek a informace o vlastníkovi nebo vlastnících tohoto účtu.

Vytvoření nového objektu BankAccount typu znamená definování konstruktoru, který tyto hodnoty přiřadí. Konstruktor je člen, který má stejný název jako třída. Slouží k inicializaci objektů tohoto typu třídy. Do typu přidejte následující konstruktor BankAccount . Nad deklaraci MakeDeposit:

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

Předchozí kód identifikuje vlastnosti objektu vytvořeného zahrnutím kvalifikátoru this . Tento kvalifikátor je obvykle volitelný a vynechán. Mohli jste také napsat:

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

this Kvalifikátor se vyžaduje pouze v případě, že má místní proměnná nebo parametr stejný název jako toto pole nebo vlastnost. Kvalifikátor this se po zbytek tohoto článku vynechá, pokud není nutné.

Konstruktory se volají při vytváření objektu pomocí new. Řádek Console.WriteLine("Hello World!"); v Program.cs nahraďte následujícím kódem (nahraďte <name> svým jménem):

using Classes;

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

Pojďme spustit, co jste zatím vytvořili. Pokud používáte Visual Studio, v nabídce Ladění vyberte Spustit bez ladění. Pokud používáte příkazový řádek, zadejte dotnet run adresář, ve kterém jste projekt vytvořili.

Všimli jste si, že číslo účtu je prázdné? Je čas to opravit. Číslo účtu by mělo být přiřazeno při vytváření objektu. Nemělo by ale být zodpovědností volajícího, aby ho vytvořil. Kód BankAccount třídy by měl vědět, jak přiřadit nová čísla účtů. Jednoduchým způsobem je začít 10místným číslem. Při vytváření každého nového účtu ho navyšte. Nakonec při vytváření objektu uložte aktuální číslo účtu.

Přidejte deklaraci člena do BankAccount třídy. Umístěte následující řádek kódu za levou složenou závorku { na začátek BankAccount třídy:

private static int s_accountNumberSeed = 1234567890;

Jedná se accountNumberSeed o datový člen. To znamená private, že k němu může přistupovat pouze kód uvnitř BankAccount třídy. Je to způsob oddělení veřejných zodpovědností (například číslo účtu) od privátní implementace (způsob generování čísel účtů). Je to také static, což znamená, že je sdíleno všemi BankAccount objekty. Hodnota nestatické proměnné je jedinečná pro každou instanci objektu BankAccount . Jedná se accountNumberSeed o private static pole, které má předponu s_ podle konvencí pojmenování jazyka C#. s Pole označující static a _ označující private pole. Přidejte následující dva řádky do konstruktoru, který přiřadí číslo účtu. Umístěte je za řádek, který říká this.Balance = initialBalance:

Number = s_accountNumberSeed.ToString();
s_accountNumberSeed++;

Zadáním dotnet run zobrazíte výsledky.

Vytváření vkladů a výběrů

Vaše třída bankovního účtu musí přijmout vklady a výběry, aby fungovaly správně. Pojďme implementovat vklady a výběry vytvořením deníku každé transakce pro účet. Sledování každé transakce má několik výhod oproti pouhé aktualizaci zůstatku u každé transakce. Historii lze použít k auditování všech transakcí a správě denních zůstatků. Výpočet zůstatku z historie všech transakcí v případě potřeby zajistí, že všechny chyby v jedné transakci, které jsou opraveny, se správně projeví v zůstatku při dalším výpočtu.

Začněme vytvořením nového typu, který představuje transakci. Transakce je jednoduchý typ, který nemá žádné zodpovědnosti. Potřebuje několik vlastností. Vytvořte nový soubor s názvem Transaction.cs. Přidejte do ní následující kód:

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;
    }
}

Teď přidáme objekty List<T>Transaction do BankAccount třídy. Za konstruktor v souboru BankAccount.cs přidejte následující deklaraci:

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

Teď pojďme správně vypočítat Balance. Aktuální zůstatek lze najít součtem hodnot všech transakcí. Protože kód je aktuálně, můžete získat pouze počáteční zůstatek účtu, takže budete muset aktualizovat Balance vlastnost. public decimal Balance { get; } Řádek v BankAccount.cs nahraďte následujícím kódem:

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

        return balance;
    }
}

Tento příklad ukazuje důležitý aspekt vlastností. Teď počítáte zůstatek, když o hodnotu požádá jiný programátor. Výpočet vypočítá všechny transakce a poskytne součet jako aktuální zůstatek.

Dále implementujte metody MakeDeposit a MakeWithdrawal metody. Tyto metody vynutí poslední dvě pravidla: počáteční zůstatek musí být kladný a každý výběr nesmí vytvořit záporný zůstatek.

Tato pravidla představují koncept výjimek. Standardní způsob, jak indikovat, že metoda nemůže úspěšně dokončit svou práci, je vyvolání výjimky. Typ výjimky a zpráva přidružená k ní popisují chybu. Metoda vyvolá výjimku, MakeDeposit pokud částka vkladu není větší než 0. Metoda MakeWithdrawal vyvolá výjimku, pokud částka výběru není větší než 0, nebo pokud použití výběru vede k zápornému zůstatku. Za deklaraci _allTransactions seznamu přidejte následující kód:

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);
}

Příkaz throw vyvolá výjimku. Provádění aktuálního bloku končí a řídí přenosy do prvního odpovídajícího catch bloku nalezeného v zásobníku volání. Později přidáte catch blok pro otestování tohoto kódu.

Konstruktor by měl získat jednu změnu, aby se přidala počáteční transakce, a ne aktualizace zůstatku přímo. Vzhledem k tomu, že jste již napsali metodu MakeDeposit , zavolejte ji z konstruktoru. Hotový konstruktor by měl vypadat takto:

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

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

DateTime.Now je vlastnost, která vrací aktuální datum a čas. Otestujte tento kód přidáním několika vkladů a výběrů ve vaší Main metodě za kódem, který vytvoří nový 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);

Dále otestujte, že zachytáváte chybové stavy tím, že se pokusíte vytvořit účet se záporným zůstatkem. Přidejte následující kód za předchozí kód, který jste právě přidali:

// 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;
}

Pomocí try-catch příkazu označíte blok kódu, který může vyvolat výjimky a zachytit tyto chyby, které očekáváte. Stejný postup můžete použít k otestování kódu, který vyvolá výjimku pro záporný zůstatek. Před deklaraci invalidAccount v Main metodě přidejte následující kód:

// 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());
}

Uložte soubor a zadejte dotnet run ho, abyste ho vyzkoušeli.

Výzva – protokolování všech transakcí

Chcete-li dokončit tento kurz, můžete napsat metodu GetAccountHistory , která vytvoří string pro historii transakcí. Přidejte tuto metodu do BankAccount typu:

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();
}

Historie používá StringBuilder třídu k formátování řetězce, který obsahuje jeden řádek pro každou transakci. V předchozích kurzech jste viděli kód pro formátování řetězců. Jeden nový znak je \t. Tím se vloží karta pro formátování výstupu.

Přidejte tento řádek a otestujte ho v Program.cs:

Console.WriteLine(account.GetAccountHistory());

Spuštěním programu zobrazíte výsledky.

Další kroky

Pokud jste se zasekli, uvidíte zdroj tohoto kurzu v našem úložišti GitHub.

Můžete pokračovat v kurzu objektově orientovaného programování .

Další informace o těchto konceptech najdete v těchto článcích: