对mvc模式的理解
Model-View-Controller
MVC模式是个威力强大的复合模式,是由数个设计模式结合起来的模式;
我们先看一下一个mp3播放器的设计,来由浅至深地了解这个设计模式的精髓所在:
从最直观的地方入手,我们不要先入为主的去使用mvc这个模式,而是尝试用自己最为直观的方式去设计这个应用,那么事情会变成这样:
首先从交互的角度,我们先提出需求:
- 用户界面上需要显示当前歌曲名
- 用户可以通过按键直接选择上一首,下一首;
- 用户可以通过menu按键,显示当前MP3中的所有歌曲列表,然后上下按键选择一首歌曲,并通过ok键播放
好的,其实上述需求,明确了一点,这些需求需要有视图,或者至少需要有View去告诉用户各种状态;ok,那有没有不需要view的需求呢?有,比如,按下风扇能转动,这种需求很简单就不需要view, 但是,这不是重点,重点在于,用户需要对机器当前的状态有个直观的感知,或者反馈,比如风扇上如果我们想要做高端,然后会加个面板,告诉用户当前的风速,室内温度等,最终还是免不了会有view的存在,view的形式可以是多样的,可以是网页的形式,可以是led的形式,可以是数码管的形式,这些只是展示方式的不同,真正的核心在于这些view展示的数据本身,这些才是用户真正care的地方;比如我的歌单里有没有某首歌(模型),比如我播放下一曲的时候,是否能正常切过去(控制器);简单总结就是:用户操作,会作用于控制器,控制器判断用户需要切歌,需要更改当前播放的歌曲索引,然后通知player 播放新的歌曲;模型的改变,会相应地引起视图的改变 ;
那么这里使用了哪些设计模式呢?
这里复习一下观察者模式和策略模式:
观察者模式的好处在于,模型对视图没有依赖,其含义是模型部分无需变更,便可以支持视图的替换,或多个视图去展示同一个模型;
纸上得来终觉浅,我们还是从代码的角度来看一下整个事情的细节吧:
class Mp3Model { public: int getSongIdxByString(string song); string getSoneNameByIdx(int idx); string getCurrentSong() void setCurrentSong(string song) { // hw hal to set song file ... // then notify observers notifyObserver(); // 通知观察者状态改变 } // 实际的start/stop void start(); void stop(); void registerObsever(Observer* observer) // model提供注册观察者方法 { mObserver = observer; } private: void notifyObserver() { mObserver->onNotify(); } Observer* mObserver; }; class Mp3Controller { public: Mp3Controller(Mp3Model* model) { mModel = model; } void playNext() { mModel->setCurrentSong(mModel->getSongIdxByString(mModel->getCurrentSong()) + 1); } void playPrevious() { mModel->setCurrentSong(mModel->getSongIdxByString(mModel->getCurrentSong()) - 1); } void start() { mModel->start();} void stop() { mModel->stop(); } private: Mp3Model* mModel; }; class Mp3View : public Observer { public: Mp3View() { mModel = new Mp3Model; mModel->registerObserver((Oberver*)this); mController = new Mp3Controller(mModel); } void on_click_next() { mController->playNext(); } void on_click_previous() { mController->playPrevious(); } void on_click_start() { mContrlloer->start(); } void on_click_stop() { mController->stop(); } void onNotify() { //实现model变化时的回调,如改变控件状态等 } void run(); bool live(); private: Mp3Controller* mController; Mp3Model* mModel; }; int main() { // 创建view需要controller, mode , 创建controller需要model Mp3View* mp3_view = new Mp3View; mp3_view.run(); while(mp3_view.live()) { sleep(1); } return 0; }