chapter11_控制对象访问——代理模式

  • 代理: 控制和管理访问

  • 远程代理

    (1) 在两个JVM中, 一个监视器想要远程调用另一个JVM上的对象, 那它先调用__本地__JVM的一个__代理对象__, 然后由代理对象处理所有网络通信的底层细节, 从而调用远程JVM上的对象

    (2) headfirst.designpatterns.proxy.gumball中的使用RMI的示例没有测试通过

  • 代理模式

    为另一个对象提供一个替身或占位符, 以__控制__这个对象的访问

    1° __远程代理__控制访问远程对象

    2° __虚拟代理__控制访问创建开销大的资源

    3° __保护代理__控制访问资源的权限

  • 虚拟代理

    (1) 作为创建开销大的对象的代表, 直到真正需要一个对象的时候再创建这个对象. 在创建前和创建中时, 由虚拟代理来扮演对象的替身

    (2) 示例: 网络加载显示图片, 加载成功前用"请等待"字样代替

      class ImageProxy implements Icon {
    
          ...
    
          private volatile ImageIcon imageIcon;
          private final URL imageURL;
          private boolean retrieving = false;
    
          public void paintIcon(final Component c, Graphics g, int x, int y) {
    
              if (imageIcon != null) {
                  imageIcon.paintIcon(c, g, x, y);
              } else {
    
                  g.drawString("Loading CD cover, please wait...", x + 300, y + 190);
    
                  if (!retrieving) {
    
                      retrieving = true;
    
                      Thread retrievalThread = new Thread(new Runnable() {
    
                          public void run() {
    
                              try {
                                  setImageIcon(new ImageIcon(imageURL, "CD Cover"));
                                  c.repaint();
                              } catch (Exception e) {
                                  e.printStackTrace();
                              }
                          }
                      });
    
                      retrievalThread.start();
                  }
              }
          }
    
          ...
      }
    
  • 代理模式和装饰器模式的区别

    意图不同: 装饰者为对象增加行为, 代理控制对象的访问

    另外, 装饰器可能包装很多层, 代理一般只包了一层

  • 代理模式和适配器模式的区别

    适配器模式会改变接口类型, 代理模式实现相同的接口

  • 如何强制客户使用代理而不是使用真正的对象?

    工厂方法

  • 保护代理

    (1) 保护代理的__作用__是控制权限, 由外面包的一层代理决定对内部对象的访问是否合法

    (2) Java中利用反射支持动态的代理(所以叫__动态代理__), 在java.lang.reflect中

    (3) 示例

    PersonBean.java

      public interface PersonBean {
    
          String getName();
    
          String getGender();
    
          ...
    
          void setName(String name);
    
          void setGender(String gender);
    
          ...
    
          void setHotOrNotRating(int rating);
      }
    

    PersonBean是一个接口, 它提供了一系列的get/set方法, 但是PersonBean的实例对象有时需要加一层代理, 由代理来决定哪些方法可以被访问, 哪些不可以

    TestProxy.java

      public class TestProxy {
    
          ...
    
          public void drive() {
    
              PersonBean joe = getPersonFromDatabase("Joe Javabean");
              PersonBean ownerProxy = getOwnerProxy(joe);
    
              ...
          }
    
          PersonBean getOwnerProxy(PersonBean person) {
    
              return (PersonBean) Proxy.newProxyInstance(person.getClass().getClassLoader(), person.getClass().getInterfaces(), new OwnerInvocationHandler(person));
          }
    
          ...
      }
    

    TestProxy是一个测试类, 在drive()方法中先创建了一个PersonBean对象joe, 但是joe的所有方法不应该全部被访问, 因此要得到joe的一个代理;

    这里使用了java.lang.Proxy中的静态方法newProxyInstance

      public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);
    

    Proxy就是PersonBean对象的代理, 但是当代理的方法被调用时, 内部其实会调用InvocationHandler对象的invoke()方法;

    所以Proxy其实只是一个壳子, 重点要看InvocationHandler的实现类对象

    OwnerInvocationHandler.java

      public class OwnerInvocationHandler implements InvocationHandler {
    
          private PersonBean person;
    
          public OwnerInvocationHandler(PersonBean person) {
    
              this.person = person;
          }
    
          public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException {
    
              try {
    
                  if (method.getName().startsWith("get")) {
                      return method.invoke(person, args);
                  } else if (method.getName().equals("setHotOrNotRating")) {
                      throw new IllegalAccessException();
                  } else if (method.getName().startsWith("set")) {
                      return method.invoke(person, args);
                  }
    
              } catch (InvocationTargetException e) {
                  e.printStackTrace();
              }
    
              return null;
          }
      }
    

    OwnerInvocationHandler实现了InvocationHandler接口的invoke方法。 无论代理被调用的是什么方法, 最终一定会调用invoke();

    Method是一个反射类, 可以获取到代理被调用的方法, 从而做出响应的选择

猜你喜欢

转载自blog.csdn.net/captxb/article/details/87900683