中介者模式
这天我和往常一样,正在电脑前苦苦思索框架设计,她又带着情绪过来了,原因就是因为她想喝奶茶的时候,奶茶涨价了,她就很生气.
虽然和她说过很多次,奶茶其实成本很便宜,最多糖多,因为人一吃糖就会很快乐,所以会让人上瘾.她说对呀,我没有吃到甜的,现在感觉很生气.
我不知道说些什么,我说,那这样吧,待会我把代码重构一下,我带你取个好地方,去那里喝奶茶.
她问:"是哪里啊?"
我说:"到了你就知道啦."
她有点不生气了,坐过来问我:"你还要多久啊"
我说:"快了快了,按照中介者模式来重构一下就ok".
"中介者模式?"她问.
"嗯,也是一个设计模式,对代码结构有着优雅的设计"我回答她.
她又问:"那是什么样的呀?"
她的好奇心又被勾起来了,几乎忘了自己要喝奶茶.
她又问:"你总说要优雅优雅,到底什么样子算优雅呀"
我想了想,也是,我也经常说要优雅的调用方法,优雅的设计,那么到底什么样的代码算是优雅呢?
过了一会儿,我说:"总结起来应该是这样,各个功能模块之间的耦合降低,封装方法良好,可扩展性好,这个应该就是一套优雅的代码了".
"听起来很简单呀,我还以为有很多个条条框框呢".她看着我说.
"哈哈,把这个简单的做好就是不简单."我看着她回答.
"这句话我初中的时候经常看."我边说边转过脸去.
我笑了笑,让出点位置,让她可以看到电脑屏幕,接着说:"游戏中会有许多个模块,这些模块之间会有通讯,比如会有战斗模块,成就模块,强化模块,声音模块,UI模块等等."
她的眼睛渐渐有了小星星,我知道,她的兴趣上来了.
我接着说:"那么这些模块之间会有相互调用的问题,不如,战斗的时候要调用声音模块,同样的,点击UI的时候一样要调用声音模块.而强化模块也会调用UI模块等等等等"
"这个你不是说过了吗?模块上层直接调用,与上层耦合性高,用的是外观模式呀,上层直接调用接口啊,不管底层."
我很惊讶的说:"你居然还记得,外观模式"
她说:"对呀,外观模式解耦的.我还记得你拿车举例子."
没想到她还记得.
我说,但是这个不对了哦.它们二者之间确实很像,但是还是有些不同的.你看,像是之前说的外观模式,它是一种结构模式,外观模式对与某个模块提供统一的接口,并且,它是单向的."
听到这里她皱了皱眉头,说:"我没太明白"
我看着她,一脸的困惑
我摸了摸她的头,说:"外观模式可以理解为对外简洁,你看它,用图表示是这样的"
打个比方,游戏开始的时候要初始化,那么用gameRoot中的init方法,初始化各个模块,那么它只需要调用外观模式的接口就可以了,这就是提供统一接口的意思.
"这个我知道啊,那个单向呢?"
"从图上看,只能高级接口调用下面的接口是吧,下面的接口是不能访问上层的"
"就这个意思?"
"是啊"
她看了看我,说:"原来这么简单."
我说对呀,本来编程就不难.
我接着说:"那么中介者模式,把它放在外观模式一起说,来做个对比的话,那么就是:外观模式单向的一对多的情况,负责的是大的模块化的调用,那么中介者模式就是每个模块与每个模块之间的调用.从图上面来说就是":
每个模块之间不直接通讯,而是通过中介脚本来传递消息,那么这样一样,每个模块之间的耦合就降低了.
"哦",她似乎懂了
我接着在代码中给她展示了一下,首先对她说,如果我们现在设计一个比较简单的中介者模式,就有一个UI,一个Game.还有一个充当中介者的GameManager.
UI_Base.cs
public abstract class UI_Base { protected GameManager gameManager = null; public UI_Base(GameManager _gameManager) { gameManager = _gameManager; } public abstract void RecvMsg(string msg); }
Game_Base.cs
public abstract class Game_Base { protected GameManager gameManager = null; public Game_Base(GameManager _gameManager) { gameManager = _gameManager; } public abstract void RecvMsg(string msg); }
GameManager.cs
public class GameManager : MonoBehaviour { }
"咦,为什么这个图标是个齿轮啊?"她说.
她看到了我建GameManager.cs的时候它的图标是个齿轮
我回答她说:"这算是它的一个bug吧,可能它内部也有个同名的脚本".
说实话,其实我也不知道-_-||
"你把这些都弄成抽象方法啦"她已经看到了,本来我还想给她说一下的,不过既然她已经看到了,就直接给她解释吧.
我说:"写成虚方法是因为好写,但是在具体的项目中,还是要写成一般的类,因为UI类和game类比较特殊,用的地方比较多.
"嗯嗯"
我说:"接下来就写一下它们的子类吧."
UILogic.cs
using UnityEngine; public class UILogic : UI_Base { public UILogic(GameManager gameManager) : base(gameManager) { } public void SendMsg() { gameManager.SendMsg(this, "按下攻击按钮了哦,英雄动起来哦"); } public override void RecvMsg(string msg) { Debug.Log("UILogic接收到信息:" + msg); } }
GameLogic.cs
using UnityEngine; public class GameLogic : Game_Base { public GameLogic(GameManager gameManager) : base(gameManager) { } public void SendMsg() { gameManager.SendMsg(this, "英雄重生了!界面恢复色彩吧"); } public override void RecvMsg(string msg) { Debug.Log("Gamelogic接收到信息:" + msg); } }
再写好gameManager
GameManager.cs
using UnityEngine; public class GameManager : MonoBehaviour { UILogic uiLogic = null; GameLogic gameLogic = null; public void InitUI(UILogic theuILogic) { uiLogic = theuILogic; } public void InitGame(GameLogic theGameLogic) { gameLogic = theGameLogic; } public void SendMsg(object obj, string msg) { if (obj == uiLogic) { gameLogic.RecvMsg(msg); } if (obj == gameLogic) { uiLogic.RecvMsg(msg); } } private void Start() { uiLogic = new UILogic(this); gameLogic = new GameLogic(this); InitUI(uiLogic); InitGame(gameLogic); uiLogic.SendMsg(); gameLogic.SendMsg(); } }
"这个不也还是调用嘛".她果然很聪明,一下就说出了本质.
对的,都是代码调用代码.
她接着又说:"这个sendMsg相当于一个封装,而UI部分的Send经过GameManager的中转最后调用到Game的Recv".
我说:"你这么聪明,要不要看看运行结果呀?"
她说:"好呀,那就看看吧"
"还有,"她又想到了啥.GameManager里面的几个实例是不是可以抽离出来,然后再给类继承,变成外观模式啊?"
我愕然,相对于我来说,她已经做的比我好多了.
"你等我一下,我去换衣服"
"换衣服干嘛?"
"出去喝奶茶"
说完,她回自己的房间了,留下我一脸的羡慕,羡慕我自己有个这么聪明的女朋友.