先来看一个传统的MVC模式:
class FooModel {
public:
int getNumber() {return m_number;}
void setNumber(int m) {m_number = m; m_view->numberChanged();}
FooView *getView() {return m_view;}
void setView(FooView *view) {m_view = view;}
};
class FooView : public Window {
public:
FooModel *getModel() {return m_model;}
void setModel(FooModel *m) {m_model = m;}
void numberChanged() {...}
void buttonPressed() {...} /* Problems Here! How to implements this? */
private:
TextEdit m_numberEdit;
Button m_buttonOk;
Button m_buttonCancel;
};
class FooControl {
public:
FooControl() {
m_model = new FooModel();
m_view = new FooView();
m_view->setModel(m_model);
m_model->setView(m_view);
}
FooModel *getModel();
FooView *getView();
};
在上面的例子中,控制器FooControl负责初始化模型和视图,当模型FooModel被修改之后会调用视图FooView的回调函数numberChanged(),来通知视图。
问题出现在当用户点击界面上一个按钮(比如OK)时,应该将TextEdit中的值赋给FooModel,也就是说如何从视图修改模型。假设按钮按下的时候函数buttonPressed会
被调用,现在的问题是如何实现它?
在MVC的惯例中View不能够直接修改Model(否则MVC的意义何在?),于是下面的代码是错误的。
//: Imp.1
void FooView::buttonPressed() {
m_model->setNumber(m_numberEdit.getNumber()); // It's wrong!
}
//:~
在MVC中Model的修改都是通过Control来完成的,View只是简单的将修改信息传给Control,所以我们在FooControl中加入方法setNumber(int n);
//: Imp.2
void FooView::buttonPressed() {
m_control->setNumber(m_numberEdit.getNumber());
}
void FooControl::setNumber(int n) {
m_model->setNumber(n);
// do record here!
}
//:~
但是新的问题是,我们没有任何硬性的方法来保证FooView调用FooControl(Imp.2),而不是直接调用FooModel的方法(Imp.1)。
事实上Imp.1方法的诱惑力是很大的,一不小心就会实现成那样。一方面FooView必须调用FooModel的get方法来获得数据,而另一方面又必需调用FooControl的set方法来
修改数据。除非你非常明白你在干什么否则很容易就写出类似Imp.1的方法。
还有一种方法是FooView并不含有FooModel,FooView的所以操作都通过FooControl来完成,无论是get还是set,我们称之为Imp.3。
在 Agile Web Development with Rails 上有着这样一句话:“保持关注点的清晰分离原本就是开发者的职责,不应该强求工具来确保这种分离”(Page. 19),说实话
当我看到这句话时我真的不知道下面的代码是否还有意义。
下面的代码是我在开发一个图像处理程序时设计的结构,通过接口强制限制了View对Model的set操作。
class IFooModel {
public:
virtual int getNumber() = 0;
};
class IFooModelObserver {
public:
virtual void numberChanged(IFooModel *model) = 0;
};
class FooModel : public IFooModel {
public:
virtual int getNumber() {return m_number;}
void setNumber(int number) {m_number = number; m_observer->numberChanged(this);}
IFooModelObserver *getObserver();
void setObserver(IFooModelObserver *observer);
};
class FooView {
private:
IFooModel *m_model; // Important!
FooControl *m_control;
};
class FooControl {
private:
...
};
即利用继承将Model的get接口和set接口分离出来。
对于从FooView中修改FooModel的操作我们引入一个新的FooActionSetNumber:
class FooActionSetNumber : public FooAction {
public:
FooActionSetNumber(IFooModel *model, int number) : m_model(model), m_number(number) {}
virtual void execute() {
dynamic_cast<FooModel *>(m_model)->setNumber(m_number);
}
}
在FooView中我们可以用IFooModel构造这个Action,然后将其交给FooControl执行。
class FooControl {
public:
void execute(FooAction *action) {
action->execute();
// record action here!
}
};
class FooModel {
public:
int getNumber() {return m_number;}
void setNumber(int m) {m_number = m; m_view->numberChanged();}
FooView *getView() {return m_view;}
void setView(FooView *view) {m_view = view;}
};
class FooView : public Window {
public:
FooModel *getModel() {return m_model;}
void setModel(FooModel *m) {m_model = m;}
void numberChanged() {...}
void buttonPressed() {...} /* Problems Here! How to implements this? */
private:
TextEdit m_numberEdit;
Button m_buttonOk;
Button m_buttonCancel;
};
class FooControl {
public:
FooControl() {
m_model = new FooModel();
m_view = new FooView();
m_view->setModel(m_model);
m_model->setView(m_view);
}
FooModel *getModel();
FooView *getView();
};
在上面的例子中,控制器FooControl负责初始化模型和视图,当模型FooModel被修改之后会调用视图FooView的回调函数numberChanged(),来通知视图。
问题出现在当用户点击界面上一个按钮(比如OK)时,应该将TextEdit中的值赋给FooModel,也就是说如何从视图修改模型。假设按钮按下的时候函数buttonPressed会
被调用,现在的问题是如何实现它?
在MVC的惯例中View不能够直接修改Model(否则MVC的意义何在?),于是下面的代码是错误的。
//: Imp.1
void FooView::buttonPressed() {
m_model->setNumber(m_numberEdit.getNumber()); // It's wrong!
}
//:~
在MVC中Model的修改都是通过Control来完成的,View只是简单的将修改信息传给Control,所以我们在FooControl中加入方法setNumber(int n);
//: Imp.2
void FooView::buttonPressed() {
m_control->setNumber(m_numberEdit.getNumber());
}
void FooControl::setNumber(int n) {
m_model->setNumber(n);
// do record here!
}
//:~
但是新的问题是,我们没有任何硬性的方法来保证FooView调用FooControl(Imp.2),而不是直接调用FooModel的方法(Imp.1)。
事实上Imp.1方法的诱惑力是很大的,一不小心就会实现成那样。一方面FooView必须调用FooModel的get方法来获得数据,而另一方面又必需调用FooControl的set方法来
修改数据。除非你非常明白你在干什么否则很容易就写出类似Imp.1的方法。
还有一种方法是FooView并不含有FooModel,FooView的所以操作都通过FooControl来完成,无论是get还是set,我们称之为Imp.3。
在 Agile Web Development with Rails 上有着这样一句话:“保持关注点的清晰分离原本就是开发者的职责,不应该强求工具来确保这种分离”(Page. 19),说实话
当我看到这句话时我真的不知道下面的代码是否还有意义。
下面的代码是我在开发一个图像处理程序时设计的结构,通过接口强制限制了View对Model的set操作。
class IFooModel {
public:
virtual int getNumber() = 0;
};
class IFooModelObserver {
public:
virtual void numberChanged(IFooModel *model) = 0;
};
class FooModel : public IFooModel {
public:
virtual int getNumber() {return m_number;}
void setNumber(int number) {m_number = number; m_observer->numberChanged(this);}
IFooModelObserver *getObserver();
void setObserver(IFooModelObserver *observer);
};
class FooView {
private:
IFooModel *m_model; // Important!
FooControl *m_control;
};
class FooControl {
private:
...
};
即利用继承将Model的get接口和set接口分离出来。
对于从FooView中修改FooModel的操作我们引入一个新的FooActionSetNumber:
class FooActionSetNumber : public FooAction {
public:
FooActionSetNumber(IFooModel *model, int number) : m_model(model), m_number(number) {}
virtual void execute() {
dynamic_cast<FooModel *>(m_model)->setNumber(m_number);
}
}
在FooView中我们可以用IFooModel构造这个Action,然后将其交给FooControl执行。
class FooControl {
public:
void execute(FooAction *action) {
action->execute();
// record action here!
}
};