在构建更复杂的程序时,你需要创建多个算法来协同工作,以实现一些高级功能。要合理地构建程序并让所有的类能互相通信,程序将会变得越来越复杂。因此在一个类中集中对程序进行控制,是非常有益的。这正是控制器设计模式背后的思想。
以图像算法中的设计模式(一):使用策略模式设计算法 这篇博客里使用的颜色检测算法为例。设计一个Controller类,它的首要任务是创建执行程序所需的类。在这里只执行颜色检测这一算法,所以只有一个类,但更复杂的程序就会有多个类。
设计Controller类结构如下:
class Controller
{
private:
ColorDetector cDetect;
cv::Mat image;
cv::Mat result;
public:
Controller();
void setTargetColor(uchar g, uchar b, uchar r);
void setMaxDist(int dist);
bool setInputImage(std::string filename);
void process();
cv::Mat getInputImage();
cv::Mat getResultImage();
~Controller();
};
Controller::Controller()
{
ColorDetector colorDetect;
cDetect = colorDetect ;
}
void Controller::setTargetColor(uchar g, uchar b, uchar r) {
cDetect.setTargetColor(g,b,r);
}
void Controller::setMaxDist(int dist) {
cDetect.setMaxDist(dist);
}
bool Controller::setInputImage(std::string filename) {
image = cv::imread(filename);
return !image.empty();
}
void Controller::process() {
result=cDetect.process(image);
}
cv::Mat Controller::getInputImage() {
return image;
}
cv::Mat Controller::getResultImage() {
return result;
}
Controller::~Controller()
{
}
可以看到,Controller类包含了所需的算法类,另外还有两个成员变量,作为输入和输出结果的引用。在Controller类中定义了设置和获取方法,通常它们只是简单地调用相关类中对应的方法。这个例子只用了一个算法类,但实际开发中通常会包含多个类。因此Controller的作用就是将请求重新定向到相关的类(在面向对象的编程中,这种机制成为委托)。
控制器模式简化了程序中类的接口,开发人员可以很容易地构建执行算法的程序接口。既不需要理解类与类是如何连接的,也不需要知道为了让所有类正确运行需要调用哪个类的哪个方法。所有的工作都由Controller类完成。开发者唯一需要做的,就是创建一个Controller类的实例。
Controller类的process方法运行算法。但是这个方法并不返回结果,想要得到最新的处理结果,需要调用另一个方法。
这里的Controller类也准备了应用开发者提供的数据,使用setInputImage方法会根据文件名,把图像装载进内存。
下面使用Visual Studio构建一个MFC对话框程序。首先将ColorDetector类和Controller类新创建进去。然后,只需要在对话框类中添加一个Controller类的成员变量,即可创建一个最基本的使用了控制器的对话框程序了。
创建两个按钮,一个用来选择图像,另一个用来启动处理,如下图所示:
两个按钮的消息处理函数如下:
void CMFCApplication1Dlg::OnBnClickedOpen()
{
// TODO: Add your control notification handler code here
CFileDialog dlg(TRUE, _T("*.bmp"), NULL, OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY, _T("image files (*.bmp;*.jpg)| *.bmp; *.jpg|All Files(*.*)|*.*||"), NULL);
dlg.m_ofn.lpstrTitle = _T("Open Image");
//若选中了一个文件名
if (dlg.DoModal() == IDOK) {
std::string fileName = CStringA(dlg.GetPathName());
if (controller.setInputImage(fileName)) {
cv::imshow("Input Image", controller.getInputImage());
}
}
}
void CMFCApplication1Dlg::OnBnClickedProcess()
{
// TODO: Add your control notification handler code here
controller.setMaxDist(100);
controller.setTargetColor(0, 150, 240);
if (!controller.getInputImage().empty()) {
controller.process();
cv::Mat result = controller.getResultImage();
cv::imshow("Output Result", result);
}
}
运行结果:
如果程序更加复杂,显然会有更多的对话框来让用户选择算法的参数。