Domácí úkol č. 4: Jednoduchý bankovní systém

  • Úkol zadán: 17. 11. 2015
  • Odevzdávání naostro možné od: 20. 11. 2015
  • Absolutní konec odevzdávání: 1. 12. 2015 6.00 (ráno)
  • Odevzdáváte soubory: AccountID.h, AccountData.h, AccountInterface.h, Account.h, Account.cpp, CustomerData.h, CustomerInterface.h, Customer.h, Customer.cpp, Bank.cpp, Bank.h, BankRegister.cpp, BankRegister.h, Database.cpp, Database.h, Interactive.cpp, Interactive.h a main.cpp

Doplnění zadání (zobrazeno podtrženě):

Představení úkolu

Cílem tohoto úkolu je další procvičení dědičnosti v praxi a využití vyjímek a to na modelu zjednodušené bankovní hierarchie. Hlavními třídami našeho modelu budou BankRegister, Bank, BasicAccount a BasicCustomer.

Zadání

  • Třída BankRegister představuje katalog bank. Umožňuje přenosy peněz mezi účty různých bank.
  • Třída CustomerData je třída která slouží pouze jako úložiště dat spojených s klientem banky. Neobsahuje žádnou tzv. „business logic“.
  • Třída CustomerInterface je abstraktní třída. Deklaruje interface pro manipulaci s klienty banky.
  • Třída BasicCustomer je přímým potomkem třídy CustomerInterface. Implementuje interface deklarované ve třídě CustomerInterface.
  • Třída AccountData je třída která slouží pouze jako úložiště dat spojených s bankovním účtem. Neobsahuje žádnou tzv. „business logic“.
  • Třída AccountInterface je abstraktní třída. Deklaruje interface pro manipulaci s bankovními účty.
  • Třída BasicAccount je přímým potomkem třídy AccountInterface. Implementuje interface deklarované ve třídě AccountInterface.
  • Třída Database představuje rozhraní k databázi (což jsou v našem případě standardní kontejnery objektů tříd CustomerData a AccountData).
  • Třída Bank představuje třídu banky. Obsahuje objekt třídy Database.

Zde si stáhněte hlavičkové a zdrojové soubory. Hlavičkové soubory obsahují pouze veřejné členské funkce zmíněné níže s výjimkou souboru BankExceptions.h, který obsahuje potřebné výjimky. Zdrojové soubory až na jednoduchý ukázkový main jsou prázdné.

(Pozn.: Nenechte se zastrašit dlouhým zadáním, snažil jsem se předejít nejasnostem ;))

Požadavky: Třídy

  • class CustomerData
    • Soubory: CustomerData.h
    • Popis:
      • Třída sloužící jakožto úložiště dat o klientovi.
    • Veřejné členské funkce:
      • Přidejte dle vašeho uvážení.
  • class CustomerInterface
    • Soubory: CustomerInterface.h
    • Popis:
      • Abstraktní třída. Deklaruje společné interface pro všechny implementace klienta.
    • Veřejné členské funkce:
      • virtual AccountInterface* createAccount() = 0;
      • virtual std::vector<AccountInterface*> const& accounts() = 0;
      • virtual std::string const& name() const = 0;
      • virtual uint number() const = 0;
  • class BasicCustomer : public CustomerInterface
    • Soubory: Customer.h, Customer.cpp
    • Popis:
      • Základní implementace klienta.
    • Veřejné členské funkce:
      • AccountInterface* createAccount();
        • Vytvoří účet spojený s tímto klientem a vrátí ukazatel na jeho interface.
        • Číslo vytvořeného účtu je 1 + celkový (míněno v celé bance) počet účtů vytvořených před tímto.
      • std::vector<AccountInterface*> const& accounts();
        • Vrátí vektor ukazatelů na interface všech účtů spojených s tímto klientem.
      • std::string const& name() const;
        • Vrátí jméno klienta.
      • uint number() const;
        • Vrátí číslo klienta v bance.
  • class AccountID
    • Soubory: AccountID.h
    • Popis:
      • AccountID slouží jako jednoznačný identifikátor bankovních účtů v systému s jediným bankovním registrem.
    • Veřejné členské funkce:
      • AccountID(uint accountNumber, uint bankNumber);
        • accountNumber – číslo účtu v bance.
        • bankNumber – číslo banky v bankovním registru.
      • uint accountNumber() const;
        • Vrátí číslo účtu v bance.
      • uint bankNumber() const;
        • Vrátí číslo banky v bankovním registru.
  • class AccountData
    • Soubory: AccountData.h
    • Popis:
      • Třída sloužící jakožto úložiště dat o účtu.
    • Veřejné členské funkce:
      • Přidejte dle vašeho uvážení.
  • class AccountInterface
    • Soubory: AccountInterface.h
    • Popis:
      • Abstraktní třída. Deklaruje společné interface pro všechny implementace účtu.
    • Veřejné členské funkce:
      • virtual void deposit(uint amount) = 0;
      • virtual void withdraw(uint amount) = 0;
      • virtual void transfer(uint amount, AccountID target) = 0;
      • virtual uint balance() const = 0;
      • virtual uint number() const = 0;
  • class BasicAccount : public AccountInterface
    • Soubory: Account.h, Account.cpp
    • Popis:
      • Základní implementace účtu.
    • Veřejné členské funkce:
      • void deposit(uint amount);
        • Vloží na účet amount peněz.
        • Množství peněz na účte se zvýší o amount.
      • void withdraw(uint amount);
        • Vybere z účtu amount peněz.
        • Množství peněz na účte se sníží o amount.
        • Výjimky:
          • Pokud balance() není alespoň amount, pak se operace neprovede a hodnotou se vyhodí výjimka InsufficientFunds.
      • void transfer(uint amount, AccountID target);
        • Převede z účtu amount peněz na účet s označením target.
        • Množství peněz na účte se sníží o amount.
        • Množství peněz na účtě s označením target se zvýší o amount.
        • Výjimky:
          • Pokud neexistuje banka s číslem target.bankNumber(), pak se operace neprovede a hodnotou se vyhodí výjimka NonexistentBank.
          • Pokud banka s číslem target.bankNumber() existuje, ovšem nemá účet s číslem target.accountNumber(), pak se operace neprovede a hodnotou se vyhodí výjimka NonexistentAccount.
          • Pokud balance() není alespoň amount, pak se operace neprovede a hodnotou se vyhodí výjimka InsufficientFunds.
      • uint balance() const;
        • Vrátí zůstatek peněz na účtu.
      • uint number() const;
        • Vrátí číslo účtu (v bance).
  • class Database
    • Soubory: Database.h, Database.cpp
    • Popis:
      • Slouží jako úložiště dat o účtech a klientech.
    • Veřejné členské funkce:
      • CustomerData createCustomer(std::string const& name);
        • Vytvoří nového klienta se jménem name a vrátí jeho data.
        • První vytvořený klient má číslo 1. Další nově vytvoření klienti mají číslo o jedna vyšší než klient vytvořený před nimi.
      • AccountData createAccount(uint ownerNumber);
        • Vytvoří nový účet klientovi s číslem ownerNumber a vrátí data nově vytvořeného účtu.
        • První vytvořený účet má číslo 1. Další nově vytvořené účty mají číslo o jedna vyšší než účet vytvořený před nimi.
      • CustomerData loadCustomer(uint number) const;
        • Vrátí data klienta s číslem number.
        • Výjimky:
          • Pokud klient s daným číslem v bance neexistuje, pak se operace neprovede a hodnotou se vyhodí výjimka NonexistentCustomer.
      • AccountData loadAccount(uint number) const;
        • Vrátí data účtu s číslem number.
        • Výjimky:
          • Pokud účet s daným číslem v bance neexistuje, pak se operace neprovede a hodnotou se vyhodí výjimka NonexistentAccount.
      • void save(CustomerData);
        • Uloží existujícího klienta.
      • void save(AccountData);
        • Uloží existující účet.
      • std::map<uint, CustomerData> const& customers() const;
        • Vrátí mapu dat všech klientů v této databázi.
        • Klíčem mapy je číslo klienta v databázi.
      • std::map<uint, AccountData> const& accounts() const;
        • Vrátí mapu dat všech účtů v této databázi.
        • Klíčem mapy je číslo účtu v databázi.
  • class BankRegister
    • Soubory: BankRegister.h, BankRegister.cpp
    • Popis:
      • Slouží jako registr bank. Umožňuje peněžní transakce mezi vícerými bankami.
    • Veřejné členské funkce:
      • BankRegister();
        • Vytvoří prázdný bankovní registr.
      • uint registerBank(Bank* bank);
        • První registrovaná banka má číslo 1. Další registrované banky mají číslo o jedna vyšší než banka registrovaná před nimi.
      • void unregisterBank(uint bankNumber);
        • Odstraní banku s daným číslem z registru. Nemá vliv na číslování bank.
        • Výjimky:
          • Pokud banka s číslem bankNumber neexistuje, pak se operace neprovede a hodnotou se vyhodí výjimka NonexistentBank.
      • Bank* bankByNumber(uint bankNumber) const;
        • Vrátí ukazatel na banku s číslem bankNumber.
        • Výjimky:
          • Pokud banka s číslem bankNumber neexistuje, pak se operace neprovede a hodnotou se vyhodí výjimka NonexistentBank.
  • class Bank
    • Soubory: Bank.h, Bank.cpp
    • Popis:
      • Třída sloužící pro manipulaci s bankou.
      • Třída by měla obsahovat objekt třídy Database jakožto členskou proměnnou.
    • Veřejné členské funkce:
      • Bank(std::string const& name, BankRegister* centralRegister);
        • Konstruktor.
        • Nastaví jméno banky na name.
        • Banka bude součástí registru na který ukazuje centralRegister.
      • CustomerInterface* createCustomer(std::string const& name);
        • Vytvoří v bance klienta s daným jménem a vrátí ukazatel na jeho interface.
        • První vytvořený klient má číslo 1. Další nově vytvoření klienti mají číslo o jedna vyšší než klient vytvořený před nimi.
      • CustomerInterface* customerByNumber(uint customerNumber);
        • Vrátí ukazatel na interface klienta s daným číslem.
        • Výjimky:
          • Pokud klient s daným číslem v bance neexistuje, pak se operace neprovede a hodnotou se vyhodí výjimka NonexistentCustomer.
      • AccountInterface* accountByNumber(uint accountNumber);
        • Vrátí ukazatel na interface účtu s daným číslem.
        • Výjimky:
          • Pokud účet s daným číslem v bance neexistuje, pak se operace neprovede a hodnotou se vyhodí výjimka NonexistentAccount.
      • std::map<uint, CustomerInterface*> const& customers();
        • Vrátí mapu ukazatelů na interface všech klientů banky.
        • Klíčem mapy je číslo klienta v bance.
      • std::map<uint, AccountInterface*> const& accounts();
        • Vrátí mapu ukazatelů na interface všech účtů v bance.
        • Klíčem mapy je číslo účtu v bance.
      • void transfer(uint amount, uint source, AccountID target);
        • Převede z účtu (z této banky) s číslem source amount peněz na účet s označením target.
        • Množství peněz na účte s číslem source se sníží o amount.
        • Množství peněz na účtě s označením target se zvýší o amount.
        • Výjimky:
          • Pokud v této bance neexistuje účet s číslem source, pak se operace neprovede a hodnotou se vyhodí výjimka NonexistentAccount.
          • Pokud neexistuje banka s číslem target.bankNumber(), pak se operace neprovede a hodnotou se vyhodí výjimka NonexistentBank.
          • Pokud banka s číslem target.bankNumber() existuje, ovšem nemá účet s číslem target.accountNumber(), pak se operace neprovede a hodnotou se vyhodí výjimka NonexistentAccount.
          • Pokud balance() účtu s číslem source není alespoň amount, pak se operace neprovede a hodnotou se vyhodí výjimka InsufficientFunds.
      • std::string name() const;
        • Vrátí název banky.
      • uint number() const;
        • Vrátí číslo banky v bankovním registru.
      • createAccount
        • Podoba této funkce, případně to jestli ji vůbec implementujete, je na vás.
  • Veškeré zde zmíněné výjimky jsou již nadefinované v souboru BankExceptions.h, který je součástí souborů ke stažení.
  • Pokud nastane případ, kdy lze dle specifikace vyhodit více výjimek, vyhoďte z nich tu, která se na seznamu výjimek příslušné funkce vyskytuje nejdříve.
  • Třídám smíte přidat konstruktory a destruktory.
  • Třídám smíte přidat libovolné množství privátních členských funkcí a proměnných.
  • Třídám nesmíte přidávat neprivátní členské funkce či proměnné, pokud není řečeno jinak.

Požadavky: Interaktivní prostředí

Další částí úkolu je vytvoření interaktivního bankovního interface pro více bank s jediným bankovním registrem.

Interakce bude probíhat přes standardní vstup a výstup. Můžete počítat s tím, že vstupy budou ve správném formátu (tj. např. tam kde má být na vstupu číslo bude číslo), ovšem ne vždy musí být hodnoty validní (tj. např. mohu chtít vložit peníze na neexistující účet, což by správně mělo vyhodit výjimku). Pokud tedy během práce s interaktivním prostředím dojde vyhození výjimky, je třeba tuto výjimku zachytit (nejlépe referencí) a vypsat její what(). Po vypsání pokračuje program dál.

V implementaci máte volnost, vyžadováno je pouze dodržení přesného formátu a funkčnosti (popsáno dále). Kód pro interaktivní prostředí vypracujte do souborů Interactive.h a Interactive.cpp. Samotný main umístěte do souboru main.cpp.

Příkazy
  • Vytvoření banky
    • create_bank <bank_name>
      • Příkaz vytvoří banku s názvem <bank_name> (může obsahovat mezery).
      • Příkaz vypíše následující:
        Created bank: <bank_name> | <bank_number>
      • Příklad:
        create_bank Generic Bank
        Created bank: Generic Bank | 1
  • Vytvoření klienta
    • create_customer <bank_number> <customer_name>
      • Příkaz vytvoří zákazníka banky s číslem <bank_number>. Zákazníkovo jméno bude <customer_name> (může obsahovat mezery).
      • Příkaz vypíše následující:
        Created customer: <customer_name> | <customer_number>
      • Příklad:
        create_customer 1 Jon Doe
        Created customer: Jon Doe | 1
      • Možné výjimky v případech:
        • Kdy neexistuje banka s příslušným číslem.
  • Vytvoření účtu
    • create_account <bank_number> <customer_number>
      • Příkaz vytvoří účet v bance s číslem <bank_number>. Účet bude patřit zákazníkovi s číslem <customer_number>
      • Příkaz vypíše následující:
        Created account: <account_number>/<bank_number>
      • Příklad:
        create_account 1 1
        Created account: 1/1
      • Možné výjimky v případech:
        • Kdy neexistuje banka s příslušným číslem.
        • Kdy banka s příslušným číslem existuje, ale neexistuje v ní uživatel s příslušným číslem.
  • Vložení peněz na účet
    • deposit <amount> <account_number>/<bank_number>
      • Příkaz vloží na účet s AccountID(<account_number>, <bank_number>) <amount> peněz.
      • Příklad:
        deposit 100 123456/2
      • Možné výjimky v případech:
        • Kdy neexistuje banka s příslušným číslem.
        • Kdy existuje banka s příslušným číslem, ale neexistuje účet s příslušným číslem.
  • Vybrání peněz z účtu
    • withdraw <amount> <account_number>/<bank_number>
      • Příkaz vybere z účtu s AccountID(<account_number>, <bank_number>) <amount> peněz.
      • Příklad:
        withdraw 100 123456/2
      • Možné výjimky v případech:
        • Kdy neexistuje banka s příslušným číslem.
        • Kdy existuje banka s příslušným číslem, ale neexistuje účet s příslušným číslem.
        • Kdy množství peněz na účtu není větší nebo rovno amount.
  • Převedení peněz mezi účty
    • transfer <amount> <account_number1>/<bank_number1> <account_number2>/<bank_number2>
      • Příkaz převede z účtu s AccountID(<account_number1>, <bank_number1>) <amount> peněz na účet s AccountID(<account_number2>, <bank_number2>)
      • Příklad:
        transfer 100 123456/2 654321/1
      • Možné výjimky v případech:
        • Kdy neexistuje 1. banka s příslušným číslem.
        • Kdy 1. banka s příslušným číslem existuje, ale neexistuje v ní účet s příslušným číslem.
        • Kdy neexistuje 2. banka s příslušným číslem.
        • Kdy 2. banka s příslušným číslem existuje, ale neexistuje v ní účet s příslušným číslem.
        • Pokud množství peněz na prvním účtu není alespoň amount.
  • Vypsání informací o bance
    • bank_info <bank_number>
      • Příkaz vypíše informace o bance s číslem <bank_number>
      • Příkaz vypíše následující:
        Name: <bank_name>
        Customers: <number_of_customers>
        Accounts: <number_of_accounts>
          
      • Příklad:
        bank_info 1
        Name: Reliable Bank
        Customers: 4
        Accounts: 7
      • Možné výjimky v případech:
        • Kdy neexistuje banka s příslušným číslem.
  • Vypsání informací o klientovi
    • customer_info <bank_number> <customer_number>
      • Příkaz vypíše informace o zákazníkovi banky s číslem <bank_number> kteréhož číslo je <customer_number>
      • Příkaz vypíše následující:
        Name: <customer_name>
        Accounts: <account_numbers>
      • Příklad:
        customer_info 1 1
        Name: Jon Doe
        Accounts: 2, 5, 7
        • (Pozn.: <account_numbers> znamená vypsání čísel účtů ve vzestupném pořadí, sousední čísla jsou oddělena řetězcem „, “)
      • Možné výjimky v případech:
        • Kdy neexistuje banka s příslušným číslem.
        • Kdy banka s příslušným číslem existuje, ale neexistuje v ní zákazník s příslušným číslem.
  • Vypsání informací o účtu
    • account_info <account_number>/<bank_number>
      • Příkaz vypíše informace o účtu banky s číslem <bank_number> kteréhož číslo je <account_number>
      • Příkaz vypíše následující:
        Balance: <account_balance>
      • Příklad:
        account_info 2/1
        Balance: 123
      • Možné výjimky v případech:
        • Kdy neexistuje banka s příslušným číslem.
        • Kdy banka s příslušným číslem existuje, ale neexistuje v ní účet s příslušným číslem.
  • Ukončení sezení
    • quit
      • Příkaz ukončí probíhající interaktivní sezení a tím i celý běh programu.
  • Vypisování výjimek
    • Formát:
      • std::cout << "Exception: " << vyjimka.what() << std::endl;
    • Přednosti:
      • Některé případy mohou být kandidáty k vypsání více výjimek (splňují více výjimečných případů). Vypište vždy pouze tu, která přísluší prvnímu takovému případu na seznamu. (Toto pořadí by mělo přirozeně vyplynout z Vaší implementace hierarchie).

Soubory k odevzdání:

  • AccountID.h
  • AccountData.h
  • AccountInterface.h
  • Account.h
  • Account.cpp
  • CustomerData.h
  • CustomerInterface.h
  • Customer.h
  • Customer.cpp
  • Bank.cpp
  • Bank.h
  • BankRegister.cpp
  • BankRegister.h
  • Database.cpp
  • Database.h
  • Interactive.cpp
  • Interactive.h
  • main.cpp

Poznámky

  • Doporučuji vytvořit si vlastní unit testy. Samotný výstup Vašeho interaktivního prostředí si můžete porovnat s výstupem vzorového vypracování, které je umístěno na aise /home/xmiklos/pb161/hw04
  • Možná jste si všimli hojného využívání konstantních referencí jakožto argumentu funkce. Důvodem jejich použití je to, že často mohou ušetřit zbytečné kopírování u potencionálně velkých objektů. Nemá smysl to ovšem přehánět, například brát konstantní referenci na int, char a další vestavěné datové typy může naopak výkonnostně uškodit. Také například nedává smysl používat konstantní reference na argumenty v případech, kdy argument musíme v těle funkce beztak zkopírovat. Z podobných důvodů z některých funkcí vracíme konstantní reference na členské proměnné.
  • Vzpomeňte si na Rule of Three a zakažte kopírování a přiřazování u tříd kde tyto funkce nedávají smysl.

Autor: Úlohu připravil Petr Pilař, v případě nejasností kontaktujte Michala Mikloše na xmiklos@fi.muni.cz nebo diskuzním fóru.

QR Code
QR Code public:pb161_fall15_hw04 (generated for current page)