1、模式标准
模式名称:代理模式
模式分类:结构型
模式意图:为其他对象提供一种代理以控制对这个对象的访问。
结构图:
适用于:
-
远程代理:也称为大使,这是最常见的类型,在分布式对象通信中被用于表示运行在不同地址空间的对象。
-
虚拟代理:根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的真实对象。
-
安全代理:用来控制真实对象访问时的权限。
-
智能指引:当调用真实对象时,代理处理另外一些事。
代理模式主要有两个角色:
-
代理(Proxy):保存一个引用使得代理可以访问实体。提供一个与主题的接口相同的接口,这样代理就可以用来替代实体。控制对实体的存取,并可能负责创建和删除它。
-
实体(Real Subject):代理所代表的真实对象,是真正的被调用的对象。
2、分析与设计
代理在日常开发中经常用到,比如通过香港服务器代理去看github上的项目。比如通过抓包工具作为代理,将app的请求过程进行拦截修改,然后在发送出去。代理虽然帮你干了你想干是事情,但是你却不知道它其实干了更多你不知道的。代理的玩法有很多,在游戏框架中我们如何使用代理模式,应该也有很多玩法,这里我只介绍其中一种。
在前端开发中vue的作者用在vue3中用proxy来取代vue2的Object.defineProperty实现响应式。游戏中也经常用到数据与视图的绑定,比如玩家的金币,在数据层player.money = 200时,我们希望页面上的所有显示金币数量的地方,都自动自动刷新视图显示为200,且如果数值不变时,你们你触发n多次player.money = 200,都不会触发刷新视图的事件。
3、开始打造
TypeScript
interface IObject {
operation()
}
class RealObject implements IObject {
operation() {
}
}
class ProxyObject implements IObject {
realObject: RealObject;
constructor(realObject: RealObject) {
this.realObject = realObject
}
public operation(): void {
//在调用目标对象之前,完成一些操作
console.log("Before Do Something");
this.realObject.operation();
//在调用目标对象之后,完成一些操作
console.log("After Do Something");
}
}
如果是代理对象,就不用自己写一个了,用内部基于 ES6 的 Proxy 实现就够用了
TypeScript
const vmHandler = {
set(obj, prop, value) {
// 代理拦截修改
obj[prop] = value;
return true
}
};
let loading_vm: ILoadingVM = {
progress: 0
}
let proxy_loading_vm = new Proxy(loading_vm, vmHandler)
我们的目的是给游戏框架整一个数据绑定的功能,所以这里用 ES6 的 Proxy就够了。
4、开始使用
这里用ES6 的 Proxy自带的那个代理,因为我们主要是对数据修改进行代理拦截
因为之前是考虑到多个游戏分离,所以给每个游戏整了一个IGameVM,目前简单点只给ui做了全局的vm
TypeScript
export class DemoVM implements IGameVM {
/**
*
* 唯一的方法出口
*/
getVMs() {
return {
// loadingVM: this._getLoadingVM(),
gateVM: this._getGateVM(),
// battleVM: this._getBattleVM(),
}
}
/**
* 动态显示数据
*/
private proxy_gate_vm: IGateVM_JCQ = null
private _gate_vm: IGateVM_JCQ = {
ps: 0,
gold: 0,
diamond: 0,
last_battle_id: 0 // 当前最高关卡
}
private _getGateVM() {
if (!xhgame.gui.getUI(xhgame.uiid.Gate_Index)) {
return this._gate_vm
}
if (this.proxy_gate_vm == null && xhgame.gui.getUI(xhgame.uiid.Gate_Index)) {
const handler = xhgame.gui.getUI(xhgame.uiid.Gate_Index).getComponent(GateView).vmHandler
// 设计模式12 代理模式
this.proxy_gate_vm = new Proxy(this._gate_vm, handler);
}
return this.proxy_gate_vm
}
}
TypeScript
export class GateView extends ViewComponent {
private ps_lable: Label
private gold_lable: Label
private diamond_lable: Label
private last_battle_lable: Label
protected onLoad(): void {
this.ps_lable = this.node.getChildByName('top').getChildByName('value').getComponent(Label)
this.gold_lable = this.node.getChildByName('top').getChildByName('gold').getChildByName('value').getComponent(Label)
this.diamond_lable = this.node.getChildByName('top').getChildByName('diamond').getChildByName('value').getComponent(Label)
this.last_battle_lable = this.node.getChildByName('bottom').getChildByName('curBattleIdText').getComponent(Label)
}
get vmHandler() {
const that = this
return {
set(obj: IGateVM_JCQ, prop, value) {
// 如果原先的值一样就不做视图层的修改了
if (prop == 'ps' && value != obj[prop]) {
that.ps_lable.string = value + '/3'
}
if (prop == 'gold') {
that.gold_lable.string = value
}
if (prop == 'diamond') {
that.diamond_lable.string = value
}
if (prop == 'last_battle_id') {
that.last_battle_lable.string = '当前第' + value + '关'
}
obj[prop] = value;
return true
}
};
}
}
TypeScript
export class xhgame {
.....
/** 当前游戏用到的视图绑定 */
static get vm() {
return gameInstance.game.getVM().getVMs()
}
....
}
使用时只需要
TypeScript
xhgame.vm.gateVM.gold = 200