MVC в c++.
// Интерфейсы (чтобы хедер модели был
// не нужен контроллеру и виду)
struct mParentC
{
virtual void SetParam(int a) = 0;
};
struct mParentV
{
virtual int GetParam() = 0;
};
// Модель
class m: public mParentC, public mParentV
{
int param;
public:
int GetParam(){return param;}
void SetParam(int a){param = a;}
m(int a){ param = 0;}
};
// Вид
struct v
{
mParentV *mPtr;
v(mParentV *p):mPtr(p){}
void PrintParam()
{
std::cout << mPtr->GetParam() << std::endl;
}
static void PrintError()
{
std::cout << "Error" << std::endl;
}
};
// Контроллер
struct c
{
mParentC *mPtr;
c(mParentC *p):mPtr(p){}
void SetParam(int a)
{
if(a > 0)
mPtr->SetParam(a);
else
v::PrintError();
}
};
int main()
{
m mObject(5);
c cObject(&mObject);
v vObject(&mObject);
vObject.PrintParam();
cObject.SetParam(-1);
cObject.SetParam(10);
vObject.PrintParam();
return 0;
}
{
private:
Model m_Model;
View * m_pView; // или даже Views
};
{
Model * m_pModel;
};
{
private:
Model m_Model;
class Subcontroller : public View
{
private:
Model * m_Model;
};
Subcontroller * m_pSubcontroller; // = new Subcontroller(&m_Model);
}
struct ISimpleController
{
virtual void StroreModel(T * model) = 0;
virtual void RestoreModel(T * model) = 0;
};
class Controller : public ISimpleController<Model>, public View
{
void StroreModel(Model * model)
{
}
void RestoreModel(Model * model)
{
}
};
Может я чего-то не понял, но в моем примере при изменении Контроллера или Представления Модель не меняется - в ней же не используется объекты этих классов. Или говорится о другой зависимости?
Ну, для контроля времени жизни можно код поменять:
struct ModelInterface
{
virtual void SetParam(int a) = 0;
virtual int GetParam() = 0;
~ModelInterface(){}
};
class Model: public ModelInterface
{
int param;
public:
int GetParam(){return param;}
void SetParam(int a){param = a;}
Model(int a){ param = a;}
};
struct View
{
static void PrintParam(ModelInterface *mPtr)
{
std::cout << mPtr->GetParam() << std::endl;
}
static void PrintError()
{
std::cout << "Error" << std::endl;
}
};
class Controller
{
ModelInterface *m_pModel;
public:
Controller()
{
m_pModel = new Model(5);
}
~Controller()
{
delete m_pModel;
}
void SetParam(int a)
{
if(a > 0)
m_pModel->SetParam(a);
else
View::PrintError();
}
void PrintParam()
{
View::PrintParam(m_pModel);
}
};
int main()
{
Controller m_Controller;
m_Controller.PrintParam();
m_Controller.SetParam(3);
m_Controller.PrintParam();
m_Controller.SetParam(-1);
return 0;
}
Вообще-то ModelInterface - это и есть часть компонента модели. То есть я вынес в него все функции модели, которые могут потребоваться каким-либо другим классам. В нем могут быть функции, которые не используются Контроллером и Представлением.
Он не есть обязательная часть. Можно обращаться напрямую к модели, но если обращаться через такой "интерфейс", то не надо включать в Контроллер хедер всей Модели, который может содержать кучу ненужных для других классов данных.
По идее, в готовом чужом классе, должно быть что-то подобное: то есть набор нужных функций, через которые можно получить доступ ко всем нужным данным.
Пройдёмся по ответственностям: View - отображение, Model - часть предметной области, Controller - высокоуровневые операции. Пример операции для Controller в вашем случае: запросить значение через View, вычислить результат через Model, вывести результат через View. Всё это - одна операция. Другой пример: запросить данные, запросить имя файла, передать данные вычисляющей модели, получить результат, отдать его и имя файла модели, реализующей сохранение данных, сообщить о результате. Опять одна операция.
Композиция MVC: есть таблица с данными, необходимо её сохранить. Модель трёхуровневая. Первый - общий, второй - для таблицы (набора записей в контексте модели), третий - для строки таблицы (записи). Контроллеры всех трёх уровней имеют высокоуровневую операцию SaveData(). Контроллер первого уровня запрашивает имя файла, делегирует вызов контроллеру второго уровня. Контроллер второго уровня проходит по всем записям, и делегирует вызов контроллеру для каждой из них (условно. контроллер может быть один и иметь метод SaveData(TableRow, Record), либо являться наследником TableRow, если класс таблицы позволяет это сделать). Контроллер третьего уровня синхронизирует строку таблицы (вид) с записью (модель) и возвращает управление. Контроллер первого уровня передаёт модели путь к файлу и вызывает её метод SaveToFile(). В контексте контроллера первого уровня, всё это - одна операция.
Поупражняюсь для лучшего понимания. Работу с файлами заменю на что-нибудь.
{
int param;
public:
int GetResult(){return param * 2;}
ModelCalc(int a){ param = a;}
};
namespace ModelSave
{
bool SaveNumberToFile(std::string fileName, int n)
{
if(n) return true;
else return false;
}
};
namespace View
{
std::string GetFileName()
{
std::cout << "Input File Name: ";
std::string fileName;
std::cin >> fileName;
return fileName;
}
int GetNumber()
{
std::cout << "Input Number: ";
int n = 0;
if(!(std::cin >> n))
{
std::cin.clear();
std::cin.ignore(255, '\n');
std::cout << "Error!" << std::endl;
return GetNumber();
}
return n;
}
void GoodSave()
{
std::cout << "File Saved" << std::endl;
}
};
struct Controller
{
bool InputAndSave()
{
ModelCalc mCalc(View::GetNumber());
if(ModelSave::SaveNumberToFile(View::GetFileName(), mCalc.GetResult()))
{
View::GoodSave();
return true;
}
return false;
}
};
int main()
{
Controller m_Controller;
while(m_Controller.InputAndSave()){}
return 0;
}
Интереснее было бы, если бы таблица, не сохранялась, а загружалась. Тогда и нужно синхронизировать с видом.
Итак. Модель первого уровня - самодельный класс, второго уровня - вектор строк, третьего - строка. Контроллер первого уровня получает через Вид имя файла и передает модели. Модель загружает данные из файла (хотя это мог бы наверно делать один из контроллеров). После этого Контроллер второго уровня синхронизирует таблицу с видом.
class Model
{
vector<string> table;
public:
Model():table(10, "line"){}
size_t Size(){return table.size();}
string GetLine(size_t n){ return table[n]; }
void Load(string fileName)
{
for(size_t i = 0; i < table.size(); ++i)
{
table = fileName;
}
}
};
namespace View
{
string GetString(string text)
{
cout << "Input "<< text <<": ";
string str;
cin >> str;
return str;
}
void PrintStr(const string &line)
{
cout << line << endl;
}
};
class Controller
{
Model m_Model;
struct SubController
{
Model *m_pModel;
SubController(Model *p):m_pModel(p){}
void UpdateTable()
{
for(size_t i = 0; i < m_pModel->Size(); ++i)
{
View::PrintStr(m_pModel->GetLine(i));
}
}
};
public:
void LoadFromFile()
{
m_Model.Load(View::GetString("File Name"));
SubController sc(&m_Model);
sc.UpdateTable();
}
};
int main()
{
Controller m_Controller;
m_Controller.LoadFromFile();
return 0;
}
Вроде принцип понял. Осталось проникнуться смыслом всего этого :)
Следует заметить, что в чистом виде вторая часть взаимодействия работает автоматически благодаря механизму событий .NET. В С++, к сожалению, для подобной автоматики нередко приходится делать дополнительную декомпозицию, и делать подконтроллер частью отображения (либо придётся долго выяснять, какие записи были удалены, какие - добавлены, а какие - изменены).
И самое главное. Синхронизация - лишь крохотная часть задач, возлагаемых на контроллер. Вообще, повторюсь, контроллер производит высокоуровневые операции над моделью и отображением.