C#设计模式(一)订阅-发布模式

本来是在持续开发Besige,但考虑到以后迟早会在设计模式这块继续拓展,就新开了一个标题,算是个开个头。

模式这东西原本可以独立于语言之外来说,但考虑到不同语言的实现毕竟有些差异,我本人搜索文章时,也不喜欢搜出一堆其他语言的参考资料来,为了少制造些垃圾信息积点德,就在标题前加了个C#。


订阅-发布模式又称观察者模式,很多资料只是介绍了个大致思想,很难找到把订阅者和发布者最大化解耦的参考代码。

我这里贴个在我比较欣赏的一个设计上作了些许修改的版本:

using UnityEngine;
using System;
using System.Collections.Generic;

public class SubPubSystem 
{
    public Dictionary<string, Delegate> records = new Dictionary<string, Delegate>();

    public void Subscribe(string name, Action method) { _Subscribe(name, method); }
    public void Subscribe<T0>(string name, Action<T0> method) { _Subscribe(name, method); }
    public void Subscribe<T0, T1>(string name, Action<T0, T1> method) { _Subscribe(name, method); }
    public void Subscribe<T0, T1, T2>(string name, Action<T0, T1, T2> method) { _Subscribe(name, method); }
    public void Subscribe<T0, T1, T2, T3>(string name, Action<T0, T1, T2, T3> method) { _Subscribe(name, method); }

    public void UnSubscribe(string name, Action method) { _UnSubscribe(name, method); }
    public void UnSubscribe<T0>(string name, Action<T0> method) { _UnSubscribe(name, method); }
    public void UnSubscribe<T0, T1>(string name, Action<T0, T1> method) { _UnSubscribe(name, method); }
    public void UnSubscribe<T0, T1, T2>(string name, Action<T0, T1, T2> method) { _UnSubscribe(name, method); }
    public void UnSubscribe<T0, T1, T2, T3>(string name, Action<T0, T1, T2, T3> method) { _UnSubscribe(name, method); }

    public void _Subscribe(string name, Delegate method)
    {
        Delegate d;
        if (records.TryGetValue(name, out d))
        {
            d = Delegate.Combine(d, method);
            records[name] = d;
        }
        else
        {
            records.Add(name, method);
        }
    }

    public void _UnSubscribe(string name, Delegate method)
    {
        Delegate d;
        if (records.TryGetValue(name, out d))
        {
            d = Delegate.Remove(d, method);
            records[name] = d; 
        }
    }

    public void Publish(string name, params object[] args)
    {
        try
        {
            Delegate d;
            if (records.TryGetValue(name, out d))
            {
                if (d != null)
                {
                    d.DynamicInvoke(args);
                }             
            }
            else
            {
                records.Remove(name);
            }
        }
        catch(Exception ex)
        {
            Debug.LogError(ex.Message); 
        }
    }
}

使用也很简单,在某个大系统模块里new一个实例,在需要的地方调用其Subscribe、Publish方法就是了,假如游戏规模小,甚至可以直接改成静态类来用。

需要注意的点:

1.订阅者方法若意外丢失了,比如Unity里绑在某个物体上的脚本随着物体的销毁也不见了,那发布消息调用时会引发异常。关于如何更好的探知调用的方法是否还存在的问题,几个群问了下,没得到很好的答复,最好还是手工在OnDestroy里UnSubscribe罢。若谁知道更好的方案,请赐教。

2.为简单起见这里最多只支持四个参数,若需要更多参数,多复制粘贴几行就是了。


我个人还是很喜欢这个系统的简单、灵活的。像.net framwork给出的EventHandler规范那种,还要自己继承EventArgs写许多消息类,感觉略微有些麻烦。

暂时就在Besige中用这套系统来处理消息事件了,以后遇到问题再完善。


最后说下下阶段的计划:

上文说到Besige还是留下大把的功能模块需要做,但我现在急切的想剑指另一块垂涎已久的地盘——脚本系统。

暂时把那些轻车熟路只是需要时间完成的工作放一放,先进入下一个相对陌生更有挑战性的领域。

公司项目用的脚本系统是自定义的一套DSL,虚拟机也是自己写的,感觉这样完全没必要,其技能脚本甚至弱到连变量都没法用。借用通用的脚本语言和第三方库明显更实惠些,功能也更强大。

这样具体来说无非就是Lua或Python了,Unity里相对用Lua的明显多一些,所以目标就锁定在ulua_tolua上了,tolua的开发者同时提供了一个LuaFramework_NGUI/UGUI的框架,用来学习其架构也是不错的。

我希望在消化其思想后,将ulua虚拟机剥离出来,最小化的放进自己的项目,在此基础上作一些脚本编程的实践,至少要做到用脚本定义UI,然后视情况看能不能做点别的比如AI之类,甚至整个逻辑都拿lua来实现。

最后,搞定了这块,很多小公司的面试者特别喜欢问的热更新,自然也就搞定了。

最近工作也忙,整个周期可能会比较长,定在两个月左右吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值