zoukankan      html  css  js  c++  java
  • 设计模式(二十一)Proxy模式

      在面向对象编程中,“本人”和“代理人”都是对象。如果“本人”对象太忙了,有些工作无法自己亲自完成,就将其交给“代理人”对象负责。

      示例程序的类图。

      示例程序的时序图。从这个时序图可以看出,直到调用print方法,开始进入实际打印阶段后,PrinterProxy类才会生成Printer类的实例。

    1 package bigjunoba.bjtu.proxy;
    2 
    3 public interface Printable {
    4     public abstract void setPrinterName(String name);
    5     public abstract String getPrinterName();
    6     public abstract void print(String string);
    7 }

      Printable接口用于是PrinterProxy类和Printer类具有一致性。三个方法分别用来设置打印机的名字、获取打印机的名字和显示文字(打印输出)。

     1 package bigjunoba.bjtu.proxy;
     2 
     3 public class Printer implements Printable {
     4     
     5     private String name;
     6     
     7     public Printer() {
     8         heavyJob("正在生成Printer的实例");
     9     }
    10     
    11     public Printer(String name) {
    12         this.name = name;
    13         heavyJob("正在生成Printer的实例(" + name + ")");
    14     }
    15 
    16     private void heavyJob(String msg) {
    17         System.out.println(msg);
    18         for (int i = 1; i < 4; i++) {
    19             try {
    20                 Thread.sleep(1000);
    21             } catch (InterruptedException e) {
    22             }
    23             System.out.println("生成阶段" + i);
    24         }
    25         System.out.println("结束。");
    26     }
    27 
    28     @Override
    29     public void setPrinterName(String name) {
    30         this.name = name;
    31     }
    32 
    33     @Override
    34     public String getPrinterName() {
    35         return name;
    36     }
    37 
    38     @Override
    39     public void print(String string) {
    40         System.out.println("===" + name + "====");
    41         System.out.println(string);
    42     }
    43     
    44 }

      Printer类是表示“本人”的类。在该类的构造函数中,heavyJob方法表示一个干3秒的“重活”,它每秒以一个阶段指示显示工作进度。

     1 package bigjunoba.bjtu.proxy;
     2 
     3 public class PrinterProxy implements Printable {
     4     
     5     private String name;
     6     private Printer real;
     7     
     8     public PrinterProxy() {
     9     }
    10     
    11     public PrinterProxy(String name) {
    12         this.name = name;
    13     }
    14     
    15     @Override
    16     public synchronized void setPrinterName(String name) {
    17         if (real != null) {
    18             real.setPrinterName(name);
    19         }
    20         this.name = name;
    21     }
    22 
    23     @Override
    24     public String getPrinterName() {
    25         return name;
    26     }
    27 
    28     @Override
    29     public void print(String string) {
    30         realize();
    31         real.print(string);
    32     }
    33 
    34     private synchronized void realize() {
    35         if (real == null) {
    36             real = new Printer(name);
    37         }
    38     }
    39     
    40 }

      PrinterProxy类是扮演“代理人”的角色的类,是Proxy模式的核心。name字段保存了打印机的名字,而real字段中保存的是“本人”。

      setPrinterName方法用于设置新的打印机名字。如果real字段不为空,也就是说已经生成了“本人”,那么就会设置“本人”的名字。但是当real字段为空时,也就是说还没有生成“本人”,那么只会设置自己PrinterProxy实例的名字。getPrinterName方法返回自己PrinterProxy实例的名字。

      print方法已经超出了代理人的工作范围,即real字段为空时,也就是没有本人实例因此它会调用realize方法来生成本人。在调用realize方法后,real字段中只会保存本人Printer类的实例,因此可以调用real.print方法。这就是“委托”。

      这里要理解的就是,Printer类不知道PrinterProxy类的存在。即Printer类并不知道自己到底是通过PrinterProxy被调用的还是直接被调用的。

      但是反过来说,PrinterProxy类是知道Printer类的。这是因为PrinterProxy类的real字段是Printer类型的。即PrinterProxy类是与Printer类紧密关联在一起的组件。

      还要注意的是synchronized关键字:

      synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:
      1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
      2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
      3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
      4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。

     1 package bigjunoba.bjtu.proxy;
     2 
     3 public class Main {
     4     public static void main(String[] args) {
     5         Printable printable = new PrinterProxy("LianJiang");
     6         System.out.println("现在的名字是" + printable.getPrinterName() + "。");
     7         printable.setPrinterName("QiaoJiang");
     8         System.out.println("现在的名字是" + printable.getPrinterName() + "。");
     9         printable.print("SB");
    10     }
    11 }

      Main方法很简单,主要就是要验证一下是否只有在调用print方法时才会生成Printer实例。

    现在的名字是LianJiang。
    现在的名字是QiaoJiang。
    正在生成Printer的实例(QiaoJiang)
    生成阶段1
    生成阶段2
    生成阶段3
    结束。
    ===QiaoJiang====
    SB
    

       结果很明显,“代理人”帮助“本人”做了set和get名字的工作,而print工作只有“本人”能做,所以只有让本人来做。

      解释一下Proxy模式的类图:

      1.Subject(主体):定义了使Proxy和RealSubject之间具有一致性的接口。由于存在Subject,因此Client不必在意它所使用的究竟是Proxy还是RealSubject。

      2.Proxy(代理人):尽量处理来自Client的请求,只有当自己不能处理时,才会将工作交给RealSubject。Proxy只有在必要时才会生成RealSubject。

      3.RealSubject(实际的主体):“本人”会在“代理人”无法胜任工作时出场。

      4.Client(请求者):使用Proxy模式。

      Proxy模式的一些应用:

      1.在Proxy模式中,代理人尽力肩负着工作使命。在示例程序中,成功地将耗时处理推迟到print方法被调用才进行。在一个大型系统的初始化过程中,存在大量的耗时处理,如果在启动系统时连那些暂时不会被使用的功能也初始化了,那么应用程序的启动时间会很长,这将会引发用户的不满。只要在有需要的时候将某个功能初始化就可以大大减少启动时间。

      2.代理和委托:当代理人不能解决问题时,就会“转交”给本人去解决,这里的“转交”就是“委托”。

      3.透明性:是指Main类可以完全不用在意调用的是本人类还是代理人类。

      4HTTP代理:HTTP代理是指位于Web服务器和Web浏览器之间,为Web页面提供高速缓存等功能的软件。这也是一种Proxy模式。在通过Web浏览器访问Web页面时,并不会每次都去访问远程Web服务器来获取页面的内容,而是会先去获取HTTP代理缓存的页面。只有当需要最新页面内容或是页面的缓存期限过期时,才去访问远程Web服务器。

      

  • 相关阅读:
    c#中this的一种特殊用法(extension method)
    Use a String.Format format and transform its output to its inputs?
    c#项目中遇到的add event 的一个小例子
    抽象类可以定义常量,接口中不可以定义常量
    c# 浅拷贝与深拷贝
    定制Dictionary
    c#中object字节问题
    编译过程知识的小补习
    抽象耦合
    控件集合属性遇到的问题
  • 原文地址:https://www.cnblogs.com/BigJunOba/p/8806518.html
Copyright © 2011-2022 走看看