3
\$\begingroup\$

this is the second version of my BankingSystem in C++ for the first one go here: Banking System console app in C++

I did quite a lot of changes since the last version mainly spliting the User class into Account, Customer and Transaction classes as suggested by @SimonLehman so lets get into the code.
Customer
header file

#pragma once

#include <string>

class Customer
{
private:
    std::string name;
    std::string address;
    size_t id;

public:
    Customer(size_t id, const std::string& name, const std::string& address);

    ~Customer();

    void PrintCustomerInfo();

    const std::string& GetName();

    const std::string& GetAddress();

    const size_t GetId();

};

cpp file

#include "Customer.h"
#include <iostream>


Customer::Customer(size_t id, const std::string& name, const std::string& address) : id(id), name(name), address(address)
{

}

Customer::~Customer()
{

}

void Customer::PrintCustomerInfo()
{
    std::cout << "Costumer info - id : " << id << ", name: " << name << ", address: " << address << '\n';
}

const std::string& Customer::GetAddress()
{
    return address;
}

const std::string& Customer::GetName()
{
    return name;
}

const size_t Customer::GetId()
{
    return id;
}

nothing too complicated just some fields and a function that prints the info of a customer.
Account
header file

#pragma once

#include "Customer.h"
#include <vector>

class Transaction;

using Money = std::int_least16_t;

class Account
{
private:
    Customer customer;
    Money balance;
    size_t accountNumber;
    std::vector<Transaction*> transactionList;



public:

    Account(size_t accountNumber, const Customer& customer, const Money& balance);

    ~Account();

    void PrintAccountInfo();


    const Customer& GetCustomer();

    const Money& GetBalance();

    size_t GetAccountNumber();

    void MoneyMovement(const Money& moneyToMove);

    const std::vector<Transaction*>& GetTransactionList();

    void ProccessTransaction(const Account& accountTo, const Money& transactionAmount);

};

cpp file

#include "Account.h"
#include "Transaction.h"

Account::Account(size_t accountNumber, const Customer& customer, const Money& balance) : accountNumber(accountNumber), customer(customer), balance(balance)
{

}

Account::~Account()
{
    for (Transaction* transaction : transactionList)
    {
        delete transaction;
    }
}

const Customer& Account::GetCustomer()
{
    return customer;
}

const Money& Account::GetBalance()
{
    return balance;
}

size_t Account::GetAccountNumber()
{
    return accountNumber;
}

void Account::MoneyMovement(const Money& moneyToMove)
{
    balance += moneyToMove;
}

const std::vector<Transaction*>& Account::GetTransactionList()
{
    return transactionList;
}

void Account::ProccessTransaction(const Account& accountTo, const Money& transactionAmount)
{
    Transaction *transaction = new Transaction(accountTo, transactionAmount);
    transactionList.emplace_back(transaction);
    MoneyMovement(transactionAmount);
}

    
void Account::PrintAccountInfo()
{
    customer.PrintCustomerInfo();
    std::cout << "Account- account number: " << accountNumber << ", balance: " << balance << '\n';
    std::cout << "Transaction History: \n";

    for (Transaction *transaction : transactionList)
    {
        transaction->PrintTransactionInfo();
    }


}

the Account class holds a list of all transactions, balance the customer its tied to and accountNumber, it also contains the methods for money manipulations from here like MoneyMovement and ProccessTransaction, the ProccessTransactions creates a new transaction pointer, I add the transaction to the transactionList and then change the balance with MoneyMovement.
Transaction
header file

#pragma once

#include <iostream>
#include <vector>
#include "Account.h"

using Money = std::int_least16_t;

class Transaction
{
    Money transactionAmount;
    Account accountTo;

public:
    Transaction(const Account& accountTo, const Money& transactionAmount);

    ~Transaction();

    void PrintTransactionInfo();


};

cpp file

#include "Transaction.h"
#include "Account.h"

Transaction::Transaction(const Account& accountTo, const Money& transactionAmount) : accountTo(accountTo), transactionAmount(transactionAmount)
{
    
}

Transaction::~Transaction()
{

}

void Transaction::PrintTransactionInfo()
{
    Customer customerTo = accountTo.GetCustomer();
    customerTo.PrintCustomerInfo();
    std::cout << "Transaction Ammount: " << transactionAmount << '\n';
}

the Transaction class represent a singular transaction, it holds the account which the money came from all went to (might need to work on the name accountTo) and the amount of money moved between accounts, because a certain Account uses the transaction itself I see no need to provide it with itself.
I also added Print methods for testing purposes.

User
header file

#pragma once

#include <string>
#include <vector>
#include "Account.h"


class User
{
    

private:
    size_t id;
    std::string username;
    std::string password;
    Account account;


public:
    
    User(size_t id, const std::string& username, std::string& password, const Account& account);




    ~User();

    size_t GetId();

    const std::string& GetUsername();

    const std::string& GetPassword();

    const Account& GetAccount();

};

cpp file

#include <iostream>

#include "HashPassword.h"
#include "User.h"





User::User(size_t id, const std::string& username, std::string& password, const Account& account) : id(id), username(username), account(account)
{
    password = HashPassword(password);
    this->password = password;
    std::cout << password << '\n';

}

    
    
User::~User()
{
}

size_t User::GetId()
{
    return id;
}

const std::string& User::GetUsername()
{
    return username;
}

const std::string& User::GetPassword()
{
    return password;
}


const Account& User::GetAccount()
{
    return account;
}

this is the User class which stores the id, the username, the password, and an account that is tied to it.
UserRepository
header file

#pragma once

#include <vector>
#include "User.h"

class UserRepository
{
private:
    std::vector<User> mainBrachRepo;

public:
    UserRepository();

    ~UserRepository();

    std::vector<User>& GetMainBranchRepository();

    void AddUser(const std::string& username, std::string password, const Account& account, std::vector<User>& repository);

    void PrintAllUsers(std::vector<User>& repository);

    User* FindUserByUsername(std::string& username, std::vector<User>& repository);
};

cpp file

#include <iostream>
#include "UserRepository.h"


UserRepository::UserRepository()
{

}
UserRepository::~UserRepository(){}

std::vector<User>& UserRepository::GetMainBranchRepository()
{
    return mainBrachRepo;
}





void UserRepository::AddUser(const std::string& username, std::string password, const Account& account, std::vector<User>& repository)
{
        
    size_t id = repository.size() + 1;
    repository.emplace_back(id, username, password, account);

}


void UserRepository::PrintAllUsers(std::vector<User>& repository)
{
    for(User& user : repository)
    {
        std::cout << user.GetUsername() << ", ";
    }
    std::cout << '\n';
}

User* UserRepository::FindUserByUsername(std::string& username, std::vector<User>& repository)
{
    for (User &user : repository)
    {
        if (user.GetUsername() == username)
        {
            return &user;
        }
    }

    return nullptr;
}

and finnaly the repository that stores users, its only field is the repository itself, I also added room for more repositories being present, I still did not implemented auto id, and account number assigning for my customers and accounts, only the users for now so the name maybe will be changed to something else later as it will store no only just users, at least this is my idea for now.
if you checked my previous post you might notice that there is no authentication system, I haven't gotten around updating it to fit my new model. if you want to run the code download it from my github you can find it on version 1 of the post but the github repo itself gets updated.

\$\endgroup\$
1
  • 2
    \$\begingroup\$ Nice work. One observation not substantial enough to make an answer: int_least16_t has a very small range, so it’s a strange choice to use for money. I doubt it will be sufficient. \$\endgroup\$
    – Davislor
    Commented Sep 24, 2023 at 20:37

2 Answers 2

5
\$\begingroup\$

General Observations

In reference to the previous answer, it isn't clear that the User class is still needed. The Customer class should probably replace it. At a bank there may be multiple types of users that will have access to the customer information, for instance tellers and bank managers will have access to the account as well as the customer.

In addition to find customer by customer name there should be a find customer by account number. A customer may have multiple accounts.

There should be multiple types of accounts, Checking, Savings, Certificate of Deposit, IRA (if you are in the USA).

Account numbers should probably be strings rather than integers.

A customer should have a vector of accounts.

Include Guards

The code uses #pragma once as include guards. The older method of include guards where the file uses #define CONSTANT and #ifdef CONSTANT is somewhat more portable than #pragma once. #pragma once is not part of the C++ Standard.

Line Width

There is no guarantee that a screen or editor will be greater than 80 characters of width; it would be better to keep line widths under 100 characters (at most) so that lines won't wrap and make the code harder to read.

I initially missed the initialization of member variables in the constructors because the lines were too wide.

Customer::Customer(size_t id, const std::string& name, const std::string& address)
    : id(id), name(name), address(address)
{

}

I'm out of time now, I may add more observations later.

\$\endgroup\$
4
\$\begingroup\$

First of all, good improvements!

Some points:

  1. UserRepository class stores the repository (or the vector) inside it; there is no need to accept the vector as a function parameter.
  2. No need to accept Money type by reference or return it by reference; it is a small object.
  3. I believe that in Transaction there should also be the account that performs the transaction.
  4. MoneyMovement should not be a public function. It allows transferring money without a transaction.
  5. Think about ProccessTransaction, and make it process the whole transaction, instead of one half of it. Maybe this functionality should be inside the Transaction constructor itself.
  6. For the pointers management it's better to use smart pointers for it, but I think you covered all relevant cases anyway here.

Once again, good job since the previous time. Keep up with it.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.