(八)代理模式
代理模式
- 代理模式
- 代理模式 介紹
- 概念
- 示例
- 代理模式 演示
- 代理模式 場景
- 網頁事件代理
- `jQuery $.proxy`
- ES6 Proxy
- 代理模式 總結
代理模式
- 介紹
- 演示
- 場景
- 總結
代理模式 介紹
- 使用者無權訪問目標對象
- 中間加代理,通過代理做授權和控制
概念
為其他對象提供一種代理以控制對這個對象的訪問。在直接訪問對象時帶來的問題,比如說:要訪問的對象在遠程的機器上。在面向對象系統中,有些對象由于某些原因(比如對象創建開銷很大,或者某些操作需要安全控制,或者需要進程外的訪問),直接訪問會給使用者或者系統結構帶來很多麻煩,我們可以在訪問此對象時加上一個對此對象的訪問層。
示例
- 科學上網,訪問github.com
- 明星經紀人,假設我們想邀請一位明星,那么并不是直接連接明星,而是聯系明星的經紀人,來達到同樣的目的
另外,想一下你通過鏈家的中介買房子,算不算代理模式?—— 不算,此時是三方關系,買家、中介、賣家,合同上都要簽字的。而上面的明星經紀人卻是兩方關系,你和經紀人聯系就好了,你不用跟明星直接聯系。
代理模式 演示
常見的 UML 類圖是
簡化之后
代碼描述
class ReadImg {constructor(fileName) {this.fileName = fileNamethis.loadFromDisk() // 初始化即從硬盤中加載}display() {console.log(`display...` + this.fileName)}loadFromDisk() {console.log(`loading... ` + this.fileName)} }class ProxyImg {constructor(fileName) {this.realImg = new ReadImg(fileName)}display() {this.realImg.display()} }// 測試代碼 let proxImg = new ProxyImg('1.png') proxImg.display()關鍵在于兩者都必須用display這一個 API 名字,就像你用不用科學上網,訪問youtube.com的網站都是一樣的。
代理模式 場景
不一定會嚴格按照 UML 類圖來實現,但是這種設計思想是能體現出來的。
網頁事件代理
將目標元素的點擊時間,代理到父元素上,以實現某種功能,很常用。
<div id="div1"><a href="#">a1</a><a href="#">a2</a><a href="#">a3</a><a href="#">a4</a> </div> <button>點擊增加一個 a 標簽</button><script>var div1 = document.getElementById('div1')div1.addEventListener('click', function (e) {var target = e.targetif (e.nodeName === 'A') {alert(target.innerHTML)}}) </script>jQuery $.proxy
$('#div1').click(function () {// this 符合期望$(this).addClass('red') }) $('#div1').click(function () {setTimeout(function () {// this 不符合期望$(this).addClass('red')}, 1000); });// 可以用如下方式解決 $('#div1').click(function () {var _this = thissetTimeout(function () {// _this 符合期望$(_this).addClass('red')}, 1000); });// 但推薦使用 $.proxy 解決,這樣就少定義一個變量 $('#div1').click(function () {setTimeout($.proxy(function () {// this 符合期望$(this).addClass('red')}, this), 1000); });總結一下,$.proxy(fn, obj)返回的就是fn的代理,這個代理和fn的功能一樣,只不過將this設置成了我們期望值。雖然看似簡單、常用的功能,但是用代理模式設計的。
ES6 Proxy
Proxy 可以理解成,在目標對象之前架設一層“攔截”,外界對該對象的訪問,都必須先通過這層攔截,因此提供了一種機制,可以對外界的訪問進行過濾和改寫。就是一個屬性的代理器。
// 明星 let star = {name: '張XX',age: 25,phone: '13910733521' }// 經紀人 let agent = new Proxy(star, {get: function (target, key) {if (key === 'phone') {// 返回經紀人自己的手機號return '18611112222'}if (key === 'price') {// 明星不報價,經紀人報價return 120000}return target[key]},set: function (target, key, val) {if (key === 'customPrice') {if (val < 100000) {// 最低 10wthrow new Error('價格太低')} else {target[key] = valreturn true}}} })// 主辦方 console.log(agent.name) console.log(agent.age) console.log(agent.phone) console.log(agent.price)// 想自己提供報價(砍價,或者高價爭搶) agent.customPrice = 150000 // agent.customPrice = 90000 // 報錯:價格太低 console.log('customPrice', agent.customPrice)代理模式 總結
- 什么是代理模式
- 關鍵:和目標接口一致,不能改變接口
- 前端使用場景
對比:
- vs 適配器模式:
- 適配器模式:提供一個不同的接口(如不同版本的插頭)
- 代理模式:提供一模一樣的接口,用不用代理,訪問 facebook 的網址都一樣
- vs 裝飾器模式
- 裝飾器模式:擴展功能,原有功能不變且可直接使用;使用方也可直接使用目標接口。
- 代理模式:顯示原有功能,提供給使用方的是經過限制或者閹割之后的功能;使用方不可直接使用目標接口。
設計模式驗證:
- 將代理類和目標類進行分離,用代理隔離開目標類和使用者
- 符合開放封閉原則
總結