接下来几篇文章打算说一下编程的一些基础思路问题。先从基本的事件机制说起,然后说一下面向对象的基础,最后说一下简单的MVC框架的搭建。希望通过这些内容,让新入门的人对编写程序有一个比较系统的思路,而不需要为了实现某个功能而烦恼怎样堆砌代码。
事件机制的说明其实之前也写过文章介绍,这次算是写得更通俗一点吧。
一、 方法直接调用和事件机制调用
在写程序的时候,我们经常会想调用某个方法,这种方法的调用很多时候会是在同一个类或者跨了另外的类去调用。我们举一个游戏里面角色血量变化的例子:
假如有一个类叫RoleData,里面是存储角色的血量(hp)多少的,然后有一个UI的类叫做BloodBar,是用于管理显示血量条的。我们有这么一个需求,当血量小于10时,血量条需要变成红色。
一般来说,我们可以直接写方法调用,比如中BloodBar里面写一个叫做TurnRed()的方法,然后中RoleData里面写逻辑:
{
BloodBar.TurnRed();
}
这样就简单的完成了变红色的方法的调用了。
下面换种方式来做,我们写一个管理器叫做EventMgr,里面有2个公共方法,一个叫做AddListen,另外一个叫做SendEvent。
我们先做BloodBar类里面写
EventMgr.AddListen (“HPLessThan10”,TurnRed);意思是收到HPLessThan10这个名称的事件时,执行TurnRed方法。
然后在RoleData类里面写:
{
EventMgr.SendEvent(“HPLessThan10”);
}
这样,当hp小于10时,会抛出一个以“HPLessThan10”作为key的事件,EventMgr会进行转发,由于BloodBar类已经监听了该事件,所以BloodBar类就收到事件,并且执行了TrunRed的方法。
二、 为什么需要事件机制
看完上面的过程之后,是不是会有这样的疑问,原来简简单单的就能调用到某个方法来,现在需要做这么多奇怪的操作之后才调用到,是不是有点傻?
我们再举复杂一点到例子。
我们还是以一个游戏角色血量为例子。假如现在血量小于10不只需要血量条变红,加入了新的需求,还需要角色模型变红,还要天空变暗。
假如我们新建一个RoleModel类作为角色模型显示的控制,新建一个SkyCtrl类作为天空的控制,那么直接调用的方法会变成这样:
在RoleModel类里面加入TurnRed方法,
在SkyCtrl类里面加入方法TurnDark。
然后在RoleData类里面加入代码:
{
BloodBar.TurnRed();
RoleModel.TurnRed();
SkyCtrl.TurnDark();
}
可以看出,每次需要新增需求时,除了需要新增对应实现该功能的类,还需要回头去修改作为方法调用者的RoleData类。也就是说,现在RoleData类和BloodBar类 RoleModel类还有SkyCtrl类已经息息相关,一个类的代码会出现在另外一个类里面,而修改一个类的方法,可能会需要涉及到调用的地方也必须同时修改。这种情况在程序编写里面叫做耦合。很明显的,如果以后再需要修改hp小于10时的表现或者处理, RoleData还是需要继续的作出修改,或者表现的方法传入的参数结构变化了,作为调用者的RoleData也需要配合修改。这时候RoleData可能觉得很冤枉了,它明明只是管理了个血量,告诉大家现在血量小于10而已,为什么其他表现的修改,还必须它去参与一起修改呢?
接下来看看用事件机制来实现:
RoleData里面的内容不变,还是
{
EventMgr.SendEvent(“HPLessThan10”);
}
然后在新增的RoleModel类里面加代码
EventMgr. AddListen (“HPLessThan10”,TurnRed);
在SkyCtrl里面加代码
EventMgr. AddListen (“HPLessThan10”,TurnDark);
这样就实现完成了。
以后就算再加新需求,也只需要在新增的类里面加入监听,然后实现,完全不需要去修改旧的代码。
如果从代码的角度看这个问题有点难以理解,我们可以举个现实一点的例子,比如我现在需要组织一个学校内的免费演唱会,需要告诉大家去观看。然后后来演唱会改时间了,我需要再通知大家。
如果是普通的事件调用,假设学校里面有100个人,我需要逐个去通知他们(调用方法)。我需要找到李雷,告诉他可以去参加,然后找到韩梅梅,告诉她可以去参加,100个人我就需要叫100次。后面改时间了,我再通知100次,如果中途多出了一个人,变成101个,我就要随时更新我的电话通讯录,然后去通知新增加的人。结果演唱会还没开始,我累死了。
改为事件机制实现,就是我在学校的公告栏上面写了一个信息,比如今晚十点在足球场有免费演唱会欢迎参加。但实际上我是不知道具体谁会看到这条信息,也许李雷感兴趣,他会去留意看,也许韩梅梅没有兴趣,她就不会去留意看。当时间改了,我同样在公告栏上写,演唱会改为明天。这时候,李雷知道了,韩梅梅本来就不关心演唱会,她不需要知道。
这样发布消息的人很轻松愉快,该知道消息的人也得到了信息。而两者之间也不需要存在任何直接的联系,我甚至可以不认识李雷或者韩梅梅。
实际上这个过程在程序里面就叫做解耦,类与类之间不再有耦合的关系,互相的功能不会有直接的影响。这里是得益于事件机制的运用。
事件机制其实是编程里面一种叫做观察者模式的设计模式的基本构成。事件的本质是一种方法的委托(Delegate),把回调方法委托到事件管理器,当条件达到时,通过事件key来告诉事件管理器可以执行那些已经委托过的方法。
三、 事件机制的简单实现
以伪代码的形式说明一下实现事件的写法:
//事件消息管理器类
class EventMgr
{
//注册监听事件的方法,由关心事件的地方去监听,使用事件类型作为Key,然后传入回调方法
static public int AddListen(string key,MsgCallback fun)
{
//把对应监听的Key和回调委托fun存到字典里面
//这里需要产生对应这次监听的一个唯一的id,用于反注册事件监听。
return 唯一的id
}
//抛出同步事件的方法
static public void SendEvent (string key,params object[] args)
{
//从字典里查找有没有人监听过对应key的事件,如果有就把参数传进回调方法执行
//这里的参数用了不定长参数,类型是object,是因为每个事件需要的参数是不一样的,类型也不确定
}
//抛出异步事件的方法
static public void SendEventAsyn(string key,params object[] args)
{
//把消息的key和参数存在一个可以固定顺序的队列里面,比如List之类。
//再在Update的时候检查队列,逐个事件抛出
}
//移除监听事件
static public void RemoveListen(string key,int id)
{
//当之前监听过事件的业务逻辑现在已经不需要再继续监听的时候,可以用之前监听时返回的id,作为反注册的用途
//根据事件类型的key和具体的id,从字典里面删除已经存在的事件
}
public void Update()
{
//具体怎样调起update就看个人需要了,比如Unity可以用自带的Update生命周期,或者用时间间隔来Tick之类。
//检查有没有需要抛出的异步事件,然后通过分帧条件来逐个抛出
}
}
//抛出事件的举例
class SendMsgDemo
{
public void SetHPData(HPData data)
{
this.hpData = data;
EventMgr.SendEventAsyn(EventType.HP_CHANGE,data);
}
}
//注册监听的举例
class AddEventDemo
{
private void AddListener()
{
listenId = EventMgr.AddListen(EventType.HP_CHANGE,OnHPChange);
}
private void RemoveListener()
{
EventMgr.RemoveListen(EventType.HP_CHANGE,listenId);
}
private void OnHPChange(object[] args)
{
HPData data = (HPData)args[0];
//当收到事件时需要做的处理
}
}
领取专属 10元无门槛券
私享最新 技术干货