12-设计模式-代理模式(设计模式学习笔记)

136 阅读6分钟

代理模式

代理模式为一个对象提供一个代用品或占位符,以便控制对它的访问。

代理模式的关键是,当客户不方便直接访问一个对象或者不满足需求时,提供一个替身对象来控制这个对象的访问,客户实际上访问的是替身对象,替身对象对请求做一些处理之后,把请求转发给本体对象。

原本的访问如下:

image.png

使用代理模式后访问如下:

image.png

保护代理和虚拟代理

保护代理和虚拟代理.png

代理模式案例

类似一个中介的作用 => 起到保护目标对象的作用(在执行具体功能之前,可以先进行其他操作)

例如:

茂茂今年18岁(年轻,脸白,活还好),但是苦于没有经济来源,这时候想到一个挣钱的好方法,兼职给客人洗脚,由于高超的手法,很讨富婆的喜欢,但是由于此时自身还是属于弱势群体,就需要一个代理人,此时精壮男子remi闪亮登场,来当这些弱势群体的代理人。

    // 本体对象	
    function DaGongRen(name){ // 打工洗脚的人
        this.name = name
    }
    DaGongRen.prototype = {
        sfh:function (){
            console.log(this.name + "手法好!洗过的富婆都说好!")
        },
        happyWithWomen:function (){
            console.log(this.name + "很会逗富婆开心!")
        },
        happyWithMan: function (){ // 独门绝技
            console.log(this.name + "很会与男人击剑!")
        }
    }

    // 激活本体对象
    var maomao = new DaGongRen("茂茂") // 打工人茂茂
    var yaoyao = new DaGongRen("二摇") // 打工人二摇
	
    // 代理人,代理人可以同时代理很多不同的本体对象,比如代理上面的茂茂和二摇
    function ProxyRemi(user){ // 代理人remi
        this.user = user
    }

    ProxyRemi.prototype = { // 代理人可以选择性拥有本体对象的功能,例如代理人remi知道他俩都有独门绝技击剑,但是没有选择帮忙代理,只是代理了其中两个功能
        sfh:function (){
            // 代理人还能进行一些功能的处理,例如需要先收小费,才会找打工人来洗脚
            console.log("支付小费300块") // 支付小费
            this.user.sfh() // 开始洗脚
        },
        happyWithWomen:function (){
            this.user.happyWithWomen()
        }
    }

	// 此时有一个叫北巷麋鹿(bxml)的富婆要来洗脚,指明需要活最好的来帮她洗,但是她并不知道这里谁的活最好,就需要通过找代理人remi了
    // 激活代理人,new一下代理人
    var bxml = new ProxyRemi(maomao) // 此时将头牌茂茂传入代理
    console.log(bxml.sfh()) // 此时北巷麋鹿就可以享受"手法好"的服务了

    var yy = new ProxyRemi(yaoyao)
    console.log(yy.happyWithWomen())

代理缓存

记忆函数:用空间换时间,用一些数据结构保存计算的结果,有类似的计算,直接取已有的数据

    // 代理缓存

    // 主体
    function add(a,b){
        // 大量复杂的计算,需要大量的时间
        return a + b
    }

    // 代理
    function memorize(fn){ // 接收一个函数
        
        var cache = {} // 数据结构容器,保存计算结果
        
        return function (){ // 代理中返回的函数,可以设置接收的形参个数,也可以直接用arguments
            // 创建一个独一无二的标志key,表示已经计算过的凭证
            var key = Array.prototype.join.call(arguments,",")// 因为arguments没有join方法,call对象冒充,使得arguments对象拥有Array对象的join方法

            if(key in cache){ // 如果已经计算过了,有数据
                console.log("缓存")
                return cache[key] // 数据结构的示例{"1,1":2,"1,2":3} key就是对象中对应的属性名
            }else{ // 否则就是首次计算
                console.log("计算")
                return cache[key] = fn.apply(this,arguments) // 将计算的结果保存到缓存中
            }
        }
    }

    var memorizeAdd = memorize(add) // 调用代理,将主题函数传入代理,最后返回一个函数
    
    // 下面都是操作代理
    console.log(memorizeAdd(1,1)) // 计算得出2
    console.log(memorizeAdd(1,1)) // 取缓存的2
    console.log(memorizeAdd(1,2)) // 计算得出3

代理模式在vue3中的应用Proxy

以下是html代码:

<!--html代码-->
<div id = "app">
    <h3 id = "paragraph"></h3>
    <input type = "text" id = "input">
</div>

以下是js代码:

    /* 先了解一下proxy的基本使用
    // let proxy = new Proxy(代理的对象,拦截的操作)
    
    let obj = {a:1} // 定义一个对象obj

	// 生成proxy实例对象,并传入刚刚的obj对象,获取拦截操作
    let proxy = new Proxy(obj,{
        // 获取值拦截
        get:function (target,name){ // 对象,属性名
            console.log("拦截之后的获取值")
            return target[name]
        },
        set:function (target,name,value){ // 对象,属性名,设置新的值
            console.log("开始设置值")
            target[name] = value
        }
    })

    console.log(proxy.a) // 拦截值,会打印1
    proxy.a = 2 // 将拦截的值重新设置
    console.log(proxy.a) // 重新获取修改后的值,打印2

     */

    // 正片开始
    // 第一步,获取对象
    let paragraph = document.getElementById("paragraph")
    let input = document.getElementById("input")

    // 第二步,准备好数据
    let data = {
        text:"今晚吃鸡!"
    }

    // 第三步,代理处理,将data对象传入并拦截处理
    let proxy = new Proxy(data,{
        // 获取值拦截
        get:function (target,name){ // 对象,属性名
            console.log("拦截之后的获取值")
            return target[name]
        },

        // 设置值
        set:function (target,name,value){ // 对象,属性名,设置新的值
            console.log("开始设置值")
            paragraph.innerHTML = value // 设置的时候给界面渲染
            input.value = value  
            target[name] = value // 给对象上对应的属性重新赋值
        }
    })

    // 触发set代码给界面渲染
    proxy.text = data.text

    // 文本框输入可以实时给模型赋值
    input.addEventListener("input",function (e){ // 回调函数接收事件对象e
        console.log(e,e.target)
        proxy.text = e.target.value // 事件对象在文本框发生,所以e.target就是文本框对象,文本框对象输入的值实时发送给proxy
    })

实现虚拟代理实现图片懒加载

在Web 开发中,图片预加载是一种常用的技术,如果直接给某个img 标签节点设置src 属性,由于图片过大或者网络不佳,图片的位置往往有段时间会是一片空白。 常见的做法是先用一张loading图片占位,然后用异步的方式加载图片,等图片加载好了再把它填充到img节点里,这种场景就很适合使用虚拟代理

var myImage = (function () {
    var imgNode = document.createElement('img');
    document.body.appendChild(imgNode);
    return {
        setSrc: function (src) {
            imgNode.src = src;
        }
    }
})();

var proxyImage = (function () {
    var img = new Image;
    img.onload = function () {
        myImage.setSrc(this.src);
    };
    return {
        setSrc: function (src) {
            myImage.setSrc('这里填写本地图片的加载路径');
            img.src = src;
        }
    }
})();

proxyImage.setSrc('这里填写请求的网络图片路径');

代理的意义

单一职责原则:

就一个类(通常也包括对象和函数等)而言,应该仅有一个引起它变化的原因。如果一个对象承担了多项职责,就意味着这个对象将变得巨大,引起它变化的原因可能会有多个。

面向对象设计鼓励将行为分布到细粒度的对象之中,如果一个对象承担的职责过多,等于把这些职责耦合到了一起,这种耦合会导致脆弱和低内聚的设计。当变化发生时,设计可能会遭到意外的破坏。

代理和本体接口的一致性

在客户看来,代理对象和本体是一致的, 代理接手请求的过程对于用户来说是透明的,用户并不清楚代理和本体的区别,这样做有两个好处:

1.用户可以放心地请求代理,他只关心是否能得到想要的结果。

2.在任何使用本体的地方都可以替换成使用代理。