Proxy(代理)模式

Proxy(代理)模式

        普通对象所需要完成的任务是通过公共接口为外界提供自己所承诺的服务。然而,有时候合法的对象可能会因为各种原因而无法完成自己常规的任务。尤其是当对象需要很长时间才能载入内存、对象正运行在另一台计算机上或者需要获取对象消息的时候,这种情况就会频繁出现。在这种情况下,我们可以使用一个代理对象,由它来承担客户所期待的责任,并将请求转发给其背后的目标对象。

      Proxy模式的意图在于为对象提供一个代理或者占位(placeholder)来控制对该对象的访问。

 

1.  经典范例:图像代理

     代理对象通常拥有与实际对象几乎相同的接口。代理的工作方式是:把服务请求明智地转发给代理控制的底层对象,最终完成服务。避免将较大的图像加载到内存就是典型的使用Proxy模式的例子。我们假定某个应用程序中的图像属于不执行初始化显示的页面或者面板。为了避免在不需要所有图像之前载入它们,我们需要为这些图像建立代理,以便在需要时加载所请求的图像。本节提供一个图像代理例子。但是要注意,使用Proxy模式的设计有时候是非常脆弱的,这是由于它们依赖转发方法来调用其底层对象。转发可能会建立一个非常脆弱并且需要经常维护的设计。

     假定Oozinoz公司的工程师在使用图像代理时,出于性能上的考虑,在载入较大的图像时显示了一个小的临时图像。这位工程师使用了一个原型(请参看程序运行结果)。用于实现该应用程序的代码在app.proxy包的ShowProxy类中。下面的代码将假定该应用程序位于com.oozinoz.imaging包中。

     图形用户界面分别在不同阶段显示这三个视图:一个视图用于表明还未开始加载图像,一个视图用于表明正在加载图像,还有一个视图用于显示实际图像。整个过程如下:当应用程序启动的时候,显示“Absent“图像,这是事先用图像处理工具做好的JPEG图像;当用户单击Load按钮的时候,立即显示我们事先准备的另一张图像"Load...";当图像加载完成之后,显示该图像。

     显示JPEG图像最简单的做法就是:创建一个ImageIcon对象,然后将它作为参数实例化一个标签类。代码如下所示:

ImageIcon icon = new ImageIcon("images/fest.jpg"); 
JLabel label = new JLabel(icon); 

 

     在构建应用程序时,我们要创建一个用它实例化JLabel类的代理,并用它转发绘制请求以(1)显示"Absent"图像,(2)显示"loading"图像,或者(3)显示所请求的图像,下图所示的顺序图给出了这个过程的消息流。

 


      
                         ImageIconProxy对象将paint()请求转发给当前的ImageIcon对象 

   当用户单击Load按钮时,ImageIconProxy对象将当前图像改为"loading..."图像,与此同时,该代理对象开始加载指定的图像。当指定图像加载完成后,ImageIconProxy对象将当前图像改为该指定图像。

   如下图所示,我们可以通过创建ImageIcon类的子类来创建代理类。ImageIconProxy类的代码定义了两个静态变量,分别引用了"Absent"图像和"Loading..."图像。

Java代码 复制代码
static final ImageIcon ABSENT = new ImageIcon(
             ClassLoader.getSystemResource("images/absent.jpg"));
static final ImageIcon LOADING = new ImageIcon(
             ClassLoader.getSystemResource("Images/loading.jpg"));



      由于ImageIconProxy对象扩展了ImageIcon对象,因而它可以代理ImageIcon对象

   ImageIconProxy类的构造器以最终要显示的图像文件名为参数。当调用ImageIconProxy对象的load()方法的时候,该方法将当前图像设置成"loading..."图像,并启动一个单独的线程来加载该图像。使用单独的线程可以保证在加载图像的时候应用程序不会被挂起。load()方法以JFrame类对象为参数,当指定图像加载完毕后,就会返回run()方法。ImageIconProxy.java中的代码如下所示:

Java代码 复制代码
package com.oozinoz.imaging;
import java.awt.*;
import javax.swing.*;

public class ImageIconProxy extends ImageIcon implements Runnable
{
     static final ImageIcon ABSENT = new ImageIcon(ClassLoader.getSystemResource("images/absend.jpg"));
     static final ImageIcon LOADING = new ImageIcon(ClassLoader.getSystemResource("images/loading.jpg"));
     ImageIcon current = ABSENT;
     protected String filename;
     protected JFrame callbackFrame;

     public ImageIconProxy(String filename)
     {
          super(ABSENT.getImage());
          this.filename = filename;
     }
    
     public void load(JFrame callbackFrame)
     {
          this.callbackFrame = callbackFrame;
          current = LOADING;
          callbackFrame.repaint();
          new Thread(this).start();
     }
 
     public void run()
     {
          current = new ImageIcon(ClassLaoder.getSystemResource(filename));
          callbackFrame.pack();
     }

     public int getIconHeight()
     {
          return current.getIconHeight();
     }

     public int getIconWidth()
     {
          return current.getIconWidth();
     }

     public synchronized void paintIcon()
     {
          current.paintIcon(c,g,x,y);
     }
}

突破题:ImageIconProxy对象将在三个有关图像显示的调用转发给当前对象。请写出ImageIconProxy类中getIconHeight()方法、getIconHeight()方法以及paintIcon()方法的代码。

 

     假设我们的这个小型演示程序可以运行。实际的应用程序只能会有多个按钮,不过在实际构建这个应用程序之前,我们有必要举行一次设计评审会。只有这样,设计中的脆弱之处才会显露出来。

     

突破题:ImageIconProxy类并不是一个设计良好的、可复用的组件。请指出该设计存在的两个问题。

答:本设计存在问题包括:

    (1)只把部分调用转发给底层的ImageIcon对象是很危险的。ImageIconProxy类从ImageIcon类继承很多字段和大约25个方法。为成为真正的代理,ImageIconProxy对象需要转发大多数或者全部调用。彻底的调用转发需要很多潜在存在问题的方法,并且需要这部分代码在ImageIcon类及其超类变化时也要做相应调整。

    (2)你也许疑惑空白图片和所需图片是否到位。最好把这些图片直接传递进来,不要让类查找它们。

 

    当评审他人的设计时,我们必须同时形成对该设计的理解和自己对该设计的看法。当开发者正在使用某种特定的设计模式时,我们可能会对是否应使用该设计模式有不同看法。在这个例子中,Proxy模式的使用很明显,但是这并不说明该设计就是合理的;实际上还存在更好的设计。当设计中使用Proxy模式时,应该有充分理由,这是因为转发可能会带来一系列其他设计所能够避免的问题。下一节会重新思考Proxy模式到底是否是明智的选择。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值