team内有同事对设计模式的兴趣,勾起了我对软件代码设计的一些遐想,感觉有些话要说,要记录,所以就有了这系列文章。
接触到软件程序开发的人,肯定对程序=算法+数据不陌生。我记得这句话,但不记得说这话的人,当初,我更不理解这句话。现在,我好像有点明白了。
先从MVC模式说起。这个模式当然很复杂,涉及到的角色互相牵连。但是,这个模式,就是程序=算法+数据的一个体现。为什么这么说?
视图是表现数据的角色。如何表现数据,如何处理数据?涉及到算法。视图只负责算法,它不关心数据如何变化,是否变化,它只读取数据,它老老实实的把得到的数据按照既定规则既定算法进行处理。这里的处理,当然包括MVC模式中的视图在屏幕上的绘制动作,还应该包括MVC模式的V之外的其他方面。就是对数据的其他处理上,比如根据数据计算得到一个结果,比如根据数据计算结果,向手机发送消息,向结果接受角色发送结果等。
视图需要数据,数据从哪里来?从Model来。Model只应关心数据的变化,收集数据的变化。至于变化的数据如何处理,不是它的职责,Model仅仅收集数据。我想起了刚学C语言的时候用TC做的麻将游戏,得到数据后立即进行处理,导致最好程序结果混乱,后来我自己都读不懂了。
至于如何控制数据的变化?如何通知视图变化?Controller就是做这个事情。这里不多说了。
说了这么多,到底在说什么呢?一句话,就是一个角色专门负责收集数据,一个角色专门负责处理数据,责任分开。
举一个反面例子,就是我的麻将游戏。依稀记得流程是这样的(省略发牌过程,实在记不起来了):
游戏开始
环境设置
游戏数据结构初始化
绘制背景图
绘制各玩家麻将图案
while(不退出)
{
为“壮加”发牌 (此词语为网络过滤词语,请见谅无法打出)
将“壮加”面前的麻将图案清除(用原来的图案和背景图进行异或之类的操作吧)
绘制“壮加”面前的麻将图案
为下一家发牌
如果(下一家是玩家)
{
在出牌位置显示所发的牌
input = 键盘输入
//处理左右键的选牌操作
if ( input == Left )当前焦点牌--; 将上张牌位置恢复;将这张牌的图案清除;将这张牌y坐标-30;绘制这张牌;
if ( input == Right )当前焦点牌--; 将上张牌位置恢复;将这张牌的图案清除;将这张牌y坐标-30;绘制这张牌;
//处理出牌动作
if ( input == Enter ) ......
}
....
}
这样的代码,有错吗?当然没有错。还不是收集数据,处理数据么?比如键盘输入是一个数据,得到这数据后,立即进行绘制不就是数据+算法么?嗯,这样理解也对。但是这样的数据+算法,是在非常低的级别上看的。处理的仅仅是眼前这一个小数据而没有放到更广阔的大环境里处理。因为一个游戏,涉及到的数据众多,在每个数据变化的地方,立即处理对该数据的变化,那么将会陷入到各种复杂的情况中去。
为什么不先设计好数据结构,将所有的数据放在数据结构里来收集起来呢?类似的游戏,就是一个收集数据变化,计算变化,绘制变化的一个过程而已。
现在我会怎么设计这游戏呢?
环境设置
初始化数据结构
while(不退出)
{
if( 需要发牌) 发牌到某角色
input=用户输入
计算数据
根据数据绘制游戏图案
}
用户输入和发牌,只改变游戏数据结构内容。改变了就改变,输入的时候不要去立即表现它。等所有的状态变化收集完毕之后,再交给绘制部分模块去完成就可以了。
如何记录是否需要发牌?如何记录发牌数据?如何记录用户输入?背景绘制时需要的数据,各玩家麻将图案绘制所需数据统统从数据结构里来。绘制模块只需要从数据结构里得到数据,绘制图案。每次都一样的过程,先绘制背景,然后绘制各玩家,然后绘制某某某。
计算机速度很快的,不要像我起初那样小家子气,绘制变化的图案先擦除原图,然后绘制新图。只要状态有变化,就整个重新绘制一次,至于闪烁,速度不够,自然有其他技术予以解决,那就不是这里所讨论的问题了。
有了这个思想之后,很多事情就比较好理解了。MFC的文档视图结构就很清晰了。