(转)代理模式(Proxy)
原文地址:http://www.cnblogs.com/QinBaoBei/archive/2010/05/18/1737866.html
?
為了深刻點理解代理模式,我們先來看一個 Demo ,
首先這個 Demo 是用來測試 QQ 號碼是否在線,
這里涉及到的內容是 Web 服務的使用,
這個 Web 服務所在地址為:
http://webservice.webxml.com.cn/webservices/qqOnlineWebService.asmx
如果有不懂 Web 服務的,還可以查看筆者一篇涉及 Web 服務的文章,
http://www.cnblogs.com/QinBaoBei/archive/2010/03/30/1700898.html
然后,我便將上面的驗證 QQ 是否在線的這個 Web 服務引入到我的項目中,
并且給這個 Web 服務命名為 WebService.TestQQ
然后就是來編寫類了,
主要是一個 TestResult 類,這個類的作用就是來完成訪問 Web 服務,返回驗證結果,
其具體代碼如下:
namespace ProxyQQ
{
??? public static class TestResult
??? {
??????? public static string GetResult(string qqNum)
??????? {
?????????? //調用引用的 Web 服務(自命名為 WebService.TestQQ)
??????????? WebService.TestQQ.qqOnlineWebService testQQ =
??????????????? new WebService.TestQQ.qqOnlineWebService();
??????????? return testQQ.qqCheckOnline(qqNum);
??????? }
??? }
}
然后就是要編寫客戶端了,我這里使用的是一個 WinForm 應用程序來實現的,
這個項目中唯一一個窗體 TestQQ 的代碼如下
using System;
using System.Windows.Forms;
namespace ProxyQQTest
{
??? public partial class TestQQ : Form
??? {
??????? public TestQQ()
??????? {
??????????? InitializeComponent();
??????? }
??????? private void btnQuery_Click(object sender, EventArgs e)
??????? {
???? ?????? //調用 ProxyQQ 命名空間下的靜態類 TestResult 的
??????????? //靜態方法 GetResult()來判斷 QQ 狀態
??????????? string result =
??????????????? ProxyQQ.TestResult.GetResult(txtQQNum.Text.Trim());
??????????? string msg = String.Empty;
??????????? switch (result)
??????????? {
??????????????? case "Y":
??????????????????? msg = "該 QQ 在線";
??????????????????? break;
??????????????? case "N":
??????????????????? msg = "該 QQ 離線";
??????????????????? break;
??????????????? case "E":
??????????????????? msg = "該 QQ 號碼不存在";
??????????????????? break;
??????????????? case "A":
??????????????????? msg = "商業用戶驗證失敗";
??????????????????? break;
??????????????? case "V":
??????????????????? msg = "免費用戶超過數量";
??????????????????? break;
??????????????? default:
??????????????????? break;
??????????? }
??????????? MessageBox.Show(msg, "提示",
??????????????? MessageBoxButtons.OK,
??????????????? MessageBoxIcon.Information);
??????? }
??? }
}
下面就來查詢一下 QQ 號碼咯
然后我讓我的 QQ 上線再來查看
好,說了這么多了,感覺是在介紹 Web 服務似的,
但是請不要誤會,本篇博文是來介紹代理模式的,而非 Web 服務,
下面我們就從上面的這個 Demo 來談談代理模式,
其實上面談到的 Demo 就是代理模式的一種最典型的應用--遠程代理,
先來看上面的 Demo 中,我添加的那個 Web 服務,
當我在我的項目中添加了 Web 服務后呢,
大家可以看到會在項目中自動生成一個 WebReference 的文件夾以及一些文件,
其實這些就是代理了,
您看我在應用程序中只是使用了下面一丁點代碼,
? //調用引用的 Web 服務(自命名為 WebService.TestQQ)
?? WebService.TestQQ.qqOnlineWebService testQQ =?
?????? new WebService.TestQQ.qqOnlineWebService();
?? return testQQ.qqCheckOnline(qqNum);
我們在客戶端只需要通過這一丁點代碼就可以完成訪問 Web 服務并且返回結果,
也可以說成是客戶端通過調用代理來完成遠程訪問并返回結果。
?????????
下面就來看看什么是遠程代理?
遠程代理可以看做另一個服務器上的對象在本地服務器上的代表,
調用代理的方法就會被代理通過網絡來轉發到遠程執行,并且結果會通過網絡返回給代理,
然后再由代理將結果轉發給客戶。
其實上面對遠程代理的定義還是不明確的,就拿 Web 服務來說的話,您也可以調用你自己的項目中的 Web 服務的,
所以完整的遠程代理的定義是,為一個不同地址空間中的對象提供一個局部代表對象,
不過這個不同的地址空間呢可以在您的本地機也可以在遠程機器上,
所以在上面的 Demo 中,
就可以把 WebService.TestQQ.qqOnlineWebService (代理對象)看做是
驗證 QQ 號碼這個對象(這里看做一個對象,它是被代理的對象)在本地機器(也就是我的項目中)上的代理對象,
而后,我在客戶端中便只需要對這個代理對象進行訪問和操作就 OK 了,其他的內容就交給代理對象來完成了,
這是典型的代理模式。
上面呢,介紹的只是代理模式的一種最典型的應用,也就是遠程代理,而下面就要來介紹代理模式的一些具體內容了,
????????
??????????
什么是代理模式?
代理模式為另一個對象提供一種代理(換句話說就是替身)以控制對這個對象的訪問,使用代理模式創建代理對象,
讓這個代理對象控制某個對象的訪問。
下面就來看看代理模式的結構類圖
從上面的結構類圖可以看出,代理類和具體主題類都繼承子抽象主題類,
何為要這樣實現繼承呢?
很簡單的,由于 Proxy 和 RealSubject 均繼承自 Subject 這個抽象主題類,
并且在 Proxy 和 RealSubject 中均實現了 Request 方法,
所以在需要使用 RealSubject 的地方便可以使用 Proxy 來替代
(換句話說的話,可以認為 Proxy 和 RealSubject 是同一類型)
由于使用了 Proxy 來替代 RealSubject ,所以便實現了通過 Proxy 來代理 RealSubject ,
同時在 Proxy 這個代理類中保存和維護了一個 RealSubject 對象,
當然就在一定程度上實現了通過 Proxy 來控制 RealSubject 。
???????
??????????
下面還是先來看看這個代理模式的最基本的結構實現吧
Subject 類
namespace Proxy
{
??? public abstract class Subject
??? {
??????? public abstract void Request();
??? }
}
Proxy 類
using System;
namespace Proxy
{
??? public class Proxy:Subject
??? {
??????? private RealSubject realSubject;
??????? public override void Request()
??????? {
??????????? Console.WriteLine(this.GetType().ToString());
??????????? if (realSubject == null)
??????????? {
??????????????? realSubject = new RealSubject();
??????????? }
??????????? realSubject.Request();
??????? }
??? }
}
RealSubject 類
using System;
namespace Proxy
{
??? public class RealSubject:Subject
??? {
??????? public override void Request()
??????? {
??????????? Console.WriteLine(this.GetType().ToString());
??????? }
??? }
}
客戶端代碼
using System;
namespace ProxyTest
{
??? class Program
??? {
??????? static void Main(string[] args)
??????? {
?????????? Proxy.Subject proxy = new Proxy.Proxy();
??????????? proxy.Request();
??????????? Console.ReadKey();
??????? }
??? }
}
光看上面的代碼和效果,我想大家很難明白是怎么一回事,那么還請看下面的解釋,然后再回過頭結合代碼來看,
那么大家應該就可以明白代理模式到底是咋一回事了,
首先,可以看到 Proxy 和 RealSubject 這兩個類都繼承自抽象類 Subject ,
同時均實現了在抽象類 Subject 類中定義的 Request 方法,
這樣的話,在某一層意義上來說,Proxy 就是 RealSubject 的替身了,
因為他們的抽象類型都是一樣的,所以,便可以在 RealSubject 出現的地方使用 Proxy 來替代了,
比如在上面的例子中,我本來的意義是要在客戶端中調用 RealSubject 的 Request 方法的,
所以本來在客戶端代碼中應該如下寫的:
???? Proxy.Subject realSubject= new Proxy.RealSubject();
?? realSubject.Request();
而事實上并非這樣,我是通過實例化一個 Proxy 類來完成操作的,因為我在 Proxy 中保存了一個 RealSubject 對象,
并且在 Proxy 類的 Request 方法中調用了 RealSubject 的 Request 方法,
所以便是代理實現了調用 RealSubject 的 Request 方法,
在這里,我想就有疑問了,那就是我為什么一定要在客戶端來通過 Proxy 代理完成這個操作,
而不是直接實例化 RealSubject 對象來完成 Request 操作了,
其實,這里涉及到的便是代理模式的具體應用了,前面看了一種應用也就是遠程代理,
下面我們來看另外一種代理模式的應用,也就是虛擬代理,
比如,我要加載一個很大的視頻文件或者是圖片文件(比如 800 像素拍下的照片 2M),
此時由于網絡速度慢的緣故,在加載的時候費個什么幾秒鐘那是非常常見的,但是就是在這幾秒鐘中,
難道您就讓用戶干巴巴的等著嗎?要我是用戶的話,看著半天沒反應的瀏覽器或者是窗體,
我想,我還是關閉算了吧 ! ! !
那么我們有什么辦法來解決上面的問題呢?
其實有一種比較好的辦法,就是當圖片在下載時,可以發布一個提示消息,比如“正在下載中,請等待”,
做的好一點的話,還可以顯示一張本地的小圖片,這樣會更炫,
然后等下載完成后便將這些提示消息去掉,然后顯示圖片就 OK 了,這樣可以極大的提高用戶體驗,
其實這種效果在很多地方都常見了,而這就是通過虛擬代理來解決的,
我們先來看一下騰訊主頁上這種效果吧
????????
???????????
下面就來看如何通過虛擬代理來完成上面的這種效果
我們就來看類了,
首先是 Subject 這個抽象類,在這里是 LoadImage 來充當
namespace ProxyVirtual
{
??? public abstract class LoadImage
??? {
??????? public abstract void LoadImageRequest();
??? }
}
然后是 Proxy 這個代理類,這里由 ProxyLoadImage 來充當,
using System.Windows.Forms;
using System.Drawing;
using System.Threading;
namespace ProxyVirtual
{
??? public class ProxyLoadImage : LoadImage
??? {
? ????? //這里要保存一個具體主題類的引用
??????? //這樣才能夠實現控制具體主題了 RealLoadImage
??????? private RealLoadImage realLoadImage;
??????? private PictureBox pcBox;
??????? public ProxyLoadImage(PictureBox pcBox, RealLoadImage realLoadImage)
??????? {
??????????? this.pcBox = pcBox;
??????????? this.realLoadImage = realLoadImage;
??????? }
??????? public override void LoadImageRequest()
??????? {
????????? ? //首先是要實例化一個線程
??????????? Thread thread = new Thread(ThreadMethod);
????? ?? ?? //然后就是要啟動這個線程了
??????????? thread.Start();
??????? }
??????? private void ThreadMethod()
??????? {
??? ??????? //將整個提示用戶還在下載的圖片(小圖片)
??????????? //和正在下載的圖片(大圖片)放在同一個線程中
??????????? //可以防止其他線程對整個應用程序進行干擾
??????????? pcBox.Image = Image.FromFile("SquareCircle.gif");
??????????? realLoadImage.LoadImageRequest();
??????? }
??? }
}
還有一個就是具體主題類 RealLoadImage 類了
using System.Windows.Forms;
using System.Drawing;
using System.Net;
namespace ProxyVirtual
{
??? public class RealLoadImage : LoadImage
??? {
??????? private string strUrl;
??????? private PictureBox pcBox;
??????? public RealLoadImage(string strUrl, PictureBox pcBox)
??????? {
??????????? this.strUrl = strUrl;
??????????? this.pcBox = pcBox;
??????? }
??????? public override void LoadImageRequest()
??????? {
??????????? //發出對指定 URL 路徑的請求(這里是請求一張圖片)
??????????? WebRequest request = WebRequest.Create(strUrl);
??????????? //返回對請求的響應結果
?????????? WebResponse response = request.GetResponse();
???????? ?? //從網絡資源中返回數據流,然后給圖片框顯示
??????????? pcBox.Image = Image.FromStream(response.GetResponseStream());
??????? }
??? }
}
然后就是客戶端代碼了(這里是一個 Form 的 Code-Behind)
using System;
using System.Windows.Forms;
namespace ProxyVirtualTest
{
??? public partial class Test : Form
??? {
??????? public Test()
??????? {
??????????? InitializeComponent();
??????? }
??????? private void Test_Load(object sender, EventArgs e)
??????? {
??????????? ProxyVirtual.LoadImage loadImage =
??????????????? new ProxyVirtual.ProxyLoadImage(this.pcBox,
??????????????????? new ProxyVirtual.RealLoadImage(
??????????????????????? "http://images.cnblogs.com/cnblogs_com/qinbaobei/6.jpg",
??????????????????????? this.pcBox));
??????????? //調用代理類的 LoadImageRequest
??????????? loadImage.LoadImageRequest();
??????? }
??? }
}
下面也把效果給出來,剛開始加載的時候效果是
當下載完成以后,便會出現
上面的這張圖片呢,是我的博客中的一張圖片,我通過 URL 來下載到我的應用程序當中。
下面我再給出上面這個 Demo 的類圖,大家可以拿這個類圖和最開始的代理模式的結構類圖比較比較,
我們不談別的內容,光從上面的這幅類圖中便可以看出上面的 Demo 使用的就是代理模式,
????????
????????
下面我們還再來具體的談一下何為虛擬代理?
虛擬代理是作為創建大的對象的代理類,一般來說,創建一個對象需要消耗比較大的資源時,
可以通過虛擬代理來實現這個對象只有在需要的時候才會被真正創建,
可以通過使用代理(這個代理不怎么耗資源)來在必要的時候將被代理的對象加載,
也就是通過不怎么耗資源的代理來控制什么時候需要加載比較耗資源的被代理對象,
在上面的 Demo 中,大的圖片就是比較耗資源的,所以通過一個不怎么耗資源的小圖片先來代理它,
在真正需要這個大圖片的時候再通過代理來加載。
再看一個虛擬代理的應用,那就是當您打開一個有很多圖片的網頁時,
一般來說,文字是全部可以看到的(會在加載網頁的時候全部下載過來),
而圖片卻必須要一張一張的下載才能看得
(比如您將滑輪往下滾會看到一些圖片并沒有顯示出來,只是在那里留出一個方框,
?? 而真實的圖片就需要等待才能顯示),
那些未打開的圖片框就是通過虛擬代理來替代了真實的圖片,
此時,代理存儲了真實圖片的路徑和大小,所以在也沒上才會出現圖片的方框,而并沒有真實的圖像。
在上面瀏覽器中使用代理模式的好處是什么呢?很明顯,通過使用虛擬代理優化了下載。
??????????????
??????????????????
上面呢,重點介紹了代理模式的兩種應用,即遠程代理和虛擬代理,其實呢,代理模式還有很多變種的
既然是變種的話,基本上就差不太多遠啦,
比如安全代理,智能引用代理,緩存代理,寫入時復制代理,防火墻代理等等,
在這里就不做一一介紹了,如果有想了解的,可以 Google 或者是 Bing 一下,就會出來一大堆了 ! ! !????????????
還有就是 TerryLee 的博客上也有一點點介紹的,感興趣的可以去看看 ! ! !
?
轉載于:https://www.cnblogs.com/fcsh820/archive/2010/05/18/1738088.html
總結
以上是生活随笔為你收集整理的(转)代理模式(Proxy)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 写了两个简单的小工具,文件夹文件操作的
- 下一篇: 前置声明相关