面向对象的MVC模式

先来看一个传统的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!
    }
};
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值