"Meatless MVC" seems success but new problem ahead.

From: Val (valmont_programming_at_hotmail.com)
Date: 10/03/04


Date: Sun, 3 Oct 2004 21:21:33 +0200

Hello everybody. I've decided to completely revamp the design of my toy database program. It seems a major success up to
now. I am quite proud. All the design-know-how you see comes from this NG. However, I encountered a problem that *must*
be solved in the spirit of comp.oo otherwise my efforts are just worthless.

Before I start, I don't have a CS degree so I don't know how to communicate about analiys and design the way you people
do. So please forgive me for my clumsyness. Also note that the source from the code below can be downloaded from my
webspace: http://home.tiscali.nl/~valmont/cpp/downloads/simplyfied.zip
The complete implented source (with a txt file wich is the database) can be downloaded from
http://home.tiscali.nl/~valmont/cpp/downloads/complete.zip

Okidoky.
The code below seems to be nicely implemented. Here is the scenario: suppose I wan't to find a record based on it's
first name, then the txt file will be loaded from disk into a vector (the model and the engine do that all well enough).
Then the vector will be sorted. Then a binary search algo will do the rest. Easy. But now my questions are:
1) Who the heck should be responsible for actually "viewing" the record?
     In the "complete implemented" source, I could use ConsoleMenu class for it as well (just rename it offcourse). But
then my engine (or model) would have to know about views.
     I don't want to actually.
     Currently my engine and the model(s) don't have a clue about views and menus and menu-items and Application
class(es). I just love that!
     Suppose you'd like to make big && out of this, thinking well ahead of might be coming next ( :D ), how could I
solve this riddle please?
2) When I view the record I wanted to find (if exists),I *might* want the option to observe all the records, wich are
now sorted by the designated field (first name, surname, phone number).
     Or I might want to exit straight away, or might want to start a new search.
     How do I do this the OO way? I have a-b-s-o-l-u-t-e-l-y no clue!
     Obviously I can not "cheat" by making a backup of the sorted version. Once I search, the sort comes automatically
with it and sorts the vector. Not the txt file itself.

Well that's it. Code below, although two versions can be downloaded.

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

using namespace std;

int main(int argc, char *argv[])
{
   Application App;
   App.run();

  return 0;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////

/*** Application.h ***/

#ifndef APPLICATION_H
#define APPLICATION_H

#include "Menu.h"
#include <string>

using std::string;

enum FieldName
{ FIRSTNAMEFIELD, SURNAMEFIELD, PHONENUMBERFIELD, LISTALL };

class Application
{
public:
   Application();
   ~Application();
 void run();
 void startEng(FieldName, string);
 void exit();
private:
   Menu* _menu;
   bool _b_running;
};

#endif //APPLICATION_H

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////

/*** Application.cpp ***/

#include "Application.h"
#include "ConsoleMenu.h"

/*** Lets pretend we have the engine ready as well
#include "SomeDatabaseEngine.h"
#include "TemplateMethodInterface.h"
#include "TemplateMethodFindByFirstName.h"
#include "TemplateMethodFindBySurName.h"
#include "TemplateMethodFindByPhoneNumber.h"
*/

Application::Application() : _b_running(true), _menu(new ConsoleMenu)
{
   _menu->addEntry(createMenuEntry(this, &Application::exit,
   "Exit Application") );
   //**Below only typical example. Not implemented yet though!
   // _menu->addEntry(createMenuEntry(this, &Application::exit, FIRSTNAMEFIELD,
   // "Exit Application") );
}

void Application::run()
{
  while(_b_running)
  {
     _menu->display();
     _menu->execute();
  }
}

void Application::startEng(FieldName fn, string item)
{
   /* Do some primary stuff like sort, then binary search.
      The enum "FieldName" will instatiate the engine with correct params
   ( using Template Method Design ) */

   /*Example below:
   -----------------------------------------------------------
 TemplateMethodInterFace* TMIF;
      switch(fn)
      {
         case FIRSTNAMEFIELD :
        TMIF = new TemplateMethodFindByFirstName;
         break;
      }
 SomeDataBaseEngine currEngine(TMIF, item);
 delete TMIF;
 ------------------------------------------------------------
 end example. */
}

void Application::exit()
{
   _b_running = false;
}

Application::~Application()
{
   delete _menu;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////

#ifndef MENU_H
#define MENU_H

#include "MenuItem.h"
#include <vector>
#include <algorithm>
#include <iterator>
#include <string>

using std::vector;
using std::for_each;
using std::iterator;

// A little helper for this Menu class.
struct Delete
{
   template<typename T>
   void operator()(T* thestuff)
   { delete thestuff; }
};

class Menu
{
public:
   void addEntry(MenuItem* mitem)
   {
      _menuItems.push_back(mitem);
   }
   virtual void display() = 0;
   virtual void execute() = 0;
   ~Menu()
   {
      for_each(_menuItems.begin(), _menuItems.end(), Delete() );
   }
protected:
   vector<MenuItem*> _menuItems;
   vector<MenuItem*>::iterator itemIterator;
};

#endif //MENU_H

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////

#ifndef MENUITEM_H
#define MENUITEM_H

#include <string>

using std::string;

class MenuItem
{
public:
   virtual void command() = 0;
};

template<typename T>
class MenuItemNoParams : public MenuItem
{
public:
   typedef void(T::*Method)();
public:
   MenuItemNoParams(T* pClass, Method mthd, string const& text) :
      _theClass(pClass), _theMethod(mthd), _sItemText(text)
      {}
protected:
   void command()
   {
      (_theClass->*_theMethod)();
   }
private:
   T* _theClass;
   Method _theMethod;
   string _sItemText;
};

template<typename T>
MenuItemNoParams<T>*
createMenuEntry(T* pClass, void(T::*method)(), string const& text)
{
   //Note that class "Menu" is responisble for freeing this allocated memory!
   return new MenuItemNoParams<T>(pClass, method, text);
}

#endif //MENUITEM_H

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////

#ifndef CONSOLEMENU_H
#define CONSOLEMENU_H

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

using std::cin;

class ConsoleMenu : public Menu
{
public:
   void display();
   void execute();
};

void ConsoleMenu::display()
{
   /* Just iterate over the vector, showing the menu options. */
}

void ConsoleMenu::execute()
{
   unsigned choice;
 cin>>choice;
 _menuItems[choice]->command();
}

#endif //CONSOLEMENU_H
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////

Oh, and nevermind robustness, as long as it is not in the way of the design issues we currently discuss here :)

- Val -



Relevant Pages

  • Last design issues to be solved.
    ... I've decided to completely revamp the design of my toy database program. ... #ifndef APPLICATION_H ... void startEng; ...
    (comp.object)
  • Is this correct OO design?
    ... //Set properties of Article Object with values from Database ... IS IT BAD OO DESIGN TO CREATE AN INSTANCE OF A CLASS ... Article OriginalArticle = new Article; ... public static void GetArticleData ...
    (microsoft.public.dotnet.general)
  • Re: design quicky
    ... >>I have a design problem that occurs fairly often. ... >> void f ... not the responsibility of the client. ... clients can define the functionality of the map at runtime. ...
    (comp.object)
  • Re: Is this correct OO design?
    ... This is NOT bad design. ... look at the timestamp data type in SQL Server, and Optimistic Concurrency ... > (OriginalArticle) WITHIN THE CLASS ITSELF? ... > public static void GetArticleData ...
    (microsoft.public.dotnet.general)
  • [RFC][PATCH (2/4)] new timeofday arch specific hooks (v A4)
    ... +#ifndef CONFIG_NEWTOD ... static void sync_cmos_clock; ... +#ifdef CONFIG_HPET_TIMER ...
    (Linux-Kernel)