代理模式(Proxy)

19-08-07 jdon
         

目的

为另一个对象提供代理或占位符以控制对它的访问。

说明

想象一座塔,当地巫师去那里学习他们的法术。象牙塔只能通过代理访问,这确保只有前三个巫师可以进入。这里的代理代表了塔台的功能,并添加了访问控制。

简而言之,使用代理模式,类表示另一个类的功能。

维基百科说

代理以其最一般的形式,是一个类,充当其他东西的接口。代理是一个包装器或代理对象,客户端正在调用它以便在后台访问真正的服务对象。使用代理可以简单地转发到真实对象,或者可以提供额外的逻辑。在代理中,可以提供额外的功能,例如,当对真实对象的操作占用大量资源时进行缓存,或者在调用对真实对象的操作之前检查前提条件。

程序化示例

以我们的巫师塔为例。首先我们有巫师塔接口和象牙塔类

public interface WizardTower {

  void enter(Wizard wizard);
}

public class IvoryTower implements WizardTower {

  private static final Logger LOGGER = LoggerFactory.getLogger(IvoryTower.class);

  public void enter(Wizard wizard) {
    LOGGER.info("{} enters the tower.", wizard);
  }

}

然后是一个简单的向导类

public class Wizard {

  private final String name;

  public Wizard(String name) {
    this.name = name;
  }

  @Override
  public String toString() {
    return name;
  }
}

然后我们有了代理来添加对向导塔的访问控制

public class WizardTowerProxy implements WizardTower {

  private static final Logger LOGGER = LoggerFactory.getLogger(WizardTowerProxy.class);

  private static final int NUM_WIZARDS_ALLOWED = 3;

  private int numWizards;

  private final WizardTower tower;

  public WizardTowerProxy(WizardTower tower) {
    this.tower = tower;
  }

  @Override
  public void enter(Wizard wizard) {
    if (numWizards < NUM_WIZARDS_ALLOWED) {
      tower.enter(wizard);
      numWizards++;
    } else {
      LOGGER.info("{} is not allowed to enter!", wizard);
    }
  }
}

这里是塔楼进入场景

WizardTowerProxy proxy = new WizardTowerProxy(new IvoryTower());
proxy.enter(new Wizard("Red wizard")); // Red wizard enters the tower.
proxy.enter(new Wizard("White wizard")); // White wizard enters the tower.
proxy.enter(new Wizard("Black wizard")); // Black wizard enters the tower.
proxy.enter(new Wizard("Green wizard")); // Green wizard is not allowed to enter!
proxy.enter(new Wizard("Brown wizard")); // Brown wizard is not allowed to enter!

适用场景

当需要一个比简单指针更通用或更复杂的对象引用时,代理就适用。下面是代理模式适用的几种常见情况

  • 远程代理为不同地址空间中的对象提供本地代理。
  • 虚拟代理按需创建昂贵的对象。
  • 保护代理控制对原始对象的访问。当对象具有不同的访问权限时,保护代理很有用。

典型用例

  • 控制对其他对象的访问
  • 延迟初始化
  • 实施日志记录
  • 方便网络连接
  • 计算对对象的引用