• 代理模式


    代理模式是一种应用非常广泛的设计模式,当客户端代码需要调用某个对象时,客户端实际上也不关心是否准确得到该对象,它只要一个能提供该功能的对象即可,此时我们就可返回该对象的代理(Proxy)。

    在这种设计方式下,系统会为某个对象提供一个代理对象,并由代理对象控制对源对象的引用。代理就是一个 Java 对象代表另一个 Java 对象来采取行动。在某些情况下,客户端代码不想或不能够直接调用被调用者,代理对象可以在客户和目标对象之间起到中介的作用。

    对客户端而言,它不能分辨出代理对象与真实对象的区别,它也无须分辨代理对象和真实对象的区别。客户端代码并不知道真正的被代理对象,客户端代码面向接口编程,它仅仅持有一个被代理对象的接口。

    总而言之,只要客户端代码不能或不想直接访问被调用对象——这种情况有很多原因,比如需要创建一个系统开销很大的对象,或者被调用对象在远程主机上,或者目标对象的功能还不足以满足需求……,而是额外创建一个代理对象返回给客户端使用,那么这种设计方式就是代理模式。

    下面示范一个简单的代理模式,程序首先提供了一个 Image 接口,代表大图片对象所实现的接口,该接口代码如下:


    清单 3. Image.java

    1  public interface Image 
    2  { 
    3  void show(); 
    4  } 

    该接口提供了一个实现类,该实现类模拟了一个大图片对象,该实现类的构造器使用 Thread.sleep() 方法来暂停 3s。下面是该 BigImage 的程序代码。

    清单 4. BigImage.java

     1 // 使用该 BigImage 模拟一个很大图片
     2  public class BigImage implements Image 
     3  { 
     4  public BigImage() 
     5  { 
     6  try 
     7  { 
     8  // 程序暂停 3s 模式模拟系统开销 
     9                  Thread.sleep(3000); 
    10  System.out.println("图片装载成功 ..."); 
    11  } 
    12  catch (InterruptedException ex) 
    13  { 
    14  ex.printStackTrace(); 
    15  } 
    16  } 
    17  // 实现 Image 里的 show() 方法
    18  public void show() 
    19  { 
    20  System.out.println("绘制实际的大图片"); 
    21  } 
    22  } 

    上面的程序代码暂停了 3s,这表明创建一个 BigImage 对象需要 3s 的时间开销——程序使用这种延迟来模拟装载此图片所导致的系统开销。如果不采用代理模式,当程序中创建 BigImage 时,系统将会产生 3s 的延迟。为了避免这种延迟,程序为 BigImage 对象提供一个代理对象,BigImage 类的代理类如下所示。


    清单 5. ImageProxy.java

     1 public class ImageProxy implements Image 
     2  { 
     3  // 组合一个 image 实例,作为被代理的对象
     4  private Image image; 
     5  // 使用抽象实体来初始化代理对象
     6  public ImageProxy(Image image) 
     7  { 
     8  this.image = image; 
     9  } 
    10  /** 
    11  * 重写 Image 接口的 show() 方法
    12  * 该方法用于控制对被代理对象的访问,
    13  * 并根据需要负责创建和删除被代理对象
    14  */ 
    15  public void show() 
    16  { 
    17  // 只有当真正需要调用 image 的 show 方法时才创建被代理对象
    18  if (image == null) 
    19  { 
    20   image = new BigImage(); 
    21   } 
    22  image.show(); 
    23  } 
    24  } 

    上面的 ImageProxy 代理类实现了与 BigImage 相同的 show() 方法,这使得客户端代码获取到该代理对象之后,可以将该代理对象当成 BigImage 来使用。

    在 ImageProxy 类的 show() 方法中增加了控制逻辑,这段控制逻辑用于控制当系统真正调用 image 的 show() 时,才会真正创建被代理的 BigImage 对象。下面程序需要使用 BigImage 对象,但程序并不是直接返回 BigImage 实例,而是先返回 BigImage 的代理对象,如下面程序所示。


    清单 6. BigImageTest.java

     1 public class BigImageTest 
     2  { 
     3  public static void main(String[] args) 
     4  { 
     5  long start = System.currentTimeMillis(); 
     6  // 程序返回一个 Image 对象,该对象只是 BigImage 的代理对象
     7  Image image = new ImageProxy(null); 
     8  System.out.println("系统得到 Image 对象的时间开销 :" + 
     9  (System.currentTimeMillis() - start)); 
    10  // 只有当实际调用 image 代理的 show() 方法时,程序才会真正创建被代理对象。
    11  image.show(); 
    12  } 
    13  } 

    上面程序初始化 image 非常快,因为程序并未真正创建 BigImage 对象,只是得到了 ImageProxy 代理对象——直到程序调用 image.show() 方法时,程序需要真正调用 BigImage 对象的 show() 方法,程序此时才真正创建 BigImage 对象。运行上面程序,看到如图 6 所示的结果。


    图 6. 使用代理模式提高性能

     

    看到如图 6 所示的运行结果,读者应该能认同:使用代理模式提高了获取 Image 对象的系统性能。但可能有读者会提出疑问:程序调用 ImageProxy 对象的 show() 方法时一样需要创建 BigImage 对象啊,系统开销并未真正减少啊?只是这种系统开销延迟了而已啊?

    我们可以从如下两个角度来回答这个问题:

    • 把创建 BigImage 推迟到真正需要它时才创建,这样能保证前面程序运行的流畅性,而且能减少 BigImage 在内存中的存活时间,从宏观上节省了系统的内存开销。
    • 有些情况下,也许程序永远不会真正调用 ImageProxy 对象的 show() 方法——意味着系统根本无须创建 BigImage 对象。在这种情形下,使用代理模式可以显著地提高系统运行性能。

    与此完全类似的是,Hibernate 也是通过代理模式来“推迟”加载关联实体的时间,如果程序并不需要访问关联实体,那程序就不会去抓取关联实体了,这样既可以节省系统的内存开销,也可以缩短 Hibernate 加载实体的时间。

     

    小结

    Hibernate 的延迟加载(lazy load)本质上就是代理模式的应用,我们在过去的岁月里就经常通过代理模式来降低系统的内存开销、提升应用的运行性能。Hibernate 充分利用了代理模式的这种优势,并结合了 Javassist 或 CGLIB 来动态地生成代理对象,这更加增加了代理模式的灵活性,Hibernate 给这种用法一个新名称:延迟加载。无论怎样,充分分析、了解这些开源框架的实现可以更好的感受经典设计模式的优势所在。

     

     

     

  • 相关阅读:
    有点忙啊
    什么是协程
    HDU 1110 Equipment Box (判断一个大矩形里面能不能放小矩形)
    HDU 1155 Bungee Jumping(物理题,动能公式,弹性势能公式,重力势能公式)
    HDU 1210 Eddy's 洗牌问题(找规律,数学)
    HDU1214 圆桌会议(找规律,数学)
    HDU1215 七夕节(模拟 数学)
    HDU 1216 Assistance Required(暴力打表)
    HDU 1220 Cube(数学,找规律)
    HDU 1221 Rectangle and Circle(判断圆和矩形是不是相交)
  • 原文地址:https://www.cnblogs.com/jdbc/p/2629166.html
Copyright © 2020-2023  润新知