(转)代理模式(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

image

然后就是来编写类了,

主要是一个 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 应用程序来实现的,

image

这个项目中唯一一个窗体 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 号码咯

image

然后我让我的 QQ 上线再来查看

image

好,说了这么多了,感觉是在介绍 Web 服务似的,

但是请不要误会,本篇博文是来介绍代理模式的,而非 Web 服务,

下面我们就从上面的这个 Demo 来谈谈代理模式,

其实上面谈到的 Demo 就是代理模式的一种最典型的应用--远程代理,

先来看上面的 Demo 中,我添加的那个 Web 服务,

当我在我的项目中添加了 Web 服务后呢,

大家可以看到会在项目中自动生成一个 WebReference 的文件夹以及一些文件,

其实这些就是代理了,

image

您看我在应用程序中只是使用了下面一丁点代码,

  //调用引用的 Web 服务(自命名为 WebService.TestQQ)
   WebService.TestQQ.qqOnlineWebService testQQ = 
       new WebService.TestQQ.qqOnlineWebService();

   return testQQ.qqCheckOnline(qqNum);

我们在客户端只需要通过这一丁点代码就可以完成访问 Web 服务并且返回结果,

也可以说成是客户端通过调用代理来完成远程访问并返回结果。

         

下面就来看看什么是远程代理?

远程代理可以看做另一个服务器上的对象在本地服务器上的代表,

调用代理的方法就会被代理通过网络来转发到远程执行,并且结果会通过网络返回给代理,

然后再由代理将结果转发给客户。

其实上面对远程代理的定义还是不明确的,就拿 Web 服务来说的话,您也可以调用你自己的项目中的 Web 服务的,

所以完整的远程代理的定义是,为一个不同地址空间中的对象提供一个局部代表对象,

不过这个不同的地址空间呢可以在您的本地机也可以在远程机器上,

所以在上面的 Demo 中,

就可以把 WebService.TestQQ.qqOnlineWebService (代理对象)看做是

验证 QQ 号码这个对象(这里看做一个对象,它是被代理的对象)在本地机器(也就是我的项目中)上的代理对象,

而后,我在客户端中便只需要对这个代理对象进行访问和操作就 OK 了,其他的内容就交给代理对象来完成了,

这是典型的代理模式。

上面呢,介绍的只是代理模式的一种最典型的应用,也就是远程代理,而下面就要来介绍代理模式的一些具体内容了,

        

          

什么是代理模式?

代理模式为另一个对象提供一种代理(换句话说就是替身)以控制对这个对象的访问,使用代理模式创建代理对象,

让这个代理对象控制某个对象的访问。

下面就来看看代理模式的结构类图

image

从上面的结构类图可以看出,代理类和具体主题类都继承子抽象主题类,

何为要这样实现继承呢?

很简单的,由于 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();
        }
    }
}

image

光看上面的代码和效果,我想大家很难明白是怎么一回事,那么还请看下面的解释,然后再回过头结合代码来看,

那么大家应该就可以明白代理模式到底是咋一回事了,

首先,可以看到 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 了,这样可以极大的提高用户体验,

其实这种效果在很多地方都常见了,而这就是通过虚拟代理来解决的,

我们先来看一下腾讯主页上这种效果吧

image

        

           

下面就来看如何通过虚拟代理来完成上面的这种效果

我们就来看类了,

首先是 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();
        }
    }
}

下面也把效果给出来,刚开始加载的时候效果是

image

当下载完成以后,便会出现

image

上面的这张图片呢,是我的博客中的一张图片,我通过 URL 来下载到我的应用程序当中。

下面我再给出上面这个 Demo 的类图,大家可以拿这个类图和最开始的代理模式的结构类图比较比较,

image

我们不谈别的内容,光从上面的这幅类图中便可以看出上面的 Demo 使用的就是代理模式,

        

        

下面我们还再来具体的谈一下何为虚拟代理?

虚拟代理是作为创建大的对象的代理类,一般来说,创建一个对象需要消耗比较大的资源时,

可以通过虚拟代理来实现这个对象只有在需要的时候才会被真正创建,

可以通过使用代理(这个代理不怎么耗资源)来在必要的时候将被代理的对象加载,

也就是通过不怎么耗资源的代理来控制什么时候需要加载比较耗资源的被代理对象,

在上面的 Demo 中,大的图片就是比较耗资源的,所以通过一个不怎么耗资源的小图片先来代理它,

在真正需要这个大图片的时候再通过代理来加载。

再看一个虚拟代理的应用,那就是当您打开一个有很多图片的网页时,

一般来说,文字是全部可以看到的(会在加载网页的时候全部下载过来),

而图片却必须要一张一张的下载才能看得

(比如您将滑轮往下滚会看到一些图片并没有显示出来,只是在那里留出一个方框,

   而真实的图片就需要等待才能显示),

那些未打开的图片框就是通过虚拟代理来替代了真实的图片,

此时,代理存储了真实图片的路径和大小,所以在也没上才会出现图片的方框,而并没有真实的图像。

在上面浏览器中使用代理模式的好处是什么呢?很明显,通过使用虚拟代理优化了下载。

              

                  

上面呢,重点介绍了代理模式的两种应用,即远程代理和虚拟代理,其实呢,代理模式还有很多变种的

既然是变种的话,基本上就差不太多远啦,

比如安全代理,智能引用代理,缓存代理,写入时复制代理,防火墙代理等等,

在这里就不做一一介绍了,如果有想了解的,可以 Google 或者是 Bing 一下,就会出来一大堆了 ! ! !            

还有就是 TerryLee 的博客上也有一点点介绍的,感兴趣的可以去看看 ! ! !

 

转载于:https://www.cnblogs.com/fcsh820/archive/2010/05/18/1738088.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/423156.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++深度剖析教程31】被遗弃的多重继承

加qq1126137994 微信:liu1126137994 C中是否允许一个类继承多个父类? C支持编写多重继承的代码: 一个子类可以拥有多个父类子类拥有所有父类的成员变量子类继承父类所有的成员函数子类对象可以当做任意父类对象使用 多重继承的语法规则&a…

zookeeper思维导图

之前用word文档记笔记,但是没有思维导图清晰,又整理了一下,分享一下;

redis 思维导图

之前整理的redis 思维导图,分享一下,后续持续更新;

网站切图初学

先做一个简单的说明为什么选择Photoshop软件而没有选择Firework软件。Friework Dreamweaver Flash号称网页制作三剑客,Friework与Dreamwaver整合得更为紧密,在这里只所以选择photoshop只是因为我的偏好,况且我对 firework软件使用的次数很少&…

main函数的参数的含义

转载自:点击链接 链接2 加qq1126137994 微信:liu1126137994 一起学习更多技术!!! 最近学习服务器网络编程,遇到了一个问题,main函数的参数,特意整理资料记录之!&#…

【C++深度剖析教程35】函数模板的概念和意义

加qq1126137994 微信&#xff1a;liu1126137994 一起学习更多技术&#xff01;&#xff01;&#xff01; 1、问题引入&#xff1a; C中有几种变量交换的方法&#xff1f; 定义宏代码块和定义函数 #include <iostream> #include <string>using namespace std;#d…

【C++深度剖析教程36】深入理解函数模板

加qq1126137994 微信&#xff1a;liu1126137994 一起学习更多技术&#xff01;&#xff01;&#xff01; 1、函数模板深入理解 编译器从函数模板通过具体类型产生不同的函数编译器会对函数模板进行两次编译 *对模板进行编译 *对参数替换后的函数进行编译 注意事项&#xf…

【C++深度剖析教程37】类模板的概念和意义

加qq1126137994 微信&#xff1a;liu1126137994 一起学习更多技术&#xff01;&#xff01;&#xff01; 1、类模板 一些类主要用于存储和组织数据元素类中数据的组织方式和数据元素的具体类型无关如 数组类&#xff0c;链表类&#xff0c;stack类&#xff0c;queue类等 C中…

【C++深度剖析教程38】类模板深度剖析

加qq1126137994 微信&#xff1a;liu1126137994 一起学习更多技术&#xff01;&#xff01;&#xff01; 1、多参数类模板 类模板可以定义任意多个不同的类型参数 类模板可以被特化&#xff1a; 指定类模板的特定实现部分类型参数必须显示指定根据类型参数分开实现类模板…

dbeaver 连接hbase 数据库

1.安装dbeaver 参考: https://blog.csdn.net/volitationLong/article/details/80583977 安装dbeaver 2.配置hosts 文件 10.21.21.89 nn01.as 10.21.21.94 nn02.as 10.21.21.93 dn01.as 3.配置用户变量 4. 配置hbase 驱动 连接参数 点击“新建连接”按钮 4.1 选择:a…

docker思维导图

之前学习的总结的思维导图&#xff0c;后续持续更新

ODI配置Mysql5.1数据库服务器

场景&#xff1a;服务器端mysql装了是Mysql5.1版本&#xff0c;最开始使用mysql-connector-java-5.0.6-bin.jar 驱动&#xff0c;经测试&#xff0c;无法连接成功&#xff0c;后来再网络上下载了最新的驱动mysql-connector-java-5.1.12-bin.jar 发现还是没法连接成功。最后面使…

【Linux进程、线程、任务调度】一 Linux进程生命周期 僵尸进程的含义 停止状态与作业控制 内存泄漏的真实含义 task_struct以及task_struct之间的关系

学习交流加&#xff08;可免费帮忙下载CSDN资源&#xff09;&#xff1a;个人微信&#xff1a; liu1126137994学习交流资源分享qq群1&#xff08;已满&#xff09;&#xff1a; 962535112学习交流资源分享qq群2&#xff1a; 780902027 文章目录1、进程控制块PCB2、进程的生命周…

Ubuntu 8.04下Netbeans的字体反锯齿解决(转)

原帖地址&#xff1a;http://www.oklinux.cn/html/Basic/jyjq/20081011/62034.html 测试可用&#xff0c;网上搜的其他方法如复制字体文件等都不成功。 首先说明&#xff0c;我是被锯齿的Netbeans折磨了半年才下决心解决来解决这个问题&#xff0c; 想想还真是冤枉。 我用的Li…

hive工作中分享总结

hive分享总结1. 数据家谱:1.1.Hive 是什么&#xff1f;1.2.数据仓库1.3.Hive与传统数据库的区别1.4.Hive的优缺点1.5.Hive使用场景1.6.Hdfs 运行机制1.7.Mapreduce 运行机制1.8.SQL转化成MapReduce过程1.9.Hive 架构:2.Hive交互方式2.1.Hive交互shell2.2.JDBC交互2.3.第三种交互…

IMX6移植Linux3.0.35内核时需要添加的矩阵按键的平台设备信息

学习交流加 个人qq&#xff1a; 1126137994个人微信&#xff1a; liu1126137994学习交流资源分享qq群&#xff1a; 962535112 本文记录在IMX6开发板上移植鼎芯Linux3.0.35内核时&#xff0c;需要配置的矩阵按键的信息。本设备使用的矩阵按键是一个5*5的矩阵按键。 本人其他开发…

IMX6移植Linux4.1.15内核时需要添加矩阵按键的设备树信息

之前在Linux3.0.35内核移植过按键的信息&#xff0c;请点击链接查看&#xff1a;3.0.35内核移植 本篇文章记录在IMX6开发板移植4.1.15内核时&#xff0c;添加矩阵按键的设备树信息。 1、具体添加的设备树信息 下面是我们的开发板中按键与核心板的引脚链接情况&#xff1a; …

【原创】简单轻松浏览FTP

笔者之前都是用浏览器浏览FTP的&#xff08;笔者用的是360安全浏览器&#xff09;&#xff0c;可后来不知道为什么不能正常浏览了&#xff0c;如下图&#xff1a; 虽然这样也可以下载和浏览FTP上的资源&#xff0c;但总觉得没有像Windows的资源管理器那样来得方便。 上网找了找…

微服务认证解决方案

之前整理的微服务认证文档&#xff0c;分享一下 微服务认证解决方案1.Token认证有两种方式&#xff1a;OAuth2.0&#xff0c;JWT2. oAuth2.0授权方式2.1授权码模式&#xff1a;2.2简化模式:简化模式详细介绍2.3密码模式&#xff1a;密码模式详细介绍2.4客户端模式&#xff1a;2…