5评论

设计模式之观察者模式(Observer)

萧然 2018-10-25 1.6k浏览

想免费获取内部独家PPT资料库?观看行业大牛直播?点击加入腾讯游戏学院游戏开发行业精英群711501594

还是《Game.Programming.Patterns》一书中对观察者模式的个人理解,惯例看一下菜鸟教程的描述:

观察者模式应用的也比较多了,各大框架中的消息/通知/事件(叫法多样),很多就是用的观察者模式,或者在此基础上结合多种设计模式 ··· ···

实现:玩家在桥上失足掉入河中,达成成就~ (233333)
public enum ActorEvent
    {
        fellOffBridge,
    }
    //角色基类
    public class Actor
    {
    }
    //观察者基类(通知/消息/事件)
    public abstract class Observer
    {
        //接收通知
        public abstract void OnNotify(Actor actor, ActorEvent actorEvent);
    }
    //观察者链表节点
    public class ObserverNode
    {
        //下一节点
        public ObserverNode next;
        //节点对应的观察者
        public Observer observer { get; private set; }
        //构造函数
        public ObserverNode(Observer relatedOnserver)
        {
            observer = relatedOnserver;
            next = null;
        }
    }
    //被观察者基类
    public class Subject
    {
        ////改进前
        ////观察者列表
        //被观察者需要保存所有的观察者, 不论采用哪种数据结构, 增删观察者需要分配内存
        //List<Observer> observerList = new List<Observer>();
        ////初次改进
        ////改用链式结构, 由观察者自己记录, 观察者的多少无太大影响, 无需动态内存
        ////这样的方式也有缺点! 一个被观察者 可以有多个观察者, 但一个观察者 只能有一个被观察对象, 不能同时对应多个观察者, 类似于线性结构
        //protected Observer observer;
        //再次改进
        //记录观察者节点, 而不是记录和观察者, 不同的被观察者有不同的节点, 不同节点可以指向同一个观察者, 类似于网状结构
        protected ObserverNode head;
        public Subject()
        {
            head = null;
        }
        //添加观察者节点, 将其插入到链表的首位, 这样被观察者依次给观察者通知时, 会与添加顺序相反
        //如果将新加项添加到链表末位, 需要遍历整个链表直到最末位, 效率相对较慢
        //如果各个观察者之间不存在耦合, 通知的先后顺序不会发生任何影响
        public void AddObserver(ObserverNode node)
        {
            node.next = head;
            head = node;
        }
        //删除观察者节点, 删除操作需要遍历整个链表, 显然这种操作不够优雅, 可以尝试双向链表~
        public void DeleteObserver(ObserverNode node)
        {
            //如果要删除项在首位, head指向下一个, 删除首位next引用
            if (head == node)
            {
                head = node.next;
                node.next = null;
                return;
            }
            //如果要删除项不在首位, 遍历链表
            ObserverNode curNode = head;
            while (curNode != null)
            {
                //剔除当前项, 将上一个的引用指向下一个
                if (curNode.next == node)
                {
                    curNode.next = node.next;
                    node.next = null;
                    return;
                }
                curNode = curNode.next;
            }
        }
        //删除所有观察者节点
        ////除了循环删除引用, 还可以定义一条特定的Notify通知, 需要删除是通知观察者, 观察者自行注销
        public void DeleteAllObserver()
        {
            //遍历删除所有节点的next
            ObserverNode curNode = head;
            while (curNode.next != null)
            {
                head = curNode.next;
                curNode.next = null;
                curNode = head;
            }
            head = null;
        }
        //通知观察者
        protected void Notify(Actor actor, ActorEvent actorEvent)
        {
            //遍历所有节点, 调用节点的观察者接收函数
            ObserverNode curNode = head;
            while (curNode != null)
            {
                curNode.observer.OnNotify(actor, actorEvent);
                curNode = curNode.next;
            }
        }
    }
    //成就, 观察者
    public class Achievements
    {
        public void OnNotify(Actor actor, ActorEvent actorEvent)
        {
            if (IsHero(actor))
                UnLock(actorEvent);
        }
        void UnLock(ActorEvent actorEvent)
        {
        }
        bool IsHero(Actor actor)
        {
            return true;
        }
    }
    //物理系统, 被观察者
    public class Physics : Subject
    {
        Actor player = new Actor();
        void UpdateMovement()
        {
            if (true)
                Notify(player, ActorEvent.fellOffBridge);
        }
    }