1、模式标准
模式名称:中介者模式
模式分类:行为型
模式意图:用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
结构图:
适用于:
1、一组对象以定义良好但是复杂的方式进行通信,产生的相互依赖关系结构混乱且难以
理解。
2、一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。
3、想定制一个分布在多个类中的行为,而又不想生成太多的子类。
2、分析与设计
游戏分单机和联机,单机模式下我们将命令直达操作对象。我让他动,他就得动。联机开发就不一样了,一般情况下我们的命令需要到服务器端走一下,和其他玩家的命令一起排个队再回来。这个时候你让他动,可能需要延迟个几十毫秒。在这里我们的通讯可能是这样的:
单机:本地指令发送--->本地指令执行
联机:本地指令发送--->上报服务器--->服务指令接受--->本地指令执行
一般情况下,单机开发和联机开发差别是比较大的。
这里我们思考能不能用中介者模式
单机:本地指令发送--->中介者发送(指令来自本地)--->本地指令接受执行
联机:
1、本地指令发送--->中介者发送(指令来自本地)--->服务端指令接受执行
2、服务指令发送--->中介者发送(指令来自服务)--->本地指令接受执行
这样一个单机游戏只需要修改中介者就变成了一个联机游戏
意图:用一个中介对象来封装一系列的(业务指令)对象交互。
3、开始打造
中介者接口
// 中介者接口
export interface IMediator {sendCommand(commandWorker: ICommandWorker, commandText: string): void;
}
联机游戏命令中介者
// 联机游戏命令中介者
export class NetworkMediator implements IMediator {private serverCommandWork: ICommandWorker = nullprivate localCommandWorks: ICommandWorker[] = []setServerCommandWork(serverCommandWork: ICommandWorker) {this.serverCommandWork = serverCommandWork}addLocalCommandWorks(localCommandWork: ICommandWorker) {this.localCommandWorks.push(localCommandWork)}sendCommand(commandWorker: ICommandWorker, commandText: string): void {if (commandWorker instanceof ServerCommandWorker) {console.log('来自ServerCommandWorker,则转到本地')this.localCommandWorks.forEach((_localCommandWork: ICommandWorker) => {_localCommandWork.receiveCommand(commandText)})} else {console.log('其他本地的全部转发到ServerCommandWorker')this.serverCommandWork.receiveCommand(commandText)}}
}
指令工作者接口
export interface ICommandWorker {sendCommand(commandText: string): voidreceiveCommand(commandText: string): void
}
服务指令工作者
// 服务指令工作者
export class ServerCommandWorker implements ICommandWorker {mediator: IMediatorconstructor(mediator: IMediator) {this.mediator = mediator}// 服务指令发送命令,通过中介者转发到本地指令sendCommand(commandText: string): void {this.mediator.sendCommand(this, commandText)}// 接受命令将它们发送到服务器端receiveCommand(commandText: string): void {// 发送到服务器console.log('发送到服务器,假设5秒后返回')setTimeout(() => {this.sendCommand(commandText)}, 5000)}
}
本地指令之一
// 单位 操作命令 另一个单位
export class UnitCommandUnitCommandWorker implements ICommandWorker {command: ICommand = nullfromUnitItem: UnitItem<any> = nulltoUnitItem: UnitItem<any> = nullmediator: IMediatorconstructor(mediator: IMediator) {this.mediator = mediator}send(fromUnitItem: UnitItem<any>, commandId: string, toUnitItem: UnitItem<any>) {let commandText = '[[' + fromUnitItem.unitId + ']]' + '{{' + commandId + '}}' + '[[' + toUnitItem.unitId + ']]'this.sendCommand(commandText)}sendCommand(commandText: string) {this.mediator.sendCommand(this, commandText)}receiveCommand(commandText: string) {// 构建抽象语法树const expressions: IExpression[] = [];const regex = /\[\[([^\]]+)\]\]|\{\{([^\}]+)\}\}|\[\{([^\}]+)\}\]|\[<([^\>]+)>\]/g;let match;while ((match = regex.exec(commandText)) !== null) {console.log(match)const token = match[0];const unitItemId = match[1]; // 捕获组1中的内容为单位项IDconst commandId = match[2]; // 捕获组2中的内容为命令IDif (unitItemId !== undefined) {expressions.push(new UnitItemExpression(unitItemId));} else if (commandId !== undefined) {expressions.push(new CommandExpression(commandId));}}console.log('expressions', expressions)// “遍历解析”表达式const commandSequence = new CommandSequenceExpression(expressions);commandSequence.interpret(this);}.......
}
4、开始使用
const commandText = "[[UnitItem.20]]{{attackgroup}}[[UnitItem.21]]";// 创建中介者和服务指令和本地指令
const mediator = new NetworkMediator();
const serverCommandWorker = new ServerCommandWorker(mediator);
const unitCommandUnitCommandWorker = new UnitCommandUnitCommandWorker(mediator);mediator.setServerCommandWork(serverCommandWorker)
mediator.addLocalCommandWorks(unitCommandUnitCommandWorker)// 将一个本地指令发送出去(自动被中介者转发到服务指令,服务指令执行推送到服务器端)
unitCommandUnitCommandWorker.sendCommand(commandText)
5、升级优化
功能已经完成了,现在整合优化一下。
因为指令中介者,服务指令及本地指令都属于command。所以我们新建一个CommandManger来统一管理
/** 指令管理者 */
export class CommandManager {/** 指令中介者 */mediator: NetworkMediator/** 服务指令 */serverCommandWorker: ServerCommandWorker/** 常用的本地指令 */unitCommandUnitCommandWorker: UnitCommandUnitCommandWorkerconstructor() {this.mediator = new NetworkMediator();this.serverCommandWorker = new ServerCommandWorker(this.mediator);this.unitCommandUnitCommandWorker = new UnitCommandUnitCommandWorker(this.mediator)this.mediator.setServerCommandWork(this.serverCommandWorker)}/** 添加其他扩充的本地指令 */addLocalCommandWorks(otherCommandWorker: ICommandWorker) {this.mediator.addLocalCommandWorks(otherCommandWorker)}
}
在全局单例管理中添加
export class SingletonInstance {// 设计模式5(单例模式)private static _instance: SingletonInstance = new this()static get instance(): SingletonInstance {return this._instance}static getInstance() {return this._instance}// game: TCSGame// game: JCQGamegame: DemoGame......// 指令管理commandManager: CommandManager = new CommandManager()
}
在外观模式中添加一个快速入口
export class xhgame {// 设计模式10(外观模式)/** 当前游戏 */static get game() {return gameInstance.game};........../** 指令管理 */static get command() {return gameInstance.commandManager}
}
现在,我们游戏中方便的使用我们的指令了。比如玩家点击释放技能按钮事件触发
onClickSkill(){const commandText = "((player1Hero)){{skill1}}";xhgame.command.unitCommandUnitCommandWorker.sendCommand(commandText)
}
当然也可能游戏就是单机游戏不需要老转换指令(毕竟指令还是有点损耗的)。这个时候我们只需修改方法(也适合联网游戏)
onClickSkill(){xhgame.command.unitCommandUnitCommandWorker.sendCommandByObject(this.unitItem, 'skill', this.targetUnitItem)
}
ok.
其他:
打算写好23种设计模式后,就将代码放到cocos store上。但前面的有些模式没有完全融合到框架内,可能还需要几天时间才能放出源码。