设计模式——代理模式

什么是代理模式

代理模式为其他对象提供了一种代理以控制对这个对象的访问,是一个使用率非常高的模式。

在我们的生活中,明星的经纪人,游戏的代练,其实都是代理模式的一种表现。简单来说,代理模式能帮被代理对象省掉不少麻烦,也可以为被代理对象提供保护,避免他人直接访问。

保护代理

保护代理用于控制不同权限的对象对目标对象的访问,但在 JavaScript 并不容易实现保护代理,因为我们无法判断谁访问了某个对象

虚拟代理

简单来说,就是把一些开销大的操作延迟到真正需要的时候才去执行。常见的虚拟代理有:

合并HTTP请求、图片预加载、惰性加载等。

缓存代理

缓存代理可以为一些开销大的运算结果提供暂时的存储,在下次运算时,如果传递进来的参数跟之前一致,则可以直接返回前面存储的运算结果

可以用于ajax异步请求数据,例如将分页的数据缓存下来。

动态代理

动态代理是在实现阶段不用关心代理谁, 而在运行阶段才指定代理哪一个对象。 相对来说, 自己写代理类的方式就是静态代理。

为什么需要代理模式

在上述定义的介绍中,其实就大体说明了为什么需要代理模式。

总结为如下几点:

  • 需要控制访问权限
  • 需要减少开销

如何实现代理模式

JavaScript

虚拟代理

图片预加载

// 被代理对象
let myImage = (function(){
    let imgNode = document.createElement( 'img' );
    document.body.appendChild( imgNode );
    return {
        setSrc: function( src ){
            imgNode.src = src;
        }
    }
})();

// 代理对象
let proxyImage = (function(){
    let img = new Image;
    img.onload = function(){
        // 当img具有src的时候才执行
        myImage.setSrc(this.src);
    };
    return {
        setSrc: function( src ){
            myImage.setSrc( './loading.gif' );
            // 触发onload事件,实现替换图片
            img.src = src;
        }
    }
})();
proxyImage.setSrc( 'https://qzonestyle.gtimg.cn/qzone/qzactStatics/imgs/20171122191630_ff8fef.jpg' );

合并HTTP请求

说白了就是设置一个定时器,过多少秒去把操作的数据以集合的形式发请求,而不是操作一次发一次。

代码模拟的是checkbox的选中。

var synchronousFile = function( id ){
	console.log( '开始同步文件, id 为: ' + id );
};
var proxySynchronousFile = (function(){
    var cache = [], // 保存一段时间内需要同步的 ID
    timer; // 定时器
    return function( id ){
        cache.push( id );
        if ( timer ){ // 保证不会覆盖已经启动的定时器
            return;
        }
        timer = setTimeout(function(){
            synchronousFile( cache.join( ',' ) ); // 2 秒后向本体发送需要同步的 ID 集合
            clearTimeout( timer ); // 清空定时器
            timer = null;
            cache.length = 0; // 清空 ID 集合
        }, 2000 );
    }
})();
var checkbox = document.getElementsByTagName( 'input' );
    for ( var i = 0, c; c = checkbox[ i++ ]; ){
      c.onclick = function(){
          if ( this.checked === true ){
              proxySynchronousFile( this.id );
          }
    }
};

缓存代理

有点动态规划的味道。核心是将之前的结果和参数存储起来,在执行前先进行查询,如果之前有匹配的目标值,则直接返回,否则执行函数。

代码模拟乘积

let mult = function(){
    let a = 1;
    for(let i = 0; i < arguments.length; i++){
        a = a * arguments[i];
    }
    return a;
}

let proxyMult = (function(){
    const cache = {};
    return function(){
        // 处理参数,为`,`拼接的字符串
        let args = Array.prototype.join.call( arguments, ',' );
        // 在缓存中查找,若有该key值,返回其value
        if( args in cache ){
            return cache[ args];
        }
        // 否则重新计算
        return cache[args] = mult.apply( this, arguments );
    }
})

动态代理

通过传入高阶函数这种更加灵活的方式,可以为各种计算方法创建缓存代理

/**************** 计算乘积 *****************/
var mult = function(){
    var a = 1;
    for ( var i = 0, l = arguments.length; i < l; i++ ){
        a = a * arguments[i];
    }
    return a;
};
/**************** 计算加和 *****************/
var plus = function(){
    var a = 0;
    for ( var i = 0, l = arguments.length; i < l; i++ ){
        a = a + arguments[i];
    }
    return a;
};
/**************** 创建缓存代理的工厂 *****************/
var createProxyFactory = function( fn ){
    var cache = {};
    return function(){
        var args = Array.prototype.join.call( arguments, ',' );
        if ( args in cache ){
            return cache[ args ];
        }
        return cache[ args ] = fn.apply( this, arguments );
    }
};
var proxyMult = createProxyFactory( mult ),
    proxyPlus = createProxyFactory( plus );
alert ( proxyMult( 1, 2, 3, 4 ) ); // 输出: 24
alert ( proxyMult( 1, 2, 3, 4 ) ); // 输出: 24
alert ( proxyPlus( 1, 2, 3, 4 ) ); // 输出: 10
alert ( proxyPlus( 1, 2, 3, 4 ) ); // 输出: 10

Java(非主要方向,就不展开了)

强制代理

所谓强制代理,就是要求我们通过真实的角色找到代理角色。否则不能访问。

就好比,你和一领导挺熟去找他,然后领导说:“不行啊,这得按规矩来,这样吧,你去找下小代,让他那边过了我这边再给你处理。”本来绕过了小代,最后又得去找小代。这就叫强制代理。

还以代练这个代理为例:

// 接口类
public interface IGamePlayer {
    //登录游戏
    public void login(String user,String password);
    //杀怪
    public void killBoss();
    //升级
    public void upgrade();
    //每个人都可以找一下自己的代理
    public IGamePlayer getProxy();
}

// ===========真实角色===========
public class GamePlayer implements IGamePlayer {
    private String name = "";
    // 代理是谁
    private IGamePlayer proxy = null;
    public GamePlayer(String _name){
        this.name = _name;
    }
    // 找到自己的代理
    public IGamePlayer getProxy(){
        this.proxy = new GamePlayerProxy(this);
        return this.proxy;
    }
    //打怪, 最期望的就是杀老怪
    public void killBoss() {
        if(this.isProxy()){
            System.out.println(this.name + "在打怪! ");
        }else{
            System.out.println("请使用指定的代理访问");
        }
    }
    //进游戏之前你肯定要登录吧, 这是一个必要条件
    public void login(String user, String password) {
        if(this.isProxy()){
            System.out.println("登录名为"+user+"的用户"+this.name+"登录成功!")
        }else{
            System.out.println("请使用指定的代理访问");
        }
    }

    //升级, 升级有很多方法, 花钱买是一种, 做任务也是一种
    public void upgrade() {
        if(this.isProxy()){
            System.out.println(this.name + " 又升了一级! ");
        }else{
            System.out.println("请使用指定的代理访问");
        }
    }
    //校验是否是代理访问
    private boolean isProxy(){
        if(this.proxy == null){
            return false;
        }else{
            return true;
        }
    }
}
    // ===========真实角色===========

// ===========代理角色===========
public class GamePlayerProxy implements IGamePlayer {
    private IGamePlayer gamePlayer = null;
    //构造函数传递用户名
    public GamePlayerProxy(IGamePlayer _gamePlayer){
        this.gamePlayer = _gamePlayer;
    }
    //代练杀怪
    public void killBoss() {
        this.gamePlayer.killBoss();
    }
    //代练登录
    public void login(String user, String password) {
        this.gamePlayer.login(user, password);
    }
    //代练升级
    public void upgrade() {
        this.gamePlayer.upgrade();
    }
    //代理的代理暂时还没有, 就是自己
    public IGamePlayer getProxy(){
        return this;
    }
}
// ===========代理角色===========

// ===========场景类============
public class Client {
    public static void main(String[] args) {
        //定义一个游戏的角色
        IGamePlayer player = new GamePlayer("张三");
        //获得指定的代理
        IGamePlayer proxy = player.getProxy();
        
        proxy.login("zhangSan", "password");
        //开始杀怪
        proxy.killBoss();
        //升级
        proxy.upgrade();
    }
}
// ===========场景类============
JavaScript

强制代理

ES6 Class实现

class GamePlayer{
    proxy = null

constructor(name) {
    this.name = name;
}

login (user,password) {
    if (this.isProxy()) {
        console.log("登录名为" + user + "的用户" + this.name + "登录成功!")
    } else {
        console.log("请使用指定的代理访问");
    }
}

upgrade () {
    if (this.isProxy()) {
        console.log(this.name + " 又升了一级! ");
    } else {
        console.log("请使用指定的代理访问");
    }
}

killBoss () {
    if (this.isProxy()) {
        console.log(this.name + "在打怪! ");
    } else {
        console.log("请使用指定的代理访问");
    }
}

getProxy () {
    this.proxy = new GamePlayerProxy(this);
    return this.proxy;
}

isProxy () {
    if (this.proxy == null) {
        return false;
    } else {
        return true;
    }
}

}

class GamePlayerProxy{
    gamePlayer = null;
constructor(gamePlayer) {
    this.gamePlayer = gamePlayer;
}
killBoss () {
    this.gamePlayer.killBoss();
}
login (user, password) {
    this.gamePlayer.login(user, password);
}
upgrade () {
    this.gamePlayer.upgrade();
}
getProxy () {
    return this;
}
}

let player = new GamePlayer("张三");
player.getProxy().login("zhangsan", "password");
player.getProxy().killBoss();
player.getProxy().upgrade();

js在使用class这种语法糖的时候,一些属性不太好私有化,感觉上还是用闭包会好点?

闭包实现

let gamePlayer = (function () {
 // ======私有属性、方法=======
 let name = '';
 let proxy = null;
 let isProxy = function () {
      if (proxy == null) {
            return false;
      } else {
            return true;
      }
 }
 // ======私有属性、方法=======
 return function (_name) {
      name = _name;
      this.login = function (user, password) {
            if (isProxy()) {
                console.log("登录名为" + user + "的用户" + name + "登录成功!")
            } else {
                console.log("请使用指定的代理访问");
            }
      }
      this.upgrade = function () {
            if (isProxy()) {
                console.log(name + " 又升了一级! ");
            } else {
                console.log("请使用指定的代理访问");
            }
      }
      this.killBoss = function () {
            if (isProxy()) {
                console.log(name + "在打怪! ");
            } else {
                console.log("请使用指定的代理访问");
            }
      }
      this.getProxy = function () {
            proxy = new GamePlayerProxy(this);
            return proxy;
      }
 }
})()

let GamePlayerProxy = (function () {
 // ======私有属性、方法=======
 let gamePlayer = null;
 // ======私有属性、方法=======
 return function (_gamePlayer) {
      gamePlayer = _gamePlayer;
      this.killBoss = function () {
            gamePlayer.killBoss();
      };
      this.login = function (user, password) {
            gamePlayer.login(user, password);
      };
      this.upgrade = function () {
            gamePlayer.upgrade();
      };
      this.getProxy = function () {
            return this;
      };
 }
})()

let player = new gamePlayer("张三");
// 直接访问
player.login("zhangsan", "password");
player.killBoss();
player.upgrade();

// 代理访问
player.getProxy().login("zhangsan", "password");
player.getProxy().killBoss();
player.getProxy().upgrade();

存在一个bug就是,getProxy()执行后,直接用player访问也行了。所以仅当做一个实现效果的参考即可。

动态代理

// ============动态代理类===========
public class GamePlayIH implements InvocationHandler {
    //被代理者
    Class cls =null;
    //被代理的实例
    Object obj = null;
    //我要代理谁
    public GamePlayIH(Object _obj){
        this.obj = _obj;
    }
    //调用被代理的方法
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable {
        Object result = method.invoke(this.obj, args);
        return result;
    }
}
// ============动态代理类===========

// ============场景类===========
public class Client {
    public static void main(String[] args) throws Throwable {
        //定义一个痴迷的玩家
        IGamePlayer player = new GamePlayer("张三");
        //定义一个handler
        InvocationHandler handler = new GamePlayIH(player);
        //开始打游戏, 记下时间戳
        System.out.println("开始时间是: 2009-8-25 10:45");
        //获得类的class loader
        ClassLoader cl = player.getClass().getClassLoader();
        //动态产生一个代理者
        IGamePlayer proxy = (IGamePlayer)Proxy.newProxyInstance(cl,GamePlayer.class.getInterfaces(),handler);
        //登录
        proxy.login("zhangSan", "password");//开始杀怪
        proxy.killBoss();
        //升级
        proxy.upgrade();
        //记录结束游戏时间
        System.out.println("结束时间是: 2009-8-26 03:40");
    }
}
// ============场景类===========

代理模式的优劣总结

优点

  • 职责清晰

  • 高扩展性

    基本上只需要更改代理者即可实现扩展。

  • 智能化

总结

代理模式包括许多小分类,在 JavaScript 开发中最常用的是虚拟代理和缓存代理。在java中实现代理模式的小类会更多一点。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值