前段時間因為“某度競價排名”的事情鬧得沸沸揚揚的,因為某度搜出來的內容的質量實在是不敢恭維,所以早就戒掉了某度改用Google了。但是Google早就被牆了呢,所以翻牆才能訪問Google呢,這個“翻牆”的過程就是一個代理的過程。“代理模式”在之前的博客中不止一次的提及過,之前的委托回調就是代理模式 ...
前段時間因為“某度競價排名”的事情鬧得沸沸揚揚的,因為某度搜出來的內容的質量實在是不敢恭維,所以早就戒掉了某度改用Google了。但是Google早就被牆了呢,所以FQ才能訪問Google呢,這個“FQ”的過程就是一個代理的過程。“代理模式”在之前的博客中不止一次的提及過,之前的委托回調就是代理模式的具體應用。今天我們就從“FQ”中來認識一下代理模式。代理模式的定義如下:
代理模式:為另一個對象提供一個替身或占位符以控制對這個對象的訪問。
首先說一下什麼是“代理”吧,其實代理很好理解,你就把“代理”看成是二道販子,說的好聽點叫代理商。就是你買個東西,不從生產地直接買,而是通過二道販子,三道販子來進行購買,這些商販就是代理商,也就是我們今天所說的代理。說的具體點,比如你要買棵蘿蔔,那麼一般人不會去找菜農,然後給他們錢直接去地里薅蘿蔔。大部分人是通過商超來獲取蘿蔔,這些商超就是所謂的蘿蔔代理商,也就是二道販子。
那麼在說一下什麼是“FQ”吧,今天就拿Facebook為例。你是用戶,Facebook網站就好比大蘿蔔,你直接去拔蘿蔔(直接訪問FaceBook站點)不太現實,所以你得通過二道販子(各種網路代理)來獲取你想要的蘿蔔呢。可是你預設的代理商(GFW--長城防火牆)發現你購買蘿蔔不太單純,所以就拒絕進行供貨,這就是你訪問的網站被牆了。可是你不甘心呢,家裡兔子還等著吃蘿蔔呢。你得尋找新的代理商,此刻你就找到了Shadowsocks這個二道販子。Shadowsocks沒有什麼估計的,他可以給你提供大蘿蔔,這就是FQ。Shadowsocks如何使用在此就不做過多的贅述了,自行Google。
言歸正傳,上面說這麼多無非都是在解釋什麼是“代理”。今天我們就使用Swift代碼來模擬上述的“FQ”過程,通過這個FQ過程來認識一下“保護代理模式”和“遠程代理模式”,然後在結合著另一個實例來認識一下“虛擬代理模式”。進入今天的主題。
一、“FQ”的類圖設計
其實在“FQ”這件事情上我們不關心如何去FQ,而是關心如何使用代理。在真正網路訪問時,無論你是進行FQ訪問,還是不FQ訪問,都使用的“代理模式”。只不過FQ之前使用的是“保護代理模式”(GFW), 而FQ之後使用的是“遠程代理模式”(因為今天的主題是“代理模式”,關於FQ我們先這麼理解著,真正的網路代理要比這個複雜的多)。在該部分,基於我們今天這個FQ的場景,然後使用“代理模式”來設計GFW和Shadowsocks兩種代理方式。下方就是我們所設計的FQ的類圖,稍後會給出具體的代碼實現。
在下方類圖中綠框的部分是我們要訪問的網站,其中有被牆的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的限制了,這也就是所謂的FQ。在下方Client類中就有兩種上網方式,一種是Shadowsocks一種是GreatFireFirewall。有一點還是需要說明的是,真正的FQ不是不經過GreatFirewall,而是你的代理地址在GreatFirewall的白名單中,就是你可以通過GreatFirewall來訪問的你的代理,然後你的代理是不經過GreatFirewall來訪問你想要訪問的Web站點的。該實例只是我們瞭解代理模式來模擬出來的實例,我們的重點在代理模式。
二、代碼實現
上述給出了結構的設計,接下來我們就需要進行具體的代碼實現了,我們在實現時仍然使用Swift語言。有了上面的類圖設計,然後在給出代碼實現似乎簡單了許多。下方會分部分給出上述類圖的代碼實現,下方是一些代碼的截圖,更完整的實例請參見本博客後方github分享地址。
1.網路訪問協議與Web站點的實現
下方就是網路訪問協議與Web站點的具體實現。在InternetAccessProtocol網路訪問協議中的response()方法用來響應用戶的請求,getId()方法用來返回網站的功能變數名稱。在InternetAccessProtocol協議的下方分別實現了三個web站點:Facebook、Twitter、Cnblogs。眾所周知,前兩個已經被牆了,所以如果你要想訪問的話,你得FQ呢。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