设计模式(十三):从“FQ”中来认识代理模式(Proxy Pattern)
我們知道Google早就被墻了,所以翻墻才能訪(fǎng)問(wèn)Google呢,這個(gè)“翻墻”的過(guò)程就是一個(gè)代理的過(guò)程。“代理模式”在之前的博客中不止一次的提及過(guò),之前的委托回調(diào)就是代理模式的具體應(yīng)用。今天我們就從“翻墻”中來(lái)認(rèn)識(shí)一下代理模式。代理模式的定義如下:
代理模式:為另一個(gè)對(duì)象提供一個(gè)替身或占位符以控制對(duì)這個(gè)對(duì)象的訪(fǎng)問(wèn)。
首先說(shuō)一下什么是“代理”吧,其實(shí)代理很好理解,你就把“代理”看成是二道販子,說(shuō)的好聽(tīng)點(diǎn)叫代理商。就是你買(mǎi)個(gè)東西,不從生產(chǎn)地直接買(mǎi),而是通過(guò)二道販子,三道販子來(lái)進(jìn)行購(gòu)買(mǎi),這些商販就是代理商,也就是我們今天所說(shuō)的代理。說(shuō)的具體點(diǎn),比如你要買(mǎi)棵蘿卜,那么一般人不會(huì)去找菜農(nóng),然后給他們錢(qián)直接去地里薅蘿卜。大部分人是通過(guò)商超來(lái)獲取蘿卜,這些商超就是所謂的蘿卜代理商,也就是二道販子。
那么在說(shuō)一下什么是“翻墻”吧,今天就拿Facebook為例。你是用戶(hù),F(xiàn)acebook網(wǎng)站就好比大蘿卜,你直接去拔蘿卜(直接訪(fǎng)問(wèn)FaceBook站點(diǎn))不太現(xiàn)實(shí),所以你得通過(guò)二道販子(各種網(wǎng)絡(luò)代理)來(lái)獲取你想要的蘿卜呢??墒悄隳J(rèn)的代理商(GFW--長(zhǎng)城防火墻)發(fā)現(xiàn)你購(gòu)買(mǎi)蘿卜不太單純,所以就拒絕進(jìn)行供貨,這就是你訪(fǎng)問(wèn)的網(wǎng)站被墻了??墒悄悴桓市哪?,家里兔子還等著吃蘿卜呢。你得尋找新的代理商,此刻你就找到了Shadowsocks這個(gè)二道販子。Shadowsocks沒(méi)有什么估計(jì)的,他可以給你提供大蘿卜,這就是翻墻。Shadowsocks如何使用在此就不做過(guò)多的贅述了,自行Google。
言歸正傳,上面說(shuō)這么多無(wú)非都是在解釋什么是“代理”。今天我們就使用Swift代碼來(lái)模擬上述的“翻墻”過(guò)程,通過(guò)這個(gè)翻墻過(guò)程來(lái)認(rèn)識(shí)一下“保護(hù)代理模式”和“遠(yuǎn)程代理模式”,然后在結(jié)合著另一個(gè)實(shí)例來(lái)認(rèn)識(shí)一下“虛擬代理模式”。進(jìn)入今天的主題。
一、“翻墻”的類(lèi)圖設(shè)計(jì)
其實(shí)在“翻墻”這件事情上我們不關(guān)心如何去翻墻,而是關(guān)心如何使用代理。在真正網(wǎng)絡(luò)訪(fǎng)問(wèn)時(shí),無(wú)論你是進(jìn)行翻墻訪(fǎng)問(wèn),還是不翻墻訪(fǎng)問(wèn),都使用的“代理模式”。只不過(guò)翻墻之前使用的是“保護(hù)代理模式”(GFW), 而翻墻之后使用的是“遠(yuǎn)程代理模式”(因?yàn)榻裉斓闹黝}是“代理模式”,關(guān)于翻墻我們先這么理解著,真正的網(wǎng)絡(luò)代理要比這個(gè)復(fù)雜的多)。在該部分,基于我們今天這個(gè)翻墻的場(chǎng)景,然后使用“代理模式”來(lái)設(shè)計(jì)GFW和Shadowsocks兩種代理方式。下方就是我們所設(shè)計(jì)的翻墻的類(lèi)圖,稍后會(huì)給出具體的代碼實(shí)現(xiàn)。
在下方類(lèi)圖中綠框的部分是我們要訪(fǎng)問(wèn)的網(wǎng)站,其中有被墻的Facebook和Twitter,也有沒(méi)被墻的Cnblogs。這三個(gè)Web站點(diǎn)都遵循一樣的網(wǎng)絡(luò)訪(fǎng)問(wèn)協(xié)議,此處我們定義了InternetAccessProtocol協(xié)議(接口)來(lái)模擬這些Web站點(diǎn)所遵循的網(wǎng)絡(luò)協(xié)議。在InternetAccessProtocol網(wǎng)絡(luò)訪(fǎng)問(wèn)協(xié)議中的response()方法是用來(lái)響應(yīng)用戶(hù)網(wǎng)絡(luò)請(qǐng)求的,getId()方法用來(lái)返回該網(wǎng)站的唯一標(biāo)示,也就是網(wǎng)站的域名。所有的Web站點(diǎn)都必須遵循該網(wǎng)絡(luò)請(qǐng)求協(xié)議。
上面紅框中是我們今天的核心部分,也就是網(wǎng)絡(luò)代理部分。該部分聲明了一個(gè)協(xié)議ProxyType,所有網(wǎng)絡(luò)代理也都必須遵循該協(xié)議。因?yàn)榫W(wǎng)絡(luò)代理是用來(lái)代理網(wǎng)絡(luò)訪(fǎng)問(wèn)的,它作為用戶(hù)與Web站點(diǎn)的中轉(zhuǎn)者,對(duì)于用戶(hù)來(lái)說(shuō)該代理就如同真正的網(wǎng)站一樣,隨意ProxyType協(xié)議也必須的遵循上面定義的網(wǎng)絡(luò)訪(fǎng)問(wèn)協(xié)議InternetAccessProtocol。在紅框中有兩個(gè)網(wǎng)絡(luò)訪(fǎng)問(wèn)的代理,一個(gè)是ShadowsocksProxy,一個(gè)是GreatFirewall。
ShadowsocksProxy是遠(yuǎn)程代理,你在使用該遠(yuǎn)程代理時(shí),你訪(fǎng)問(wèn)的網(wǎng)站不受限制,也就是說(shuō)你可以訪(fǎng)問(wèn)Google、Twitter、Facebook這些網(wǎng)站,遠(yuǎn)程代理的態(tài)度是Open&Freedom的。遠(yuǎn)程代理只負(fù)責(zé)橋接,至于你訪(fǎng)問(wèn)的什么網(wǎng)站它不做關(guān)心,它只負(fù)責(zé)響應(yīng)你的請(qǐng)求。而下方的GreatFirewall就不同了,GreatFirewall是一個(gè)保護(hù)代理,其中有一個(gè)blackList(黑名單),如下所示。blackList數(shù)組中記錄的就是那些被墻的網(wǎng)站,只要是請(qǐng)求的網(wǎng)站在blackList中,你的請(qǐng)求是不會(huì)得到你請(qǐng)求網(wǎng)站的響應(yīng)的。這也就是保護(hù)代理模式的功能,保護(hù)代理模式會(huì)添加一些權(quán)限的限制,會(huì)限制用戶(hù)訪(fǎng)問(wèn)一些不安全的網(wǎng)站。
然后就是黃框中的Client了,在Client中依賴(lài)的是代理接口,也就是說(shuō)Client只能依賴(lài)于代理進(jìn)行網(wǎng)絡(luò)訪(fǎng)問(wèn)。默認(rèn)的代理就是GreatFirewall,GreatFirewall會(huì)屏蔽一些不能讓你訪(fǎng)問(wèn)的網(wǎng)站。如果用戶(hù)選擇ShadowsocksProxy遠(yuǎn)程代理進(jìn)行網(wǎng)絡(luò)訪(fǎng)問(wèn),就不受GreatFirewall的限制了,這也就是所謂的翻墻。在下方Client類(lèi)中就有兩種上網(wǎng)方式,一種是Shadowsocks一種是GreatFireFirewall。有一點(diǎn)還是需要說(shuō)明的是,真正的翻墻不是不經(jīng)過(guò)GreatFirewall,而是你的代理地址在GreatFirewall的白名單中,就是你可以通過(guò)GreatFirewall來(lái)訪(fǎng)問(wèn)的你的代理,然后你的代理是不經(jīng)過(guò)GreatFirewall來(lái)訪(fǎng)問(wèn)你想要訪(fǎng)問(wèn)的Web站點(diǎn)的。該實(shí)例只是我們了解代理模式來(lái)模擬出來(lái)的實(shí)例,我們的重點(diǎn)在代理模式。
二、代碼實(shí)現(xiàn)
上述給出了結(jié)構(gòu)的設(shè)計(jì),接下來(lái)我們就需要進(jìn)行具體的代碼實(shí)現(xiàn)了,我們?cè)趯?shí)現(xiàn)時(shí)仍然使用Swift語(yǔ)言。有了上面的類(lèi)圖設(shè)計(jì),然后在給出代碼實(shí)現(xiàn)似乎簡(jiǎn)單了許多。下方會(huì)分部分給出上述類(lèi)圖的代碼實(shí)現(xiàn),下方是一些代碼的截圖,更完整的實(shí)例請(qǐng)參見(jiàn)本博客后方github分享地址。
1.網(wǎng)絡(luò)訪(fǎng)問(wèn)協(xié)議與Web站點(diǎn)的實(shí)現(xiàn)
下方就是網(wǎng)絡(luò)訪(fǎng)問(wèn)協(xié)議與Web站點(diǎn)的具體實(shí)現(xiàn)。在InternetAccessProtocol網(wǎng)絡(luò)訪(fǎng)問(wèn)協(xié)議中的response()方法用來(lái)響應(yīng)用戶(hù)的請(qǐng)求,getId()方法用來(lái)返回網(wǎng)站的域名。在InternetAccessProtocol協(xié)議的下方分別實(shí)現(xiàn)了三個(gè)web站點(diǎn):Facebook、Twitter、Cnblogs。眾所周知,前兩個(gè)已經(jīng)被墻了,所以如果你要想訪(fǎng)問(wèn)的話(huà),你得翻墻呢。web站點(diǎn)以及網(wǎng)絡(luò)訪(fǎng)問(wèn)協(xié)議代碼如下:
2. 兩種代理的實(shí)現(xiàn)
首先我們先實(shí)現(xiàn)一個(gè)比較簡(jiǎn)單的,也就是遠(yuǎn)程代理。使用遠(yuǎn)程代理訪(fǎng)問(wèn)Web站點(diǎn)時(shí)沒(méi)有一些不必要的限制,就是你訪(fǎng)問(wèn)什么網(wǎng)站,該遠(yuǎn)程代理就會(huì)請(qǐng)求什么網(wǎng)站并返回相應(yīng)的信息。下方代碼片段就是代理協(xié)議和遠(yuǎn)程代理ShadowsocksProxy的具體實(shí)現(xiàn)。在ProxyType代理協(xié)議中,setDelegate(delegate)方法用來(lái)設(shè)置代理,也就是用來(lái)設(shè)置訪(fǎng)問(wèn)的Web站點(diǎn),ProxyType協(xié)議也同樣遵循InternetAccessProtocol協(xié)議。在ShadowsocksProxy中的delegate成員變量就是用戶(hù)要請(qǐng)求的Web站點(diǎn),你訪(fǎng)問(wèn)的是A站點(diǎn),那么此處的代理就是A站點(diǎn)的對(duì)象。在ShadowsocksProxy中的response()方法會(huì)請(qǐng)求delegate的response()方法,而代理中的getId()方法中則會(huì)返回當(dāng)前遠(yuǎn)程代理的域名或者IP, 這一點(diǎn)很關(guān)鍵呢。具體代碼實(shí)現(xiàn)如下:
接下來(lái)我們來(lái)實(shí)現(xiàn)我們的長(zhǎng)城防火墻,也就是GreatFirewall。下方的代碼實(shí)現(xiàn)就是GreatFirewall的實(shí)現(xiàn),其中比上述的遠(yuǎn)程代理的實(shí)現(xiàn)多了一些東西,其中多了一項(xiàng)權(quán)限的控制。當(dāng)你使用GreatFirewall來(lái)訪(fǎng)問(wèn)Web站點(diǎn)時(shí),GreatFirewall首先會(huì)判斷你所訪(fǎng)問(wèn)的Web站點(diǎn)在不在自己的黑名單中,也就是下方的blackList。如果你訪(fǎng)問(wèn)的Web站點(diǎn)在blackList中,就說(shuō)明該站點(diǎn)被墻了,GreatFirewall就不會(huì)調(diào)用該Web站點(diǎn)的response(),所以用戶(hù)就不會(huì)受到該網(wǎng)站的相應(yīng)。相反,如果不在黑名單中,那么就會(huì)設(shè)置代理,然后就可以調(diào)用該Web站點(diǎn)的response()方法做出相應(yīng)的響應(yīng),這也就是所說(shuō)的“保護(hù)代理模式”。其實(shí)說(shuō)白了,保護(hù)代理模式就是在遠(yuǎn)程代理上添加了一些權(quán)限的控制。具體代碼實(shí)現(xiàn)如下。
三、Client與測(cè)試用例
上面實(shí)現(xiàn)完了Web站點(diǎn),與Web站點(diǎn)的兩個(gè)訪(fǎng)問(wèn)代理,下方我們就該實(shí)現(xiàn)Client用戶(hù)的代碼。然后在此基礎(chǔ)上給出測(cè)試用例。下方的代碼段就是我們的Client的代碼實(shí)現(xiàn)。在Client中你可選擇shadowsocks也可以選擇使用greatFirewall。Client代碼如下:
下方是我們的測(cè)試用例,以及該測(cè)試用例的輸出結(jié)果。我們先使用遠(yuǎn)程代理訪(fǎng)問(wèn)Facebook是可以訪(fǎng)問(wèn)的,因?yàn)槲覀兊倪h(yuǎn)程代理沒(méi)有添加任何限制。如果你通過(guò)GFW來(lái)訪(fǎng)問(wèn)Facebook,就訪(fǎng)問(wèn)不了,因?yàn)镕acebook在GFW的黑名單中。但是你通過(guò)GFW訪(fǎng)問(wèn)遠(yuǎn)程代理服務(wù)器,然后在通過(guò)遠(yuǎn)程代理服務(wù)器去訪(fǎng)問(wèn)FaceBook是可行的。因?yàn)槲覀兊倪h(yuǎn)程代理服務(wù)器不在GFW的黑名單中,所以我們可以訪(fǎng)問(wèn)遠(yuǎn)程代理服務(wù)器,而我們的遠(yuǎn)程代理服務(wù)器是可以訪(fǎng)問(wèn)Facebook,所以我們可以訪(fǎng)問(wèn)Facebook。這也是測(cè)試用例中的第三段。代碼以及輸出結(jié)果如下所示。
四、虛擬代理
虛擬代理的作用就是為占用存儲(chǔ)空間比較大的對(duì)象提供替身。當(dāng)我們實(shí)例化大對(duì)象需要一定時(shí)間時(shí)(比如從網(wǎng)請(qǐng)求較大的圖片),我們就可以使用虛擬代理提供一個(gè)對(duì)象的替身,等對(duì)象加載完畢后在使用我們這個(gè)真正的對(duì)象。因?yàn)樘摂M代理較為簡(jiǎn)單,在此就不給出類(lèi)圖了,就直接給出代碼實(shí)現(xiàn)吧。其實(shí)虛擬代理說(shuō)白了也是代理模式,就是在大對(duì)象的使用者與大對(duì)象間添加了一層,這一層就是虛擬代理。
下方我們就以加載大圖片為例,當(dāng)我們加載比較大的圖片時(shí),為了不讓用戶(hù)等待,我可以先通過(guò)虛擬代理模式添加一個(gè)小的圖片。然后在虛擬代理中將大圖片加載完畢后我們?cè)趽Q回大的圖片即可。下方我們創(chuàng)建了一個(gè)圖片協(xié)議ImageType,然后創(chuàng)建了一個(gè)大的圖片BigImage和一個(gè)小的圖片SmallImage。SmallImage就作為BigImage未加載時(shí)的替身,當(dāng)SmallImage加載完畢后我們就不使用SmallImage,而使用BigImage。而這一系列的替換的過(guò)程交給我們的虛擬代理來(lái)處理。
下方代碼段中的BigImageProxy就是我們的虛擬代理。BigImageProxy也遵循于ImageType協(xié)議,對(duì)于用戶(hù)來(lái)說(shuō),BigImageProxy的用法與普通的圖片是一樣的。在BigImageProxy中的loadImage()方法是我們虛擬代理的核心。在調(diào)用虛擬代理中的loadImage()方法時(shí),如果BigImage已被實(shí)例化,就會(huì)調(diào)用loadImage()方法,如果BigImage沒(méi)有實(shí)例化,那么就會(huì)調(diào)用SmallImage中的loadImage()方法,并且對(duì)BigImage進(jìn)行實(shí)例化,并將虛擬代理中bigImage的狀態(tài)設(shè)置為正在初始化狀態(tài)。具體實(shí)現(xiàn)代碼如下所示:
下方代碼段就是上述虛擬代碼的測(cè)試用例。ImageClient依賴(lài)的是ImageType協(xié)議,所以其中的image成員變量可以是真正的圖片,也可以是我們的虛擬代理。虛擬代理對(duì)象的使用方式與普通圖片的使用方式一致,測(cè)試用例如下所示:
今天我們的博客中就介紹了三種代理模式:遠(yuǎn)程代理模式、保護(hù)代理模式、虛擬代理模式。遠(yuǎn)程代理訪(fǎng)問(wèn)是控制訪(fǎng)問(wèn)遠(yuǎn)程對(duì)象,保護(hù)代理是基于權(quán)限的資源訪(fǎng)問(wèn),虛擬代理是控制訪(fǎng)問(wèn)創(chuàng)建開(kāi)銷(xiāo)大的資源。
上述代碼示例仍然會(huì)在github上進(jìn)行分享,分享地址為:https://github.com/lizelu/DesignPatterns-Swift
總結(jié)
以上是生活随笔為你收集整理的设计模式(十三):从“FQ”中来认识代理模式(Proxy Pattern)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: C#对象实例化
- 下一篇: k8s实战之Service