代理模式

1. Proxy in the Real World

说到代理,首先想到的是买火车票。比如现在你想买一张票,有好多种方法,简单说三种: 古时候,我们都要去火车站售票厅买,里面的售票员会根据我们的需要,从内部系统中获取一张票卖给你。没有手续费。这里售票窗口可以看成是内部系统的代理。 后来,有了代售点,我们还是把购票需求告诉代售点售票员,然后代售点售票员连接火车站内部系统,获取票给你,但是要收取5块钱手续费。这里代售点可以看成是火车站售票窗口的代理,多了一些操作,中间收取了手续费。 在后来,互联网发达了,有了网上售票代理商,我们填写买票信息,就能拿到票。这里网上代理商也可以看成火车站内部系统的代理。

这些代理有一个共同的特点,那就是都可以售票,就好像他们与车票内部系统都实现了相同的接口。普通人是没有权限直接访问内部系统的,所以,必须经过代理渠道。 而这些代理除了提供访问内部系统的权限外,还会增加一些额外的控制操作。 比如,如果你没身份证,就不允许买票,这是一种权限控制; 比如,代售点需要收取5块手续费; 等。

2.Proxy in GoF

Allows for object level access control by acting as a pass through entity or a placeholder object.

使用代理模式创建代理对象,让代理对象控制目标对象的访问(目标对象可以是远程的对象、创建开销大的对象或需要安全控制的对象),并且可以在不改变目标对象的情况下添加一些额外的功能。

  • Subject:抽象主题角色,抽象主题类可以是抽象类,也可以是接口,是一个最普通的业务类型定义,无特殊要求。
  • RealSubject:具体主题角色,也叫被委托角色、被代理角色。是业务逻辑的具体执行者。
  • Proxy:代理主题角色,也叫委托类、代理类。它把所有抽象主题类定义的方法给具体主题角色实现,并且在具体主题角色处理完毕前后做预处理和善后工作。(最简单的比如打印日志)

3.代理模式的应用形式

  • 远程代理(Remote Proxy) -可以隐藏一个对象存在于不同地址空间的事实。也使得客户端可以访问在远程机器上的对象,远程机器可能具有更好的计算性能与处理速度,可以快速响应并处理客户端请求。
  • 虚拟代理(Virtual Proxy) – 允许内存开销较大的对象在需要的时候创建。只有我们真正需要这个对象的时候才创建。
  • 写入时复制代理(Copy-On-Write Proxy) – 用来控制对象的复制,方法是延迟对象的复制,直到客户真的需要为止。是虚拟代理的一个变体。
  • 保护代理(Protection (Access) Proxy) – 为不同的客户提供不同级别的目标对象访问权限
  • 缓存代理(Cache Proxy) – 为开销大的运算结果提供暂时存储,它允许多个客户共享结果,以减少计算或网络延迟。
  • 防火墙代理(Firewall Proxy) – 控制网络资源的访问,保护主题免于恶意客户的侵害。
  • 同步代理(Synchronization Proxy) – 在多线程的情况下为主题提供安全的访问。
  • 智能引用代理(Smart ReferenceProxy) - 当一个对象被引用时,提供一些额外的操作,比如将对此对象调用的次数记录下来等。
  • 复杂隐藏代理(Complexity HidingProxy) – 用来隐藏一个类的复杂集合的复杂度,并进行访问控制。有时候也称为外观代理(Façade Proxy),这不难理解。复杂隐藏代理和外观模式是不一样的,因为代理控制访问,而外观模式是不一样的,因为代理控制访问,而外观模式只提供另一组接口。

4. 什么时候用

Proxy Pattern Tutorial with Java Examples

  • The object being represented is external to the system.
  • Objects need to be created on demand.
  • Access control for the original object is required
  • Added functionality is required when an object is accessed.

通常,在项目中我们会把代价高的第三方调用(比如远程调用)包装到代理中。代理可以缓存数据,控制调用次数。 代理在访问大文件和图时也很有用,可以延迟加载,缓存。

5. 代码实现

先看一种普遍写法:

/**
 * 抽象主题,定义主要功能
 */
public interface Subject {
   void operate();
}

/**
 * 具体主题
 */
public class RealSubject implements Subject{
 
   @Override
   public void operate() {
        System.out.println("realsubject operate started......");
   }
}
/**
 * 代理类
 */
publicclass Proxy implements Subject{
 
   private Subject subject;
 
   public Proxy(Subject subject) {
        this.subject = subject;
   }
 
   @Override
   publicvoid operate() {
        System.out.println("before operate......");
        subject.operate();
        System.out.println("after operate......");
   }
}

/**
 * 客户
 */
public class Client {
   public static void main(String[] args) {
        Subject subject = new RealSubject();
        Proxy proxy = new Proxy(subject);
        proxy.operate();
   }
}

这种实现方式可以体现一些代理的特性,但是,有些地方写的不好。client构造RealSubject对象,再通过RealSubject对象构造ProxySubject对象,这种逻辑不复合代理模式思想。代理模式的思想是代理对象控制对真实对象的访问,如果已经把真实对象暴露给外部就没有必要使用代理模式了,我觉得应该改造下,代理对象持用的真实对象的引用不应该通过构造方法传入,可以考虑通过工厂模式创建或者Spring注入的方式比较合理。

再看另一个例子,实现一个读取文件的代理: Design Patterns - Proxy Pattern

public interface Image {
   void display();
}

public class RealImage implements Image {

   private String fileName;

   public RealImage(String fileName){
      this.fileName = fileName;
      loadFromDisk(fileName);
   }

   @Override
   public void display() {
      System.out.println("Displaying " + fileName);
   }

   private void loadFromDisk(String fileName){
      System.out.println("Loading " + fileName);
   }
}

public class ProxyImage implements Image{

   private RealImage realImage;
   private String fileName;

   public ProxyImage(String fileName){
      this.fileName = fileName;
   }

   @Override
   public void display() {
      if(realImage == null){
         realImage = new RealImage(fileName);
      }
      realImage.display();
   }
}

public class ProxyPatternDemo {
	
   public static void main(String[] args) {
      Image image = new ProxyImage("test_10mb.jpg");

      //image will be loaded from disk
      image.display(); 
      System.out.println("");
      
      //image will not be loaded from disk
      image.display(); 	
   }
}

输出结果:

Loading test_10mb.jpg
Displaying test_10mb.jpg

Displaying test_10mb.jpg

ProxyImage代理用来减少RealImage对象加载时的内存访问代价。

转载于:https://my.oschina.net/damon4u/blog/750706

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值