本篇文章来自于设计模式一书中的“代理模式”
当需要将一个复杂的对象或创建的比较花费时间的对象表示成一个简单对象时,可以使用代理模式。如果创建一个对象比较浪费时间或浪费计算机资源,Proxy允许将创建过程推迟到需要该实际对象的时候。Proxy对象通常具有和它所代表的对象一样的方法,一旦对象被调入,就把调用方法从Proxy传递给实际对象。
下列几种情况适合于使用Proxy
1.调入一个对象需要花费很长时间,如调入一个大图像。
2.计算结果需要花费很长时间才能完成,计算过程中需要显示中间结果。
3.对象位于远程机器上,通过网络调入会很慢,特别是在网络负载高峰期间。
4.对象限制了访问权限,代理可以使访问许可对用户有效。
我们考虑这样一个例子,一个程序需要调入一个大图像并显示出来。程序开始运行时,必须对要图像有些说明,这样才能正确布局屏幕,但真正的图像显示可以推迟到图像完全调入之后。这一点在诸如文字处理、Web浏览器程序中是相当重要的,这类程序在图像可用之前,要在图像周围安排文本。
本例中,我们创建一个简单的程序,调入一个图像时,在一个图像控件上显示另一幅图像。不是直接调入该图像,而是使用一个ImageProxy类推迟调入过程并在之前出现一个load的效果,一直到调入完成。
public partial class Main : Form { ImageProxy ImgProxy; public Main() { InitializeComponent(); Init(); } private void Init() { ImgProxy = new ImageProxy(); } private void BtnLoad_Click(object sender, EventArgs e) { pic.Image = ImgProxy.GetImage(); } }
注意,在我们需要一个图像代理时,创建一个ImageProxy实例,ImageProxy类准备图像的调入,并创建了一个Imager对象跟踪调入过程,它是个实现Imager接口的类。
public interface Imager { Image GetImage(); }
在这个简单的例子里,ImageProxy类只延迟了5秒钟,然后从开始的图像切换到最后的图像,它使用一个Timer类的实例完成上述工作。TimerCallback类中定义了定时器到时间时要调用的方法,我们使用该类处理定时器。这与我们添加其他事件处理句柄方式非常类似,方法TimerCall设置Done标志并关闭时钟。
public class ImageProxy { private bool Done { get; set; } private Timer Timer { get; set; } public ImageProxy() { Timer = new Timer(TimerCall, this, 5000, 0); } private void TimerCall(object state) { Done = true; Timer.Dispose(); } public Image GetImage() { Imager img; if (Done) { img = new FinalImage(); } else { img = new QuickImage(); } return img.GetImage(); } }
我们在两个小类里面实现Imager接口,这两个类分别为QuickImage和FinalImage。一个类获取一个小的gif图像,另一个类获取一个较大的(可能较慢)jpeg图像。
public class FinalImage : Imager { public Image GetImage() { return new Bitmap(@"E:\C#设计模式\代理模式\ProxyPattern\ProxyPattern\Imgs\color.jpg"); } } ----------------------------- public class QuickImage : Imager { public Image GetImage() { return new Bitmap(@"E:\C#设计模式\代理模式\ProxyPattern\ProxyPattern\Imgs\load.gif"); } }
取一幅图像时,首先得到的是load图像,5秒钟后,若再次调用该方法,会得到最终的图像。如下图。
C#中的Proxy
C#语言比其他语言有更多的代理行为,例如Ado.net中的数据库连接类实现上全部都是代理模式。
思考
一个连接到数据库的服务器,如果有几个客户终端要同时连接到服务器上,如果使用Proxy?