游戏前端开发的必要设计--观察者模式的应用
我们都知道观察者模式吧,它非常有用,是游戏开发中的必备设计。
游戏开发设计,尤其是在前端开发中观察者模式是一个非常有用的设计。在这里我所讲的观察者模式不同于教科书上。
这里我把观察者和发布者结合,两者并存于一体。这样的一个设计使得他可以完整的模拟现实中的对象和对象之间的所有交互。
下面是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 }
以下控制台输出了正确的结果:
对于说话者来说他们说话的内容作为主题被发送出来,而此时的听众是观察者的角色,每一个听得懂这种主题语言的(类似于关注了这个主题的)角色都能收到对应的信息。
同时说话者和听众的身份是并存的。每个人都可以说也都可以听。
我们看到语言学叫兽每次都能听到别人说话,就是因为他关注了所有语言。
__EOF__

本文链接:https://www.cnblogs.com/monkeycoder/articles/4588412.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】FlashTable:表单开发界的极速跑车,让你的开发效率一路狂飙
【推荐】Flutter适配HarmonyOS 5知识地图,实战解析+高频避坑指南
【推荐】博客园的心动:当一群程序员决定开源共建一个真诚相亲平台
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 领域模型应用
· 记一次 ADL 导致的 C++ 代码编译错误
· MySQL查询执行顺序:一张图看懂SQL是如何工作的
· 为什么PostgreSQL不自动缓存执行计划?
· 于是转身独立开发者
· 从被喷“假开源”到登顶 GitHub 热榜,这个开源项目上演王者归来!
· Cursor 1.2重磅更新,这个痛点终于被解决了!
· C#/.NET/.NET Core优秀项目和框架2025年6月简报
· 上周热点回顾(6.30-7.6)
· Stack Overflow,轰然倒下!