What’s the proxy pattern?

代理模式其实就是将违反单一性原则的类给抽离出来,尽量满足开放和封闭的原则。 相当于一个类的行为只是一种,但是你可以给这个类添加额外的行为。比如: 一个工厂制造伞,你可以给这个工厂设置一个代理,提供订单,运货,淘宝网店等多种行为。当然,里面还有最关键的一点就是,这个代理能把一些骗纸和忽悠都过滤掉,将最真实最直接的订单给工厂,让工厂能够放心的让工人加班,顺利的发年终奖.

!!!说人话:
就是将你的本体请求,放在代理的怀抱里,让他来帮你挡风挡雨。

图片懒加载

图片懒加载是指,图片的加载应该在页面全部加载完后,再去加载。这样能够提高加载的速度和网页的体验性。实际操作就是,将原来网页的src独立出来,将原来图片使用一个loading.gif代替,然后在js里面手动创建个img去加载这个src,当加载完后把src替换就可以了。

没有使用代理模式时

var delayload = (function(){
    var img = document.querySelector("#img");
    img.src = "loading.gif";
    var newImg = document.createElement("img");
    newImg.onload = function(){
        img.src = newImg.src;
    }
    return function(src){
        newImg.src = src;
    }
})();
window.onload = ()=>{delayload("jimmy.jpg")};

这样写,我给满分(在没有产经这个生物的前提下).
现在,假设有一个初级产经(还没有进化那种)
她有一个需求: 我不要你的这个loading.gif图片,你换一个。
恩,好没问题。 (不过就是换一下src而已)

她又有一个需求: 你弄一个纯色的背景吧~.
MD~ 搞什么,你这样我很为难诶,我逻辑都变了,你让我怎么改。。

在没有使用代理模式的情况下,你的改动应该是:

var delayload = (function(){
    var img = document.querySelector("#img");
    img.style.backgoundColor="red";
    var newImg = document.createElement("img");
    newImg.onload = function(){
        img.src = newImg.src;
    }
    return function(src){
        newImg.src = src;
    }
})();

这就直接改动代码,破坏了开放封闭原则,添加功能时,下下策就是改动函数内部代码。

所以,为了应对产经这个神奇的物种,以及能写出一手好代码,推荐使用代理模式这个利器.

来,上利器

//将背景图设置,和图片加载的src修改分开
var delayload = (function(){
    var img = document.querySelector("#img");
    return {
        setSrc:function(src){
            img.src = src;
        }
    }
})();
var proxy = (function(){
    var img = document.createElement('img');
    delayload.setSrc("loading.gif");
    img.onload = function(){
        delayload.setSrc(img.src);
    }
    return {
        setSrc:function(src){
            img.src = src;
        }
    }
})();
proxy.setSrc("jimmy.jpg");

恩,如果产经要求换图片,好解决,换一下loading.gif就over了.如果产经要求换成纯色的。也行。 再加一个方法.

var delayload = (function(){
    var img = document.querySelector("#img");
    return {
        setSrc:function(src){
            img.src = src;
        },
        setBg:function(color){
            img.style.backgroundColor = color;
        }
    }
})();
var proxy = (function(){
    var img = document.createElement('img');
    delayload.setBg("red");  //只需要替换这一句
    img.onload = function(){
        delayload.setSrc(img.src);
    }
    return {
        setSrc:function(src){
            img.src = src;
        }
    }
})();
proxy.setSrc("jimmy.jpg");

使用代理就可以在,最大限度降低代码改动的情况下,完成需求。

当然,
如果有一天神奇生物又提出一个需求:
亲,我们现在宽带升级了,有100M了诶,你可以把加载去掉了吧,直接让用户一下就用看见图片了哦。
(呵呵~我丢你雷姆)

没事,谁叫我有代理模式嘞。改~
神奇的事发生了.

var proxy = (function(){
    var img = document.createElement('img');
    //删除~
    img.onload = function(){
        delayload.setSrc(img.src);
    }
    return {
        setSrc:function(src){
            img.src = src;
        }
    }
})();
proxy.setSrc("jimmy.jpg");

仔细观察就可以发现,我接口没改,对象没动,只是将delayload.setBg(“red”)删除了。

加载功能还在,只是我使用代理传了一层。66666

代理的一致性

通常来说,代理其实就是本体的一个影子,我有的行为你应该都有,我能sit,代理也能sit. 所以为了不让用于迷惑,这里有一个需求就是,代理的接口名应该和本体的接口名一致,就和上面的setSrc一样.

但通常这是个主要是针对java的静态语言的要求。因为在js里面没有实现接口的继承,而且在js里面,函数是名副其实的一等公民,所以这里对于代理的一直性要求就没有这么高了。

上面其实可以改写为:

var setImg = (function(){
     var img = document.querySelector("#img");
    return function(src){
        img.src = src;
    }
})();
var proxyImg = (function(){
    var newImg = document.createElement("img");
    setImg("loading.gif");
    newImg.onload = function(){
        setImg(newImg.src);
    }
    return function(src){  
        newImg.src = "jimmy.jpg";
    }
});
proxyImg("test.jpg");

但本人推荐上面那种使用接口的形式,这样拓展性比较强,而且易于复用.

保护代理和虚拟代理

这其实很好理解,保护代理就是起到保护作用,用来过滤掉一下不必要的请求,将真正需要的递给本体。

虚拟代理和函数节流的思想是一样的,将用户对性能的rape的伤害降低到最低。就像送快递一样,一件一件的送,这个公司是不是傻~ 所以为了生存,快递公司会当物品积攒到一定程度后才会让快递哥骑着小电驴穿梭在神州大地上。

保护代理

//譬如,验证用户名是否唯一
//这里我们应用,保护代理的思想,如果用户名是不合法的,则不会将该请求给本体执行
var checkUser = function(name){
    $.ajax({
        url:"xxxxx",
        type:"POST",
        contentType:"application/json",
        data:JSON.stringify({
            name:name  //用户名
        })
    })
}
var proxy = (function(){
    var user = document.querySelector("#username");
    return function(){
        var userName = user.value;
        var errMsg = detect(userName,["NotEmpty","isUserName"]);  //利用策略模式,验证.
        if(errMsg){
            console.log(errMsg);
            return;
        }
        checkUser(userName);
    }
})();

可以清楚的看到,如果你的用户名格式不正确,这个请求是不会达到本体的。直接会在proxy里面被拦截掉。所以一个很好的代理(保护代理),能让你的请求100%用在刀刃上。

虚拟代理
上面的保护代理阐述了怎样去拒绝请求,而虚拟代理的原则是收集请求(来者不拒). 他的出发点和保护代理的是一样的,都是为了节省请求的开支。

比如: 一个在线的编辑器,他是怎样同步你的内容呢?不会是,你内容一改变就发送一起请求同步吧。这个想法显然不切实际。如果这样,我每天没事都会打开这个编辑器,把asfdsafdsafsad…在里面敲上几分钟。保证分分钟弄死他的服务器。所以一般,我们会使用虚拟代理来接受你的请求。

var send = function(article) {
    return $.ajax({
        url: xxx,
        type: "POST",
        contentType: "text/plain",
        data: article
    })
}
var proxy = (function() {
    var content = document.querySelector('#article'),
        timer;
    return function() {
        var article = content.value;
        if (timer) { //不覆盖已经发送的请求
            return;
        }
        timer = setTimeout(function() {
            send(article)
                .then(function() {  //执行完成再处理
                    clearTimeout(timer);
                    timer = null;
                })

        }, 2000);
    }
})();
setTimeout(function(){
    proxy();
},2000); //定时发送请求

就算你手速再快,我就只能2s发一次。 但这只是一个比较简单的实例。如果大家感兴趣可以研究一下,作业部落这个编辑器,他这个markdown同步和体验交互式我迄今为止用过应该算最好的一个了吧~

缓存代理

这个应该是应用最多的一个代理方式了,这种方式能够真正解决运算时间问题,网络不畅问题,离线问题。

搬一个例子过来吧。
计算乘积问题

function fb(num){  //斐波那契函数,超级耗内存
    if(num<=1){
        return 1;
    }
    return num*fb(--num)
}
//缓存代理出场了
var cProxy = (function(){
    var cache = {};
    return function(num){
        if(cache[num]){
          console.log(`this is cache ${cache[num]}`);
            return cache[num];
        }
        return cache[num] = fb(num);
    }
})();
//测试
console.log(cProxy(4));  //24
cProxy(4);  //"this is cache 24"

恩,完美。 我们知道阶乘是比较累的一个计算方法,如果某天你的leader需要你计算很多次fb(2000)。用户的电脑也吃不消啊,所以为了体验,缓存代理是你百分百女友,好好珍惜。

当然,缓存代理并不只有这一点用途,比如需要重复获取网页中大部分数据的时候,就可以考虑使用缓存代理。前端工作者,99.9999%的应该都会遇见分页的问题(请不要告诉我你的分页是同步方式). 当我们点击一个页面的时候,获取后台的数据,然后再由我恩渲染到页面上,这是这样一个流程。 首先,渲染流程可以复用,那数据的复用性该怎么做呢?

恩,猜到了吧,就是使用cache进行一个缓存,然后如果下次获取分页的页码一致的话,就可以直接使用该数据了。

//向后台发请求,获取当前页面的数据
// http.getPage(page);
var pageProxy = (function(){
    var cache = {};
    return function(fn){   //fn作为处理页码数据的函数
        var pageData = cache[page];
        if(pageData){
            return fn(pageData);  //返回制定页码的数据
        }
        http.getPage(page)   //获取制定页码的数据
        .then((data)=>{
            cache[page] = data;  //存放数据
            fn(data);
        })
    }
})();

最后再说一句吧,由于代理写起来需要更多的逻辑和代码,如果你的产经没有什么需求的话,不用代理也是行得通的。还有就是,用不用代理和你原来的本体执行的业务逻辑是完全分开的,即,如果后期产经有什么需求,或者你对自己的代码不满意,重构的时候,再添加代理,这种方式也是很推荐的。

js设计模式系列之(一)请节约你的请求-代理模式的更多相关文章

  1. javascript设计模式与开发实践阅读笔记(6)——代理模式

    代理模式:是为一个对象提供一个代用品或占位符,以便控制对它的访问. 代理模式的关键是,当客户不方便直接访问一个对象或者不满足需要的时候,提供一个替身对象来控制对这个对象的访问,客户实际上访问的是替身对 ...

  2. [Python设计模式] 第7章 找人帮忙追美眉——代理模式

    github地址:https://github.com/cheesezh/python_design_patterns 题目1 Boy追求Girl,给Girl送鲜花,送巧克力,送洋娃娃. class ...

  3. Java 设计模式系列(十五)观察者模式(Observer)

    Java 设计模式系列(十五)观察者模式(Observer) Java 设计模式系列目录(https://www.cnblogs.com/binarylei/p/10198698.html) Java ...

  4. [js高手之路]设计模式系列课程-组合模式+寄生组合继承实战新闻列表

    所谓组合模式,就是把一堆结构分解出来,组成在一起,现实中很多这样的例子,如: 1.肯德基套餐就是一种组合模式, 比如鸡腿堡套餐,一般是是由一个鸡腿堡,一包薯条,一杯可乐等组成的 2.组装的台式机同理, ...

  5. [js高手之路] 设计模式系列课程 - jQuery的extend插件机制

    这里在之前的文章[js高手之路] 设计模式系列课程 - jQuery的链式调用与灵活的构造函数基础上增加一个extend浅拷贝,可以为对象方便的扩展属性和方法, jquery的插件扩展机制,大致就是这 ...

  6. ASP.NET MVC深入浅出系列(持续更新) ORM系列之Entity FrameWork详解(持续更新) 第十六节:语法总结(3)(C#6.0和C#7.0新语法) 第三节:深度剖析各类数据结构(Array、List、Queue、Stack)及线程安全问题和yeild关键字 各种通讯连接方式 设计模式篇 第十二节: 总结Quartz.Net几种部署模式(IIS、Exe、服务部署【借

    ASP.NET MVC深入浅出系列(持续更新)   一. ASP.NET体系 从事.Net开发以来,最先接触的Web开发框架是Asp.Net WebForm,该框架高度封装,为了隐藏Http的无状态模 ...

  7. JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(一)

    前言:出于某种原因,需要学习下Knockout.js,这个组件很早前听说过,但一直没尝试使用,这两天学习了下,觉得它真心不错,双向绑定的机制简直太爽了.今天打算结合bootstrapTable和Kno ...

  8. JS组件系列——BootstrapTable+KnockoutJS实现增删改查解决方案(三):两个Viewmodel搞定增删改查

    前言:之前博主分享过knockoutJS和BootstrapTable的一些基础用法,都是写基础应用,根本谈不上封装,仅仅是避免了html控件的取值和赋值,远远没有将MVVM的精妙展现出来.最近项目打 ...

  9. JS组件系列——Bootstrap组件福利篇:几款好用的组件推荐(二)

    前言:上篇 JS组件系列——Bootstrap组件福利篇:几款好用的组件推荐 分享了几个项目中比较常用的组件,引起了许多园友的关注.这篇还是继续,因为博主觉得还有几个非常简单.实用的组件,实在不愿自己 ...

随机推荐

  1. mybatis错误Invalid bound statement (not found) 的解决办法

    <!-- IDEA需要添加一下内容,否则无法找到mapper --> <build> <resources> <resource> <direct ...

  2. Xcode开启gcc/g++

    Apple announced Xcode 4.3 for OSX Lion and 4.4 for OSX Mountain Lion last week. The major difference ...

  3. (转)iOS如何取得APP的版本信息跟服务器对比进行升级提示?

    关键是自动取版本信息: [NSString stringWithFormat:@"Version %@",[[NSBundle mainBundle] objectForInfoD ...

  4. Struts2属性驱动与模型驱动

    为什么要使用属性驱动和模型驱动 struts2与struts很大的不同点在于,struts的execute方法提供了HttpServletRequest和HttpServletResponse方法在获 ...

  5. 二、UITableView和它的亲戚们

    . UITableView 参考: https://developer.apple.com/library/ios/#documentation/UIKit/Reference/UITableView ...

  6. Kinect for Windows V2和V1对照开发___彩色数据获取并用OpenCV2.4.10显示

    V1彩色分辨率:640x480 V2彩色分辨率:1920x1080 1,打开彩色图像帧的方式 对于V1: 使用NuiImageStreamOpen方法打开 hr = m_PNuiSensor-> ...

  7. 戴明PDCA方法

    戴明PDCA方法:即Plan(计划).Do(执行).Check(检查)和Action(处理)的缩写. P(plan)计划: 包括方针和目标的确定以及活动计划的制定; D(do)执行: 执行就是具体运作 ...

  8. qt 实现钟表图标

    #include "clock.h" CLOCK::CLOCK(QWidget *parent) : QWidget(parent) { QTimer *timer = new Q ...

  9. jquery之多重判断

    var appPath = getAppPath(); $(function(){ $('#addTeskDlg').window('close'); teskGrid(); }); function ...

  10. JAVA--好友界面面板

    package GongYou; //package windows.best_demo; import java.awt.*; import javax.swing.*; import java.u ...