29

设计模式之观察者模式(Observer)-腾讯游戏学院

 5 years ago
source link: http://gad.qq.com/article/detail/287970
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
还是《Game.Programming.Patterns》一书中对观察者模式的个人理解,惯例看一下菜鸟教程的描述:
5bd1beda6e336.png

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

实现:玩家在桥上失足掉入河中,达成成就~ (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);
        }
    }

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK