游戏前端开发的必要设计--观察者模式的应用

我们都知道观察者模式吧,它非常有用,是游戏开发中的必备设计。 

游戏开发设计,尤其是在前端开发中观察者模式是一个非常有用的设计。在这里我所讲的观察者模式不同于教科书上。

这里我把观察者和发布者结合,两者并存于一体。这样的一个设计使得他可以完整的模拟现实中的对象和对象之间的所有交互。

下面是C#语言版本的实现。

 Actor.cs 这个类用于提供数据的发送和接收

复制代码
  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Text;
  5 /// <summary>
  6 /// 信号接收者处理各自逻辑的回调
  7 /// </summary>
  8 /// <typeparam name="T"></typeparam>
  9 /// <param name="signal"></param>
 10 public delegate void OnSignalHandler<T>(Signal<T> signal);
 11 /// <summary>
 12 /// 用于实现观察者和主题发布者
 13 /// instance.Add()添加感兴趣的主题并对应处理
 14 /// instance.Send()发送主题
 15 /// 解耦发送者和接受者之间的强引用关系
 16 /// 达到数据通信之间的弱关系
 17 /// 达到高维护性的目的
 18 /// </summary>
 19 public class Actor
 20 {
 21     /// <summary>
 22     /// 信号字典
 23     /// </summary>
 24     private Dictionary<Enum, Delegate> signalDict = new Dictionary<Enum, Delegate>(); 
 25     /// <summary>
 26     /// 管理器
 27     /// </summary>
 28     private ActorManager actorManager = ActorManager.GetInstance();
 29     /// <summary>
 30     /// 发送者
 31     /// </summary>
 32     private object target = null;
 33     /// <summary>
 34     /// 生命状态控制
 35     /// </summary>
 36     private byte lifeCondition = 1;//1:awake 0;sleep
 37     /// <summary>
 38     /// 缺省状态下 target==null
 39     /// </summary>
 40     public Actor()
 41     {
 42 
 43     }
 44     /// <summary>
 45     /// 可选 target 用于需要
 46     /// </summary>
 47     /// <param name="target"></param>
 48     public Actor(object target)
 49     {
 50         this.target = target;
 51     }
 52     /// <summary>
 53     /// 不可手动调用
 54     /// </summary>
 55     /// <typeparam name="T"></typeparam>
 56     /// <param name="type"></param>
 57     /// <param name="signal"></param>
 58     public void handler<T>(Enum type, Signal<T> signal)
 59     {
 60         if (lifeCondition == 0) return;
 61         if (signalDict.ContainsKey(type))
 62         {
 63             OnSignalHandler<T> caller = signalDict[type] as OnSignalHandler<T>;
 64             if (caller != null)
 65             {
 66                 caller(signal);
 67             }
 68         }
 69     }
 70     /// <summary>
 71     /// 添加关注
 72     /// </summary>
 73     /// <typeparam name="T"></typeparam>
 74     /// <param name="type"></param>
 75     /// <param name="caller"></param>
 76     public void Add<T>(Enum type, OnSignalHandler<T> caller)
 77     {
 78         this.signalDict.Add(type, caller);
 79         this.actorManager.Add(type, this);
 80     }
 81     /// <summary>
 82     /// 移除关注
 83     /// </summary>
 84     /// <param name="type"></param>
 85     public void Remove(Enum type)
 86     {
 87         this.signalDict.Remove(type);
 88         this.actorManager.Remove(type, this);
 89     }
 90 
 91     /// <summary>
 92     /// 移除全部
 93     /// </summary>
 94     public void RemoveAll()
 95     {        
 96         Dictionary<Enum, Delegate>.KeyCollection keys = this.signalDict.Keys;
 97         Enum[] tempArr = new Enum[keys.Count];
 98         keys.CopyTo(tempArr, 0);
 99         foreach (Enum key in tempArr)
100         {
101             Remove(key);
102         }            
103     }
104     /// <summary>
105     /// 发送信号
106     /// </summary>
107     /// <typeparam name="T"></typeparam>
108     /// <param name="type"></param>
109     /// <param name="obj"></param>
110     public void Send<T>(Enum type, T obj)
111     {
112         Signal<T> data = new Signal<T>(obj);     
113         data.type = type;
114         data.target = target == null ? this : target;
115         actorManager.Send<T>(type, data);
116     }
117     /// <summary>
118     /// 唤醒状态
119     /// </summary>
120     public void Awake()
121     {
122         lifeCondition = 1;
123     }
124     /// <summary>
125     /// 休眠状态
126     /// </summary>
127     public void Sleep()
128     {
129         lifeCondition = 0;
130     }
131     public void Destory()
132     {
133         this.RemoveAll();
134         this.actorManager = null;
135         this.signalDict = null;
136         this.target = null;
137     }
138 }
复制代码

 

 

 

 ActorManager.cs 这个类管理所有的Actor

复制代码
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 
 6 using System.Collections;
 7 
 8 
 9 public class ActorManager
10 {
11     private Dictionary<Enum, List<Actor>> signalDict;
12     private static ActorManager ins;
13     private ActorManager()
14     {        
15         signalDict = new Dictionary<Enum, List<Actor>>();
16     }
17     /// <summary>
18     /// 添加actor的信号类型
19     /// </summary>
20     /// <param name="type"></param>
21     /// <param name="observer"></param>
22     public void Add(Enum type, Actor observer)
23     {
24         List<Actor> obsers;
25         if (this.signalDict.ContainsKey(type))
26         {
27             obsers = this.signalDict[type] as List<Actor>;
28             if (!obsers.Contains(observer))
29                 obsers.Add(observer);
30         }
31         else
32         {
33             obsers = new List<Actor>();
34             this.signalDict.Add(type, obsers);
35             obsers.Add(observer);
36         }
37     }
38     /// <summary>
39     /// 移除指定的actor的信号类型
40     /// </summary>
41     /// <param name="type"></param>
42     /// <param name="observer"></param>
43     public void Remove(Enum type, Actor observer)
44     {
45         List<Actor> obsers;
46         if (this.signalDict.ContainsKey(type))
47         {
48             obsers = this.signalDict[type] as List<Actor>;
49             if(obsers.Contains(observer))
50                 obsers.Remove(observer);
51         }
52     }
53     /// <summary>
54     /// 单例 获得
55     /// </summary>
56     /// <returns></returns>
57     public static ActorManager GetInstance()
58     {
59         if (null == ins)
60             ins = new ActorManager();
61         return ins;
62     }
63     /// <summary>
64     /// 发送信号数据
65     /// </summary>
66     /// <param name="type"></param>
67     /// <param name="data"></param>
68     public void Send<T>(Enum type, Signal<T> data)
69     {
70         if (this.signalDict.ContainsKey(type))
71         {
72             List<Actor> obsers = this.signalDict[type] as List<Actor>;
73             int count = obsers.Count;
74             for (int i = 0; i < count; ++i)
75             {
76                 obsers[i].handler<T>(type, data);
77             }           
78         }
79     }   
80 }
复制代码

 

 

 

 Signal.cs 发送数据的封装类

复制代码
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 
 6 /// <summary>
 7 /// 信号数据 
 8 /// 发送者发出去的数据包装类
 9 /// </summary>
10   
11 public class Signal<T>
12 {
13     /// <summary>
14     /// 信号类型
15     /// </summary>
16     public Enum type;
17     /// <summary>
18     /// 数据
19     /// </summary>
20     public T data;
21     /// <summary>
22     /// 发送者
23     /// </summary>
24     public object target;
25 
26     public Signal(T obj)
27     {
28         this.data = obj;
29     }
30     
31     /// <summary>
32     /// 类型判断
33     /// </summary>
34     /// <param name="type"></param>
35     /// <returns></returns>
36     public bool IsEqualType(Enum type)
37     {
38         return this.type == type;
39     }
40     /// <summary>
41     /// 发送者判断
42     /// </summary>
43     /// <param name="target"></param>
44     /// <returns></returns>
45     public bool IsEqualTarget(object target)
46     {
47         return this.target == target;
48     }
49 }
复制代码

 

 

·上面的代码介绍完了。那么我们能用它来实现什么样的功能需求呢?

试想一下这么一个情景:在一个教室里有这么几个人,一个会汉语和英语的中国人,一个只会日语的日本人,一个只会讲英语的英国人,还有一个人是会他们三种语言的

语言学教授。他们在一起交流的过程中有一部分人能听懂对方而有一部分人听不懂。中国人说英语的时候会只有日本人不会有所反应,而日本人说日语的时候中国人和

英国人是听不懂的。。。。如果每个人都你一句我一句的说话,那么最后该有哪些人作出哪些正确的响应呢?

 

要解决这个问题。我们需要首先构造一个人的基类 。实现一些简单的功能,比如Talk。就是通过字符串和语言类型讲出自己的话。下面是Human类的实现:

Human.cs 这个类

复制代码
 1 using System;
 2 using System.Collections.Generic;
 3 
 4 /// <summary>
 5 /// 6 /// </summary>
 7 public class Human
 8 {
 9     /// <summary>
10     /// 语言类型
11     /// </summary>
12     public enum Language
13     {
14         Chinese,
15         English,
16         Japanese,
17     }
18     /// <summary>
19     /// 姓名
20     /// </summary>
21     protected string name;
22     /// <summary>
23     /// Actor 实例
24     /// </summary>
25     protected Actor actor = new Actor();
26     /// <summary>
27     /// 名字构造
28     /// </summary>
29     /// <param name="name"></param>
30     public Human(string name)
31     {
32         this.name = name;
33     }
34     /// <summary>
35     /// 讲话
36     /// </summary>
37     /// <param name="language"></param>
38     /// <param name="value"></param>
39     public virtual void Talk(Enum language, string value)
40     {
41         System.Console.WriteLine(name + "\t 说 \t" + value);
42         actor.Send<string>(language, value);
43     }  
44 }
复制代码

 中国人 像大多数天朝人民一样这个中国人也会一点英语

复制代码
 1 using System;
 2 using System.Collections.Generic;
 3 
 4 /// <summary>
 5 /// 中国人
 6 /// </summary>
 7 public class Chinese:Human
 8 {
 9     public Chinese(string name)
10         : base(name)
11     {
12         //会中文
13         actor.Add<string>(Language.Chinese, hearChineseHandler);
14         //会英语
15         actor.Add<string>(Language.English, heareEnglishHandler);
16     }
17 
18     private void hearChineseHandler(Signal<string> signal)
19     {
20         System.Console.WriteLine(name + "\t 听到了\t " + signal.data);
21     }
22 
23     private void heareEnglishHandler(Signal<string> signal)
24     {
25         System.Console.WriteLine(name + "\t 听到了\t " + signal.data);
26     }
27 }
复制代码


日本人

复制代码
 1 using System.Collections;
 2 /// <summary>
 3 /// 日本人
 4 /// </summary>
 5 public class Japanese : Human
 6 {
 7     public Japanese(string name)
 8         : base(name)
 9     {
10         //只会日语
11         actor.Add<string>(Language.Japanese, hearJapaneseHandler);
12     }
13 
14     private void hearJapaneseHandler(Signal<string> signal)
15     {
16         System.Console.WriteLine(name + "\t 听到了\t " + signal.data);
17     }
18 }
复制代码


英国人

复制代码
 1 using System.Collections;
 2 /// <summary>
 3 /// 英国人
 4 /// </summary>
 5 public class English : Human
 6 {
 7     public English(string name)
 8         : base(name)
 9     {
10         //只会英语
11         actor.Add<string>(Language.English,hearEnglistHandler);
12     }
13 
14     private void hearEnglistHandler(Signal<string> signal)
15     {
16         System.Console.WriteLine(name + "\t 听到了\t " + signal.data);
17     }
18 }
复制代码

 

语言学教授对于他来说会汉语日语英语简直太平常了

复制代码
 1 using System.Collections;
 2 /// <summary>
 3 /// 语言学家
 4 /// </summary>
 5 public class Philologer : Human
 6 {
 7     public Philologer(string name)
 8         : base(name)
 9     {
10         //会中文
11         actor.Add<string>(Language.Chinese, hearChineseHandler);
12         //会英语
13         actor.Add<string>(Language.English, heareEnglishHandler);
14         //会日语
15         actor.Add<string>(Language.Japanese, hearJapaneseHandler);
16     }
17 
18 
19     private void hearJapaneseHandler(Signal<string> signal)
20     {
21         System.Console.WriteLine(name + "\t 听到了\t " + signal.data);
22     }
23 
24     private void hearChineseHandler(Signal<string> signal)
25     {
26         System.Console.WriteLine(name + "\t 听到了\t " + signal.data);
27     }
28 
29     private void heareEnglishHandler(Signal<string> signal)
30     {
31         System.Console.WriteLine(name + "\t 听到了\t " + signal.data);
32     }
33 }
复制代码

 

 主程序

复制代码
 1 class Program
 2     {        
 3         static void Main(string[] args)
 4         {
 5            
 6             Chinese chi = new Chinese("小张");
 7             English eng = new English("查理");
 8             Japanese jap = new Japanese("坂本龙马");
 9             Philologer phi = new Philologer("语言学叫兽");
10 
11             //test
12             chi.Talk(Human.Language.Chinese, "你好");//小张 和 叫兽 能听到
13             System.Console.WriteLine("--------------------------------");
14             jap.Talk(Human.Language.Japanese, "斯国一");//坂本龙马 和 叫兽 能听到
15             System.Console.WriteLine("--------------------------------");
16             eng.Talk(Human.Language.English, "hello");//小张 查理 和 叫兽 能听到
17             System.Console.WriteLine("--------------------------------");
18             phi.Talk(Human.Language.English, "i am a professor");//小张 查理 和 叫兽 能听到
19             System.Console.ReadKey();
20         }
21     }
复制代码

 

 以下控制台输出了正确的结果:

 对于说话者来说他们说话的内容作为主题被发送出来,而此时的听众是观察者的角色,每一个听得懂这种主题语言的(类似于关注了这个主题的)角色都能收到对应的信息。

 同时说话者和听众的身份是并存的。每个人都可以说也都可以听。 

 我们看到语言学叫兽每次都能听到别人说话,就是因为他关注了所有语言。  

 

 

posted @ 2017-05-12 16:03  小黄瓜  阅读(523)  评论(1)    收藏  举报
编辑推荐:
· 复杂业务系统线上问题排查过程
· 通过抓包,深入揭秘MCP协议底层通信
· 记一次.NET MAUI项目中绑定Android库实现硬件控制的开发经历
· 糊涂啊!这个需求居然没想到用时间轮来解决
· 浅谈为什么我讨厌分布式事务
阅读排行:
· Coze Studio:字节跳动 Coze 的开源版本来了!第一时间深度解析
· 为大模型 MCP Code Interpreter 而生:C# Runner 开源发布
· 复杂业务系统线上问题排查过程
· 独立开发:高效集成大模型,看这篇就够了
· 在SqlSugar的开发框架的Vue3+ElementPlus前端中增加对报表模块的封装处理,实现常
点击右上角即可分享
微信分享提示