Witam,
Postawiłem sobie za cel napisanie ładnego kodu aplikacji konsolowej, posiadającego menu z trzema opcjami, z których dwie wchodzą do podmenu a trzecia wyłącza program. Każde z podmenu ma dwie opcje: przykładowa akcja i wstecz.
Chciałem zastosować wzorzec MVC jednak nie jestem pewien czy do końca wyszło to tak jak powinno pod względem ideowym, ponieważ nie wszystko zrobiłem dokładnie tak jak znalazłem w opisie wzorca. W internecie ciężko znaleźć jakikolwiek przykład w C++ pod konsolę. Kod, który wkleiłem kompiluje się i działa poprawnie (informacja dla tych co omijają wywody autora znajdujące się powyżej kodu).
Chciałbym, żeby ktoś w wolnej chwili skrytykował mi ten kod tj.:
- np. napisał coś w stylu: to nie jest wzorzec MVC, powinieneś raczej... (tutaj opis co robię źle i jak to powinno wyglądać)
- co mogłoby być lepiej/inaczej
- odpowiedź na pytanie, czy poniższy kod dałoby się przerobić bez większych problemów (tj. dopisanie tylko odpowiedniej klasy widoku) na aplikację okienkową.
Za poświęcony czas z góry dziękuję :)
Kod jest w jednym pliku, jednak myślę, że jest dość czytelny
#include <vector>
#include <queue>
#include <string>
#include <iostream>
#include <sstream>
using namespace std;
class View;
class Controller;
class Model;
class View
{
public:
virtual void show()=0;
Controller* getController() { return controller; }
virtual void setController(Controller* controller) { this->controller = controller; }
virtual ~View() {}
private:
Controller* controller;
};
typedef int ActionType;
class Controller
{
public:
typedef queue<ActionType> Actions;
Controller() {}
void userAction(ActionType actionType) { actions.push(actionType); }
void setModel(Model* model) { this->model = model; }
Model* getModel() { return model; }
bool processActions()
{
if (actions.empty())
return false;
ActionType action = actions.front();
actions.pop();
processAction(action);
return true;
}
virtual void processAction(ActionType action)=0;
virtual ~Controller() {}
private:
Model* model;
Actions actions;
};
class Model
{
public:
virtual void setView(View* view)
{
this->view = view;
view->setController(controller);
}
virtual void setController(Controller* controller)
{
this->controller = controller;
controller->setModel(this);
if (view)
view->setController(controller);
}
Controller* getController() { return controller; }
View* getView() { return view; }
virtual void displayView() { view->show(); }
Model(): view(NULL), controller(NULL) {}
virtual ~Model() {}
private:
View* view;
Controller* controller;
};
class ConsoleMenuItem: public View
{
public:
virtual void show()=0;
virtual ActionType getActionType()=0;
void notify() { getController()->userAction(getActionType()); }
};
class ConsoleMenuCaption: public ConsoleMenuItem
{
public:
ConsoleMenuCaption(const string& caption, ActionType actionType): caption(caption), actionType(actionType) {}
void show() { cout << caption; }
ActionType getActionType() { return actionType; }
private:
string caption;
ActionType actionType;
};
class ConsoleMenuView: public View
{
public:
typedef vector<ConsoleMenuItem*> Items;
void addItem(ConsoleMenuItem* v)
{
items.push_back(v);
v->setController(getController());
}
void setController(Controller* controller)
{
View::setController(controller);
for (auto it : items)
it->setController(getController());
}
ConsoleMenuItem* getItem(int index) { return items[index]; }
void removeItem(int index) { items.erase(items.begin()+index); }
void show()
{
unsigned int n=1;
for (int i=0; i<25; i++) cout << "\n";
for(auto it : items)
{
cout << n++ << ". ";
it->show();
cout << "\n";
}
cout.flush();
string line;
do
{
getline(cin, line);
istringstream ss(line);
n = 0;
ss >> n;
n--;
}
while (n>=items.size());
getItem(n)->notify();
}
private:
Items items;
};
/********************************************************/
class MainMenu
{
public:
static const ActionType OPTIONQUIT,OPTION1,OPTION2;
View* getView() { return view; }
MainMenu()
{
view = new ConsoleMenuView;
quit = new ConsoleMenuCaption("Quit",OPTIONQUIT);
op1 = new ConsoleMenuCaption("Menu 1",OPTION1);
op2 = new ConsoleMenuCaption("Menu 2",OPTION2);
view->addItem(op1);
view->addItem(op2);
view->addItem(quit);
}
~MainMenu()
{
delete view;
delete op1;
delete op2;
delete quit;
}
private:
ConsoleMenuView *view;
ConsoleMenuCaption *op1,*op2,*quit;
const MainMenu& operator= (const MainMenu&) { return *this; }
MainMenu(const MainMenu&) {}
};
class SubMenu1
{
public:
static const ActionType OPTION1,OPTION2;
View* getView() { return view; }
SubMenu1()
{
view = new ConsoleMenuView;
op1 = new ConsoleMenuCaption("Cos 1",OPTION1);
op2 = new ConsoleMenuCaption("wstecz",OPTION2);
view->addItem(op1);
view->addItem(op2);
}
~SubMenu1()
{
delete view;
delete op1;
delete op2;
}
private:
ConsoleMenuView *view;
ConsoleMenuCaption *op1,*op2;
const SubMenu1& operator= (const SubMenu1&) { return *this; }
SubMenu1(const SubMenu1&) {}
};
class SubMenu2
{
public:
static const ActionType OPTION1,OPTION2;
View* getView() { return view; }
SubMenu2()
{
view = new ConsoleMenuView;
op1 = new ConsoleMenuCaption("Bla bla 1",OPTION1);
op2 = new ConsoleMenuCaption("wstecz",OPTION2);
view->addItem(op1);
view->addItem(op2);
}
~SubMenu2()
{
delete view;
delete op1;
delete op2;
}
private:
ConsoleMenuView *view;
ConsoleMenuCaption *op1,*op2;
const SubMenu2& operator= (const SubMenu2&) { return *this; }
SubMenu2(const SubMenu2&) {}
};
const ActionType MainMenu::OPTIONQUIT = 0;
const ActionType MainMenu::OPTION1 = 1;
const ActionType MainMenu::OPTION2 = 2;
const ActionType SubMenu1::OPTION1 = 3;
const ActionType SubMenu1::OPTION2 = 4;
const ActionType SubMenu2::OPTION1 = 5;
const ActionType SubMenu2::OPTION2 = 6;
class Application;
class AppController: public Controller
{
public:
AppController(Application* app): app(app) {}
void processAction(ActionType action);
private:
Application* app;
};
class Application
{
public:
MainMenu mainMenu;
SubMenu1 subMenu1;
SubMenu2 subMenu2;
void setView(View* view) { model->setView(view); }
void display() { model->displayView(); }
void run() { display(); }
Application()
{
model = new Model;
controller = new AppController(this);
model->setController(controller);
model->setView(mainMenu.getView());
}
~Application()
{
delete controller;
delete model;
}
bool running()
{
return controller->processActions();
}
private:
const Application& operator= (const Application&) { return *this; }
Application(const Application&) {}
Controller* controller;
Model* model;
};
void AppController::processAction(ActionType action)
{
switch (action)
{
case MainMenu::OPTION1:
{
app->setView(app->subMenu1.getView());
break;
}
case MainMenu::OPTION2:
{
app->setView(app->subMenu2.getView());
break;
}
case SubMenu1::OPTION2:
case SubMenu2::OPTION2:
{
app->setView(app->mainMenu.getView());
break;
}
case SubMenu1::OPTION1:
{
cout << "Jakas akcja 1. Nacisnij ENTER..." << endl;
string foo;
getline(cin,foo);
break;
}
case SubMenu2::OPTION1:
{
cout << "Jakas akcja 2. Nacisnij ENTER..." << endl;
string foo;
getline(cin,foo);
break;
}
}
if (action!=MainMenu::OPTIONQUIT)
app->display();
}
int main()
{
Application app;
app.run();
while (app.running());
return 0;
}