View Single Post
Old 01-14-2005, 01:03 PM   #13 (permalink)
Valmont
[code][/code] enforcer
 
Valmont's Avatar
 
Join Date: Mar 2003
Location: Netherlands
Posts: 1,544
Valmont is on a distinguished road
Quote:
I've been working on C++ code for 5 years and am taking 400 level college classes.
In that case, why not start to program object oriented. That doesn't mean encapsulation only, but also independency. Besides that. Consistancy as well. My credo always stands:
Programming is communicating

- Since you have structs of a Student, Grade and Course, you are able to see the true domain problem: Students, Grades and Courses. Encapsulate these structs in classes, with the names: Students, Grades and Classes.
- Store all the students, grades and classes in a vector. That vector is obviously in the classes I just mentioned.
So the classes are just wrappers for the vector.
Code:
#include "Student.h"
#include <fstream>
#include <vector>

using std::vector;
using std::ifstream; 
using std::ofstream;
//And so forth.

class Students
{
public:
  Students();
  vector<Student>& get_students();
  void create_database();
private:
    vector<Student> _vecStudents;  
};
Do this for the Grades class and Courses class as well.
Now these classes are the classes that matter actually. Therefore, consider this our domain to keep it simple but clear.

Now there is your function handling.
Your functions are long and often misleading. Or not clear at all. Suppose I am a stranger to your library. How would I know what a courgrad() is? Make so any fool could understand what it does.
Furthermore, lot of your functions are extremely long. Keep a simple rule:
If a function or method has 25 lines of code or more, then split it up.
We want to keep things readable.
Also make sure that the responsabilities for your the things you create are clear. Don't let them do two or more things. Let it do only 1 and 1 thing only.

Ok, let's think object oriented. You have 5 years experiance or so. I expect you to understand the things I post below this.

First of all we want the menu seperated from the rest. We also don't want return it an int. We don't like throwing "menu-int's" all over the application.
So we implement the Command Design pattern:
This way, we can modify our view but also our controller(s) or concrete command implementations without affecting the other entities.
The code below is complete and working. Notice how it supports submenu's. Just like in MS Windows or a GUI Linux environment. Take it and play with it. -- Create your own menu's and submenus in the main() function.
- The Lookup submenu is obviously for finding and printing items on screen. Like all students, or a specific one. Whatever. I implemented one demo Print Command.
- Convince yourself that proper clean-up is done. What is required to guarantee proper clean-up?
- What is needed to support an "undo" command for everything you change in the database(s) and/or table(s)?
- Would you even like to clean up main() and therefore create an Application class, and let that class to all the pre-initialization?

main.cpp
Code:
#include "Command.h"

int main()
{
  Menu MainMenu( "Main Menu" );
  Menu* LookupMenu = new Menu( "Search and Print Menu" );
  
  MainMenu.add( LookupMenu );
  MainMenu.add( new QuitCmd );
  
  LookupMenu->add( new PrintCmd );
  LookupMenu->add( new ReturnCmd );

  MainMenu.execute(); 

  return 0;

}
Command.h
Code:
#ifndef COMMAND_H
#define COMMAND_H

#include <vector>
#include <string>

using std::string;
using std::vector;

//Abstract.
class Command
{
public:

   Command(const string name);
  virtual ~Command();
  virtual bool execute() = 0;
  string get_name();
private:
  //Protect copy ctor and assignment.
  Command(const Command&);
  Command& operator=(const Command& rho);
  const string _sName;
};


//------------------------------------------

class Menu : public Command
{
public:
   Menu(const string name);
  virtual ~Menu();
  void add(Command* pCommand);
  bool execute();
private:
  Menu(const Menu&);
  Menu& operator=(const Menu& rho);
  Command* select(int index);
  void show();
  int get_user_input();
  vector<Command*> _vecMenuItemCommands;
};

//------------------------------------------

class PrintCmd : public Command
{
public:
  PrintCmd();
  ~PrintCmd();
  bool execute();
private:
  PrintCmd(const PrintCmd&);
  PrintCmd& operator=(const PrintCmd& rho);
};

//------------------------------------------

class ReturnCmd : public Command
{
public:
  ReturnCmd();
  ~ReturnCmd();
  bool execute();
private:
  ReturnCmd(const ReturnCmd&);
  ReturnCmd& operator=(const ReturnCmd& rho);
};

//------------------------------------------

class QuitCmd : public Command
{
public:
  QuitCmd();
  ~QuitCmd();
  bool execute();
private:
  QuitCmd(const QuitCmd&);
  QuitCmd& operator=(const QuitCmd& rho);
};


#endif  //COMMAND_H
Command.cpp
Code:
#include "Command.h"
#include <iostream>

using std::cout;
using std::endl;
using std::cin;

// CLASS COMMAND //

Command::Command(const string name) :
  _sName(name)
{ }

Command::~Command()
{ }

string Command::get_name()
{
  return _sName;
}

// CLASS MENU //

Menu::Menu(const string name) :
    Command(name)
{ }

Menu::~Menu()
{
  Command* p;
  unsigned sz=_vecMenuItemCommands.size();
  for(int i = 0; i < sz; ++i)
  {
    p = _vecMenuItemCommands[i];
    delete p;
  }
}

void Menu::add(Command* pCommand)
{
  _vecMenuItemCommands.push_back( pCommand );
}

bool Menu::execute()
{
  Command* pCmd;
  do
  {
    show();
    int userInput = get_user_input();
    pCmd = select( userInput );
  }
  while( pCmd->execute() );
  return true;
}

void Menu::show()
{
  cout << endl << "*** " << get_name() << " ***" << endl;
  for(int i=0; i<_vecMenuItemCommands.size(); ++i)
  {
    cout << i+1 << ". " << _vecMenuItemCommands[i]->get_name() << endl;
  }
}

Command* Menu::select(int index)
{
  return _vecMenuItemCommands[index-1];
}

int Menu::get_user_input()
{
  int input = 0;
  while(input < 1 || input > (_vecMenuItemCommands.size()))
  {
    cout << "Please select an item 1-" << (_vecMenuItemCommands.size()) << endl << ">";
    cin >> input;
  }
  return input;
}

// CLASS RETURNCMD //

ReturnCmd::ReturnCmd() :
  Command("Return To Previous Menu")
{ }

ReturnCmd::~ReturnCmd()
{ }

bool ReturnCmd::execute()
{
  return false;
}

// CLASS PRINTCMD //

PrintCmd::PrintCmd() :
  Command("Print")
{}

PrintCmd::~PrintCmd()
{}

bool PrintCmd::execute()
{
  cout<<"I am printing stuff :)"<<endl;
  return true;
}

// CLASS QUITCMD //

QuitCmd::QuitCmd() :
  Command("Quit Application")
{ }

QuitCmd::~QuitCmd()
{ }

bool QuitCmd::execute()
{
  return false;
}
That's it.
Look how I don't care about student(s) and anything else. I've setup my neat menu framework. Later I will include the classes (models !) and implement all the concrete execute() methods. But even they won't know the details of what's going on.

See how far you can get with this.
__________________

Last edited by Valmont; 01-15-2005 at 07:45 AM.
Valmont is offline   Reply With Quote