Megosztás a következőn keresztül:


Objektumorientált programozás (C#)

A C# egy objektumorientált programozási nyelv. Az objektumorientált programozás négy alapelve:

  • Absztrakció Az entitások releváns attribútumainak és interakcióinak modellezése osztályokként a rendszer absztrakciós ábrázolásának meghatározásához.
  • Beágyazás Elrejti az objektumok belső állapotát és funkcióit, és csak nyilvános függvénykészleten keresztül engedélyezi a hozzáférést.
  • Öröklési képesség új absztrakciók létrehozására a meglévő absztrakciók alapján.
  • Polimorfizmus Az öröklött tulajdonságok vagy metódusok implementálása különböző módokon több absztrakcióban.

Az előző oktatóanyagban az osztályok bemutatása során absztrakciót és beágyazást is láthatott. Az BankAccount osztály absztrakciót biztosított a bankszámla fogalmához. A megvalósítást anélkül módosíthatja, hogy az az osztályt használó BankAccount kód bármelyikét érintené. Mind az osztályok, mind az BankAccountTransaction osztályok biztosítják a kódban szereplő fogalmak leírásához szükséges összetevők beágyazását.

Ebben az oktatóanyagban kiterjeszti az alkalmazást, hogy az öröklés és a polimorfizmus használatával új funkciókat adjon hozzá. Emellett funkciókkal is bővítheti az BankAccount osztályt, kihasználva az előző oktatóanyagban tanult absztrakciós és beágyazási technikákat.

Különböző típusú fiókok létrehozása

A program létrehozása után a program funkcióinak hozzáadására vonatkozó kérelmeket kap. Nagyszerűen működik abban a helyzetben, amikor csak egy bankszámlatípus van. Idővel szükség van a változásra, és a kapcsolódó fióktípusokat a rendszer kéri:

  • Kamatkereseti számla, amely minden hónap végén kamatot halmoz fel.
  • Egy hitelkeret, amely negatív egyenleggel rendelkezhet, de ha van egyenleg, minden hónapban kamatot számítunk fel.
  • Előre fizetett ajándékkártya számla, amely egyetlen befizetéssel kezdődik, és csak kifizethető. Minden hónap elején egyszer tölthető újra.

A különböző fiókok mindegyike hasonló a korábbi oktatóanyagban meghatározott osztályhoz BankAccount . Másolhatja a kódot, átnevezheti az osztályokat, és módosításokat végezhet. Ez a technika rövid távon működne, de idővel több munka lenne. A módosítások az összes érintett osztályra át lesznek másolva.

Ehelyett létrehozhat új bankszámlatípusokat, amelyek az előző oktatóanyagban létrehozott osztályból öröklik a BankAccount metódusokat és az adatokat. Ezek az új osztályok kiterjeszthetik az BankAccount osztályt az egyes típusokhoz szükséges viselkedéssel:

public class InterestEarningAccount : BankAccount
{
}

public class LineOfCreditAccount : BankAccount
{
}

public class GiftCardAccount : BankAccount
{
}

Mindegyik osztály örökli a megosztott viselkedést a megosztott alaposztálytól, az BankAccount osztálytól. Írja meg az implementációkat az új és a különböző funkciókhoz az egyes származtatott osztályokban. Ezek a származtatott osztályok már rendelkeznek az osztály összes viselkedésével BankAccount .

Ajánlott minden új osztályt egy másik forrásfájlban létrehozni. A Visual Studióban kattintson a jobb gombbal a projektre, és válassza az Osztály hozzáadása lehetőséget, ha új osztályt szeretne hozzáadni egy új fájlhoz. A Visual Studio Code-ban válassza a Fájl, majd az Új lehetőséget egy új forrásfájl létrehozásához. Mindkét eszközben nevezze el a fájlt, hogy megfeleljen az osztálynak: InterestEarningAccount.cs, LineOfCreditAccount.cs és GiftCardAccount.cs.

Amikor az előző mintában látható módon hozza létre az osztályokat, azt fogja tapasztalni, hogy egyik származtatott osztály sem fordítható le. A konstruktor feladata egy objektum inicializálása. A származtatott osztálykonstruktornak inicializálnia kell a származtatott osztályt, és útmutatást kell adnia a származtatott osztályban szereplő alaposztály-objektum inicializálásához. A megfelelő inicializálás általában további kód nélkül történik. Az BankAccount osztály egy nyilvános konstruktort deklarál a következő aláírással:

public BankAccount(string name, decimal initialBalance)

A fordító nem hoz létre alapértelmezett konstruktort, amikor ön definiál egy konstruktort. Ez azt jelenti, hogy minden származtatott osztálynak explicit módon kell meghívnia ezt a konstruktort. Deklarálhat egy konstruktort, amely argumentumokat adhat át az alaposztály-konstruktornak. Az alábbi kód a következő konstruktort mutatja:InterestEarningAccount

public InterestEarningAccount(string name, decimal initialBalance) : base(name, initialBalance)
{
}

Az új konstruktor paraméterei megegyeznek az alaposztály-konstruktor paramétertípusával és nevével. A szintaxissal : base() egy alaposztály-konstruktor hívását jelezheti. Egyes osztályok több konstruktort határoznak meg, és ez a szintaxis lehetővé teszi, hogy kiválaszthatja, hogy melyik alaposztály-konstruktort hívja meg. Miután frissítette a konstruktorokat, fejlesztheti az egyes származtatott osztályok kódját. Az új osztályok követelményei az alábbiak szerint adhatók meg:

  • Kamatkereseti fiók:
    • A hónap végi egyenleg 2%-ának jóváírása lesz.
  • Hitelkeret:
    • Negatív egyenlege lehet, de abszolút értékben nem lehet nagyobb, mint a hitelkeret.
    • Minden hónapban kamatot számít fel, ha a hónap végi egyenleg nem 0.
    • A jóváírási korlátot túllépő minden egyes kifizetésért díjat kell fizetnie.
  • Ajándékkártya-fiók:
    • Havonta egyszer, a hónap utolsó napján egy adott összeggel tölthető újra.

Láthatja, hogy mindhárom fióktípus rendelkezik olyan művelettel, amely minden hónap végén történik. Az egyes fióktípusok azonban különböző feladatokat végeznek. A kód implementálásához polimorfizmust használ. Egyetlen metódus létrehozása virtual az BankAccount osztályban:

public virtual void PerformMonthEndTransactions() { }

Az előző kód bemutatja, hogyan deklarálhat egy metódust a virtual kulcsszóval az alaposztályban, amelynek egy származtatott osztály más implementációt biztosíthat. A virtual metódus olyan módszer, amelyben bármely származtatott osztály dönthet úgy, hogy újrakonfigurálja azokat. A származtatott osztályok a override kulcsszó használatával határozzák meg az új implementációt. Ezt általában "az alaposztály implementálásának felülírása" kifejezésre hivatkozik. A virtual kulcsszó azt határozza meg, hogy a származtatott osztályok felülírhatják a viselkedést. Deklarálhat abstract olyan metódusokat is, amelyekben a származtatott osztályoknak felül kell bírálnia a viselkedést. Az alaposztály nem biztosít implementációt egy abstract metódushoz. Ezután meg kell határoznia a létrehozott két új osztály implementációját. Kezdje a InterestEarningAccountkövetkezővel:

public override void PerformMonthEndTransactions()
{
    if (Balance > 500m)
    {
        decimal interest = Balance * 0.02m;
        MakeDeposit(interest, DateTime.Now, "apply monthly interest");
    }
}

Adja hozzá a következő kódot a LineOfCreditAccountkövetkezőhöz. A kód tagadja az egyenleget, hogy kiszámítsa a számláról kivont pozitív kamatot:

public override void PerformMonthEndTransactions()
{
    if (Balance < 0)
    {
        // Negate the balance to get a positive interest charge:
        decimal interest = -Balance * 0.07m;
        MakeWithdrawal(interest, DateTime.Now, "Charge monthly interest");
    }
}

Az GiftCardAccount osztálynak két módosításra van szüksége a hónap végi funkcióinak implementálásához. Először módosítsa a konstruktort úgy, hogy az tartalmazza a havonta hozzáadandó opcionális összeget:

private readonly decimal _monthlyDeposit = 0m;

public GiftCardAccount(string name, decimal initialBalance, decimal monthlyDeposit = 0) : base(name, initialBalance)
    => _monthlyDeposit = monthlyDeposit;

A konstruktor alapértelmezett értéket biztosít az értékhez, így a monthlyDeposit hívók kihagyhatnak egy 0 havi befizetést. Ezután felülbírálja a PerformMonthEndTransactions havi betét hozzáadásának metódusát, ha a konstruktor nem nulla értékre van beállítva:

public override void PerformMonthEndTransactions()
{
    if (_monthlyDeposit != 0)
    {
        MakeDeposit(_monthlyDeposit, DateTime.Now, "Add monthly deposit");
    }
}

A felülbírálás a konstruktorban beállított havi betétet alkalmazza. Adja hozzá a következő kódot a metódushoz a Main módosítások teszteléséhez a következőhöz és a GiftCardAccountInterestEarningAccountkövetkezőhöz:

var giftCard = new GiftCardAccount("gift card", 100, 50);
giftCard.MakeWithdrawal(20, DateTime.Now, "get expensive coffee");
giftCard.MakeWithdrawal(50, DateTime.Now, "buy groceries");
giftCard.PerformMonthEndTransactions();
// can make additional deposits:
giftCard.MakeDeposit(27.50m, DateTime.Now, "add some additional spending money");
Console.WriteLine(giftCard.GetAccountHistory());

var savings = new InterestEarningAccount("savings account", 10000);
savings.MakeDeposit(750, DateTime.Now, "save some money");
savings.MakeDeposit(1250, DateTime.Now, "Add more savings");
savings.MakeWithdrawal(250, DateTime.Now, "Needed to pay monthly bills");
savings.PerformMonthEndTransactions();
Console.WriteLine(savings.GetAccountHistory());

Ellenőrizze az eredményeket. Most vegyen fel egy hasonló tesztkódot a LineOfCreditAccountkövetkezőhöz:

var lineOfCredit = new LineOfCreditAccount("line of credit", 0);
// How much is too much to borrow?
lineOfCredit.MakeWithdrawal(1000m, DateTime.Now, "Take out monthly advance");
lineOfCredit.MakeDeposit(50m, DateTime.Now, "Pay back small amount");
lineOfCredit.MakeWithdrawal(5000m, DateTime.Now, "Emergency funds for repairs");
lineOfCredit.MakeDeposit(150m, DateTime.Now, "Partial restoration on repairs");
lineOfCredit.PerformMonthEndTransactions();
Console.WriteLine(lineOfCredit.GetAccountHistory());

Amikor hozzáadja az előző kódot, és futtatja a programot, az alábbihoz hasonló hibaüzenet jelenik meg:

Unhandled exception. System.ArgumentOutOfRangeException: Amount of deposit must be positive (Parameter 'amount')
   at OOProgramming.BankAccount.MakeDeposit(Decimal amount, DateTime date, String note) in BankAccount.cs:line 42
   at OOProgramming.BankAccount..ctor(String name, Decimal initialBalance) in BankAccount.cs:line 31
   at OOProgramming.LineOfCreditAccount..ctor(String name, Decimal initialBalance) in LineOfCreditAccount.cs:line 9
   at OOProgramming.Program.Main(String[] args) in Program.cs:line 29

Feljegyzés

A tényleges kimenet tartalmazza a projekttel rendelkező mappa teljes elérési útját. A mappanevek nem lettek megadva a rövidség kedvéért. Emellett a kódformátumtól függően a sorszámok kissé eltérőek lehetnek.

Ez a kód meghiúsul, mert feltételezi BankAccount , hogy a kezdeti egyenlegnek 0-nál nagyobbnak kell lennie. Egy másik feltételezést sütött az BankAccount osztályban, hogy az egyensúly nem megy negatív. Ehelyett minden olyan visszavonást, amely felülírja a fiókot, elutasítja. Mindkét feltételezésnek változnia kell. A hitelkeret 0-kor kezdődik, és általában negatív egyenlege lesz. Továbbá, ha egy ügyfél túl sok pénzt vesz fel, díjat kell fizetnie. A tranzakció elfogadva, csak többe kerül. Az első szabály implementálható úgy, hogy hozzáad egy opcionális argumentumot a BankAccount konstruktorhoz, amely meghatározza a minimális egyenleget. Az alapértelmezett érték 0. A második szabályhoz olyan mechanizmus szükséges, amely lehetővé teszi a származtatott osztályok számára az alapértelmezett algoritmus módosítását. Bizonyos értelemben az alaposztály "megkérdezi" a származtatott típust, hogy mi történjen overdraft esetén. Az alapértelmezett viselkedés a tranzakció elvetése kivétellel.

Először adjunk hozzá egy második konstruktort, amely tartalmaz egy opcionális minimumBalance paramétert. Ez az új konstruktor elvégzi a meglévő konstruktor összes műveletét. Emellett beállítja a minimális egyenleg tulajdonságot is. Másolhatja a meglévő konstruktor törzsét, de ez azt jelenti, hogy a jövőben két hely változik. Ehelyett konstruktorláncolással egy konstruktor meghívhat egy másikat. Az alábbi kód a két konstruktort és az új további mezőt mutatja be:

private readonly decimal _minimumBalance;

public BankAccount(string name, decimal initialBalance) : this(name, initialBalance, 0) { }

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

    Owner = name;
    _minimumBalance = minimumBalance;
    if (initialBalance > 0)
        MakeDeposit(initialBalance, DateTime.Now, "Initial balance");
}

Az előző kód két új technikát mutat be. Először is a minimumBalance mező a következőként readonlyvan megjelölve: . Ez azt jelenti, hogy az érték nem módosítható az objektum létrehozása után. BankAccount A létrehozás után a minimumBalance nem módosítható. Másodszor, a két paramétert használó konstruktor implementálásként használja : this(name, initialBalance, 0) { } . A : this() kifejezés meghívja a másik konstruktort, az egyiket három paraméterrel. Ez a technika lehetővé teszi, hogy egyetlen implementációval inicializáljon egy objektumot, annak ellenére, hogy az ügyfélkód számos konstruktor közül választhat.

Ez a megvalósítás csak akkor hív meg MakeDeposit , ha a kezdeti egyenleg nagyobb, mint 0. Ez megőrzi azt a szabályt, hogy a betéteknek pozitívnak kell lenniük, de lehetővé teszi, hogy a hitelszámla egyenleggel 0 legyen megnyitva.

Most, hogy az BankAccount osztály rendelkezik egy írásvédett mezővel a minimális egyenleghez, a végső változás az, hogy a kemény kódot 0 a MakeWithdrawal metódusban a következőre minimumBalance módosítja:

if (Balance - amount < _minimumBalance)

Az BankAccount osztály kiterjesztése után módosíthatja a konstruktort LineOfCreditAccount , hogy meghívja az új alapkonstruktort az alábbi kódban látható módon:

public LineOfCreditAccount(string name, decimal initialBalance, decimal creditLimit) : base(name, initialBalance, -creditLimit)
{
}

Figyelje meg, hogy a LineOfCreditAccount konstruktor módosítja a creditLimit paraméter jelét, hogy az megfeleljen a minimumBalance paraméter jelentésének.

Különböző overdraft szabályok

Az utolsó hozzáadandó funkció lehetővé teszi, LineOfCreditAccount hogy a tranzakció elutasítása helyett díjat számítsunk fel a hitelkeret túllépése után.

Az egyik módszer egy olyan virtuális függvény definiálása, amelyben megvalósítja a szükséges viselkedést. Az BankAccount osztály két metódusba alakítja át a MakeWithdrawal metódust. Az új módszer akkor hajtja végre a megadott műveletet, ha a visszavonás a minimum alatti egyenleget veszi fel. A meglévő MakeWithdrawal metódus a következő kóddal rendelkezik:

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 < _minimumBalance)
    {
        throw new InvalidOperationException("Not sufficient funds for this withdrawal");
    }
    var withdrawal = new Transaction(-amount, date, note);
    _allTransactions.Add(withdrawal);
}

Cserélje le a következő kódra:

public void MakeWithdrawal(decimal amount, DateTime date, string note)
{
    if (amount <= 0)
    {
        throw new ArgumentOutOfRangeException(nameof(amount), "Amount of withdrawal must be positive");
    }
    Transaction? overdraftTransaction = CheckWithdrawalLimit(Balance - amount < _minimumBalance);
    Transaction? withdrawal = new(-amount, date, note);
    _allTransactions.Add(withdrawal);
    if (overdraftTransaction != null)
        _allTransactions.Add(overdraftTransaction);
}

protected virtual Transaction? CheckWithdrawalLimit(bool isOverdrawn)
{
    if (isOverdrawn)
    {
        throw new InvalidOperationException("Not sufficient funds for this withdrawal");
    }
    else
    {
        return default;
    }
}

A hozzáadott metódus az protected, ami azt jelenti, hogy csak származtatott osztályokból hívható meg. Ez a deklaráció megakadályozza, hogy más ügyfelek meghívják a metódust. Emellett a virtual származtatott osztályok is módosíthatják a viselkedést. A visszatérési típus egy Transaction?. A ? széljegyzet azt jelzi, hogy a metódus visszatérhet null. Adja hozzá a következő megvalósítást, LineOfCreditAccount hogy díjat számítsunk fel a visszavonási korlát túllépésekor:

protected override Transaction? CheckWithdrawalLimit(bool isOverdrawn) =>
    isOverdrawn
    ? new Transaction(-20, DateTime.Now, "Apply overdraft fee")
    : default;

A felülbírálás egy díjtranzakciót ad vissza, amikor a fiók túl van állítva. Ha a visszavonás nem lépi túl a korlátot, a metódus egy tranzakciót null ad vissza. Ez azt jelzi, hogy nincs díj. A módosítások teszteléséhez adja hozzá a következő kódot a Main metódushoz az Program osztályban:

var lineOfCredit = new LineOfCreditAccount("line of credit", 0, 2000);
// How much is too much to borrow?
lineOfCredit.MakeWithdrawal(1000m, DateTime.Now, "Take out monthly advance");
lineOfCredit.MakeDeposit(50m, DateTime.Now, "Pay back small amount");
lineOfCredit.MakeWithdrawal(5000m, DateTime.Now, "Emergency funds for repairs");
lineOfCredit.MakeDeposit(150m, DateTime.Now, "Partial restoration on repairs");
lineOfCredit.PerformMonthEndTransactions();
Console.WriteLine(lineOfCredit.GetAccountHistory());

Futtassa a programot, és ellenőrizze az eredményeket.

Összegzés

Ha elakadt, az oktatóanyag forrását a GitHub-adattárban tekintheti meg.

Ez az oktatóanyag számos, az objektumorientált programozásban használt technikát mutatott be:

  • Az Absztrakciót használta, amikor osztályokat definiált az egyes fióktípusokhoz. Ezek az osztályok leírták az ilyen típusú fiók viselkedését.
  • Az Encapsulation parancsot használta, amikor az egyes osztályokban sok részletet private tartott meg.
  • Az öröklést akkor használta, amikor az osztályban már létrehozott implementációt használta a BankAccount kód mentéséhez.
  • Polimorfizmust használt, amikor olyan metódusokat hozott létrevirtual, amelyek származtatott osztályai felülbírálhatók az adott fióktípus adott viselkedésének létrehozásához.