設計模式(十三):從“FQ”中來認識代理模式(Proxy Pattern)


我們知道Google早就被牆了,所以翻牆才能訪問Google呢,這個“翻牆”的過程就是一個代理的過程。“代理模式”在之前的博客中不止一次的提及過,之前的委托回調就是代理模式的具體應用。今天我們就從“翻牆”中來認識一下代理模式。代理模式的定義如下:

代理模式:為另一個對象提供一個替身或占位符以控制對這個對象的訪問。

首先說一下什么是“代理”吧,其實代理很好理解,你就把“代理”看成是二道販子,說的好聽點叫代理商。就是你買個東西,不從生產地直接買,而是通過二道販子,三道販子來進行購買,這些商販就是代理商,也就是我們今天所說的代理。說的具體點,比如你要買棵蘿卜,那么一般人不會去找菜農,然后給他們錢直接去地里薅蘿卜。大部分人是通過商超來獲取蘿卜,這些商超就是所謂的蘿卜代理商,也就是二道販子。

那么在說一下什么是“翻牆”吧,今天就拿Facebook為例。你是用戶,Facebook網站就好比大蘿卜,你直接去拔蘿卜(直接訪問FaceBook站點)不太現實,所以你得通過二道販子(各種網絡代理)來獲取你想要的蘿卜呢。可是你默認的代理商(GFW--長城防火牆)發現你購買蘿卜不太單純,所以就拒絕進行供貨,這就是你訪問的網站被牆了。可是你不甘心呢,家里兔子還等着吃蘿卜呢。你得尋找新的代理商,此刻你就找到了Shadowsocks這個二道販子。Shadowsocks沒有什么估計的,他可以給你提供大蘿卜,這就是翻牆。Shadowsocks如何使用在此就不做過多的贅述了,自行Google。

言歸正傳,上面說這么多無非都是在解釋什么是“代理”。今天我們就使用Swift代碼來模擬上述的“翻牆”過程,通過這個翻牆過程來認識一下“保護代理模式”和“遠程代理模式”,然后在結合着另一個實例來認識一下“虛擬代理模式”。進入今天的主題。

 

一、“翻牆”的類圖設計

其實在“翻牆”這件事情上我們不關心如何去翻牆,而是關心如何使用代理。在真正網絡訪問時,無論你是進行翻牆訪問,還是不翻牆訪問,都使用的“代理模式”。只不過翻牆之前使用的是“保護代理模式”(GFW), 而翻牆之后使用的是“遠程代理模式”(因為今天的主題是“代理模式”,關於翻牆我們先這么理解着,真正的網絡代理要比這個復雜的多)。在該部分,基於我們今天這個翻牆的場景,然后使用“代理模式”來設計GFW和Shadowsocks兩種代理方式。下方就是我們所設計的翻牆的類圖,稍后會給出具體的代碼實現。

在下方類圖中綠框的部分是我們要訪問的網站,其中有被牆的Facebook和Twitter,也有沒被牆的Cnblogs。這三個Web站點都遵循一樣的網絡訪問協議,此處我們定義了InternetAccessProtocol協議(接口)來模擬這些Web站點所遵循的網絡協議。在InternetAccessProtocol網絡訪問協議中的response()方法是用來響應用戶網絡請求的,getId()方法用來返回該網站的唯一標示,也就是網站的域名。所有的Web站點都必須遵循該網絡請求協議。

上面紅框中是我們今天的核心部分,也就是網絡代理部分。該部分聲明了一個協議ProxyType,所有網絡代理也都必須遵循該協議。因為網絡代理是用來代理網絡訪問的,它作為用戶與Web站點的中轉者,對於用戶來說該代理就如同真正的網站一樣,隨意ProxyType協議也必須的遵循上面定義的網絡訪問協議InternetAccessProtocol。在紅框中有兩個網絡訪問的代理,一個是ShadowsocksProxy,一個是GreatFirewall。

ShadowsocksProxy是遠程代理,你在使用該遠程代理時,你訪問的網站不受限制,也就是說你可以訪問Google、Twitter、Facebook這些網站,遠程代理的態度是Open&Freedom的。遠程代理只負責橋接,至於你訪問的什么網站它不做關心,它只負責響應你的請求。而下方的GreatFirewall就不同了,GreatFirewall是一個保護代理,其中有一個blackList(黑名單),如下所示。blackList數組中記錄的就是那些被牆的網站,只要是請求的網站在blackList中,你的請求是不會得到你請求網站的響應的。這也就是保護代理模式的功能,保護代理模式會添加一些權限的限制,會限制用戶訪問一些不安全的網站。

然后就是黃框中的Client了,在Client中依賴的是代理接口,也就是說Client只能依賴於代理進行網絡訪問。默認的代理就是GreatFirewall,GreatFirewall會屏蔽一些不能讓你訪問的網站。如果用戶選擇ShadowsocksProxy遠程代理進行網絡訪問,就不受GreatFirewall的限制了,這也就是所謂的翻牆。在下方Client類中就有兩種上網方式,一種是Shadowsocks一種是GreatFireFirewall。有一點還是需要說明的是,真正的翻牆不是不經過GreatFirewall,而是你的代理地址在GreatFirewall的白名單中,就是你可以通過GreatFirewall來訪問的你的代理,然后你的代理是不經過GreatFirewall來訪問你想要訪問的Web站點的。該實例只是我們了解代理模式來模擬出來的實例,我們的重點在代理模式。

    

 

二、代碼實現

上述給出了結構的設計,接下來我們就需要進行具體的代碼實現了,我們在實現時仍然使用Swift語言。有了上面的類圖設計,然后在給出代碼實現似乎簡單了許多。下方會分部分給出上述類圖的代碼實現,下方是一些代碼的截圖,更完整的實例請參見本博客后方github分享地址。

1.網絡訪問協議與Web站點的實現

下方就是網絡訪問協議與Web站點的具體實現。在InternetAccessProtocol網絡訪問協議中的response()方法用來響應用戶的請求,getId()方法用來返回網站的域名。在InternetAccessProtocol協議的下方分別實現了三個web站點:Facebook、Twitter、Cnblogs。眾所周知,前兩個已經被牆了,所以如果你要想訪問的話,你得翻牆呢。web站點以及網絡訪問協議代碼如下:

     

 

2. 兩種代理的實現

首先我們先實現一個比較簡單的,也就是遠程代理。使用遠程代理訪問Web站點時沒有一些不必要的限制,就是你訪問什么網站,該遠程代理就會請求什么網站並返回相應的信息。下方代碼片段就是代理協議和遠程代理ShadowsocksProxy的具體實現。在ProxyType代理協議中,setDelegate(delegate)方法用來設置代理,也就是用來設置訪問的Web站點,ProxyType協議也同樣遵循InternetAccessProtocol協議。在ShadowsocksProxy中的delegate成員變量就是用戶要請求的Web站點,你訪問的是A站點,那么此處的代理就是A站點的對象。在ShadowsocksProxy中的response()方法會請求delegate的response()方法,而代理中的getId()方法中則會返回當前遠程代理的域名或者IP, 這一點很關鍵呢。具體代碼實現如下:

    

 

接下來我們來實現我們的長城防火牆,也就是GreatFirewall。下方的代碼實現就是GreatFirewall的實現,其中比上述的遠程代理的實現多了一些東西,其中多了一項權限的控制。當你使用GreatFirewall來訪問Web站點時,GreatFirewall首先會判斷你所訪問的Web站點在不在自己的黑名單中,也就是下方的blackList。如果你訪問的Web站點在blackList中,就說明該站點被牆了,GreatFirewall就不會調用該Web站點的response(),所以用戶就不會受到該網站的相應。相反,如果不在黑名單中,那么就會設置代理,然后就可以調用該Web站點的response()方法做出相應的響應,這也就是所說的“保護代理模式”。其實說白了,保護代理模式就是在遠程代理上添加了一些權限的控制。具體代碼實現如下。

    

 

三、Client與測試用例

上面實現完了Web站點,與Web站點的兩個訪問代理,下方我們就該實現Client用戶的代碼。然后在此基礎上給出測試用例。下方的代碼段就是我們的Client的代碼實現。在Client中你可選擇shadowsocks也可以選擇使用greatFirewall。Client代碼如下:

     

 

下方是我們的測試用例,以及該測試用例的輸出結果。我們先使用遠程代理訪問Facebook是可以訪問的,因為我們的遠程代理沒有添加任何限制。如果你通過GFW來訪問Facebook,就訪問不了,因為Facebook在GFW的黑名單中。但是你通過GFW訪問遠程代理服務器,然后在通過遠程代理服務器去訪問FaceBook是可行的。因為我們的遠程代理服務器不在GFW的黑名單中,所以我們可以訪問遠程代理服務器,而我們的遠程代理服務器是可以訪問Facebook,所以我們可以訪問Facebook。這也是測試用例中的第三段。代碼以及輸出結果如下所示。

   

 

四、虛擬代理

虛擬代理的作用就是為占用存儲空間比較大的對象提供替身。當我們實例化大對象需要一定時間時(比如從網請求較大的圖片),我們就可以使用虛擬代理提供一個對象的替身,等對象加載完畢后在使用我們這個真正的對象。因為虛擬代理較為簡單,在此就不給出類圖了,就直接給出代碼實現吧。其實虛擬代理說白了也是代理模式,就是在大對象的使用者與大對象間添加了一層,這一層就是虛擬代理。

下方我們就以加載大圖片為例,當我們加載比較大的圖片時,為了不讓用戶等待,我可以先通過虛擬代理模式添加一個小的圖片。然后在虛擬代理中將大圖片加載完畢后我們在換回大的圖片即可。下方我們創建了一個圖片協議ImageType,然后創建了一個大的圖片BigImage和一個小的圖片SmallImage。SmallImage就作為BigImage未加載時的替身,當SmallImage加載完畢后我們就不使用SmallImage,而使用BigImage。而這一系列的替換的過程交給我們的虛擬代理來處理。

下方代碼段中的BigImageProxy就是我們的虛擬代理。BigImageProxy也遵循於ImageType協議,對於用戶來說,BigImageProxy的用法與普通的圖片是一樣的。在BigImageProxy中的loadImage()方法是我們虛擬代理的核心。在調用虛擬代理中的loadImage()方法時,如果BigImage已被實例化,就會調用loadImage()方法,如果BigImage沒有實例化,那么就會調用SmallImage中的loadImage()方法,並且對BigImage進行實例化,並將虛擬代理中bigImage的狀態設置為正在初始化狀態。具體實現代碼如下所示:

     

下方代碼段就是上述虛擬代碼的測試用例。ImageClient依賴的是ImageType協議,所以其中的image成員變量可以是真正的圖片,也可以是我們的虛擬代理。虛擬代理對象的使用方式與普通圖片的使用方式一致,測試用例如下所示:

     

 

今天我們的博客中就介紹了三種代理模式:遠程代理模式、保護代理模式、虛擬代理模式。遠程代理訪問是控制訪問遠程對象,保護代理是基於權限的資源訪問,虛擬代理是控制訪問創建開銷大的資源。

上述代碼示例仍然會在github上進行分享,分享地址為:https://github.com/lizelu/DesignPatterns-Swift


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM