ATM Project for School

Started by
2 comments, last by NightCreature83 12 years, 1 month ago
Hey everybody. I'm in my intro to programming class in College (Computer Science Major). We're learning C++ at the moment. So, our first big project of the semester is to make an ATM (Automated Teller Machine) Simulation Program, using the console for input and output. We haven't gotten to Classes or anything like that yet, but I wanted to try and implement them anyway (I've got a bit of past experience in Java, not a lot, but enough to know the basics of OOP). My teacher said it was fine to use anything in C++ if we knew about content that we haven't learned about yet. Point. I wrote the program today, and I was hoping that if I posted my thought process (as concise as possible), and how I implemented everything, I could get a few critiques and suggestions on my coding style. Why waste a learning opportunity, right?

I approached the program similar to how I've learned to think about a game. I made a big, state driven loop. I used enums to control the states, and just used a boolean value (exit) to control the loop.

First, the user selects the language after they insert their card (card is 'inserted' automatically, there is a cout statement that tells the user they inserted it.) So, we have to have the rest of the program's output in 2 languages. I chose English and Spanish. I implemented this by setting any output that is given to the console as variables. I set constants, for example,

const string ENGLISH_PIN_MENU_TEXT = "Please enter your pin Number.";
const string SPANISH_PIN_MENU_TEXT = "BLAH BLAH BLAH EL BLAH";
string pinMenuText;
while (!exit)
switch (currentState)
{
case SELECT_LANGUAGE:
if (input == 1) //english
pinMenuText = ENGLISH_PIN_MENU_TEXT;
else if (input == 2) //spanish
pinMenuText = SPANISH_PIN_MENU_TEXT;
break;
case PIN_MENU:
int input;
cout << pinMenuText;
cin >> input;
etc...


Is this a good way to implement language localization? I was trying to avoid writing to the hard drive cause I'm not extremely comfortable with file parsing and all that yet.

So yeah. The language implementation is one of the big things I would appreciate criticism on.

The other big question I have is about my menu implementation. I created a class ATMMenu that I used to make formatting the output text for each state in the program a little bit easier.

class ATMMenu {
public:
void SetText(std::string txtdisplayed);
void AddOption(int option, std::string optiontext);
void SetMaxTries(int maxtries);

int GetMaxTries();
int GetInput();

void Display();

private:
std::string _MenuText;
static const int MAX_OPTIONS = 5;
std::string _Options[MAX_OPTIONS];
int _MaxTries = 50;
std::string _Error;
};


Did I implement all of this alright? The general idea is that I use SetText to set the Menu's Text, AddOption to add options for the user to select, and then Display() outputs both of them together in a formatted cout state. GetInput then uses cin statements so that in my main() method I can put GetInput into a switch statement and then handle the user's input from there. Max Tries are there so that in the main() method I can use a for loop to let the user go through a menu more than once if incorrect input is entered (if the input is correct, the for loop breaks, if not, it loops again and gets more user input). How is this looking?

Those are my big 3 questions about everything I coded. The Language Implementation, The State Driven Design, and the ATMMenu class. What could I have done differently? How could I have added more classes and simplified the design? Is my coding style alright? How could I have taken advantage of pointers?

Here is one more excerpt from my actual program that shows the usage of the ATMMenu class and also the for loop I was talking about.

// Select account to display or withdraw from
case SELECT_ACCOUNT:
{
ATMMenu selAcct;
selAcct.SetText(accountSelect);
// Adds the options, checking and savings are both //string variables that are set in the SELECT_LANGUAGE state of //the loop
selAcct.AddOption(1, checking);
selAcct.AddOption(2, savings);


// allows the user multiple chances to enter correct
//input
for (int i = 0; i < selAcct.GetMaxTries(); i++)
{
selAcct.Display();
switch (selAcct.GetInput())
{
case 1:
//activeAccount is an enum
activeAccount = CHECKING;
curBal = CheckingBalance;
curState = BALANCE_OR_WITHDRAW;
break;
case 2:
activeAccount = SAVINGS;
curBal = SavingsBalance;
curState = BALANCE_OR_WITHDRAW;
break;
// 0 is used to exit the program.
case 0:
exit = true;
break;
default:
// handles all other entered numbers
cout << endl << invalidNumberError << endl;
break;
}

// If the correct input is received, break out of the for loop.
if (curState == BALANCE_OR_WITHDRAW || exit == true)
{
break;
}
}
break;
}


Thanks for any help I'm given.
Advertisement
You could implement the menus as a state, if you then initialise them all at the begining of the application you can do a state change by just making a different state the current active state. Here is some psuedocode that shows the idea.


class MenuState //Interface for all the menus
{
public:
virtual void initialise(const LanguageChoice currentLanguage);
virtual void display();
virtual void handleInput();
virtual NextMenuStateType getNextMenuTransition();
}

class MainMenuState : public MenuState
{
...
}

class SecondMenuState : public MenuState
{
...
}

void main()
{
//Initialise all the menus you need here

MenuState* currentActiveMenu = getMainMenu(); //This gets the first menu state somehow
while(runApp)
{
currentActiveMenu->display();
currentActiveMenu->handleInput();
NextMenuStateType nextMenu = currentActiveMenu->getNextMenuTransition();
currentActiveMenu = getNextMenuFromTransitionType(nextMenu);
}
}


The benefit of this is that you don't have a massive state machine in your main loop and if you decide to have to add an additional menu, you can just add the state, initialise it, add a link in to a previous state, add a new NextMenuStateType or look up of this new state and it will work, all without having to add a new case label and case in the switch.

Worked on titles: CMR:DiRT2, DiRT 3, DiRT: Showdown, GRID 2, theHunter, theHunter: Primal, Mad Max, Watch Dogs: Legion

Alright. That makes sense. Is it okay to make a new class for every menu I have? For example, have a class FirstMenu, SecondMenu, ThirdMenu, all the way up to like 10 menus, that all extend the abstract class? I didn't want to go overboard with classes, so I avoided that, but I thought of doing something similar. My main method is quite bloated as of right now as a result.

Alright. That makes sense. Is it okay to make a new class for every menu I have? For example, have a class FirstMenu, SecondMenu, ThirdMenu, all the way up to like 10 menus, that all extend the abstract class? I didn't want to go overboard with classes, so I avoided that, but I thought of doing something similar. My main method is quite bloated as of right now as a result.


Yeah thats fine thats what specialisation in inheritance is for, but you should try to push common functionality to the base class still if possible. I wouldn't name them as you just suggested though I'd call them that because it was an example only. Names like MainMenu, OptionsMenu, GraphicsMenu are far better, I am using a game as example here only.

And what I mean with common functionality is stuff like the strings for a screen can be stored in a vector in the base class, the display function doesn't nessecarily need to be virtual as a likely implementation would be this in the base class:

void MenuState::display()
{
for(std::vector<std::string>::iterator it = m_strings.begin(); it != m_strings.end(); ++it)
{
std::cout << *it << std::endl; //Output all the strings in the string vector line by line.
}
}

Worked on titles: CMR:DiRT2, DiRT 3, DiRT: Showdown, GRID 2, theHunter, theHunter: Primal, Mad Max, Watch Dogs: Legion

This topic is closed to new replies.

Advertisement