代理模式

代理模式又称为委托模式。代理模式从字面上很好理解,有些事情你可以不必亲自去做,而是通过更为专业的人或者机构去做。比如你开公司需要工商注册。你可以选择自己去了解相关的规章制度,亲自跑腿去办理。也可以通过专业机构办理。这样你就不需要去了解办理的细节,只需把材料交给机构。机构会为你完成注册工作。在这个过程中,机构不但会为你完成机构注册,而且还有可能为你办理一些你并不知道的增值服务。这就是典型的代理模式。

1. 实现代理模式

我们再看一个生活中的例子。房屋买卖中经常会出现代理的情况。当卖家不在房屋所在地时,可能会委托自己的亲人或者朋友进行交易。而买方会和代理人直接进行交易。交易中间的问题代理人会回答,手续代理人会办理。如下图:
图片描述
这个代理人不太老实,私自加了20万,想赚差价。所以不要以为代理人真的只是代理,在这个过程中他可以加入自己的逻辑处理。而客户和被代理人并不知道。

我们看看采用代理模式如何实现这个场景。

首先真正卖掉房子的还是房主,只不过和买房人直接进行买卖的是代理人。那么房主和代理人有一个公共的行为都是卖房。那么我们可以抽象出一个接口定义卖房的行为。房主和代理人都需要实现这个接口。真正的卖房逻辑在房主的实现中,代理人的卖房实现只是调用房主的实现而已。要达到这个目的,代理人需要持有房主的引用。而买方进行买卖的时候,仅和代理人打交道。不用知道房主是谁,也不用让房主到现场过户。甚至连房主身在何处都不知道。

上面其实就是这个例子的程序设计。代码如下:

房屋交易接口代码:

public interface RealEstate {
    void sell();
}

房主代码:

public class Seller implements RealEstate {
    @Override
    public void sell() {
        System.out.println("卖了房子");
    }
}

代理人代码:

public class SellerProxy implements RealEstate{
    private Seller seller;
    @Override
    public void sell() {
        if(seller==null){
            seller = new Seller();
        }

        seller.sell();

        System.out.println("退税办理完毕");
    }
}

类图:
图片描述
代理人在这里有什么用处呢?没有代理人,直接和房主买就好了啊?试想下,假如现在有了新的买房政策,交易完成后可以退税,那么在不修改房主代码的前提下,我们只需要修改此代理人的代码即可。如果在其他地方卖房没有此政策,只需要定义另外一个地区的代理人即可,这里实现了开闭原则。其实代理模式还有很多好处和适用的场景。我们下面详细来看。

2. 代理模式优缺点

2.1 优点

代理模式在客户端代码和真正的逻辑代码中引入了一层代理,这样做有很多好处:代理模式

  1. 隐藏逻辑的真正实现对象。上面的例子中,如果卖房人身份特殊,那么通过代理人来卖房,可以不让买房人接触到自己;
  2. 隐藏委托类的某些行为,在代理类认为应该触发时再触发;
  3. 代理类可以为委托类的行为附加一些逻辑处理,例如上例中的退税。

2.2 缺点

  1. 代理类和委托类实现同一个接口,因此只能面向接口代理;
  2. 代理类和委托类实现同一个接口。即使代理类只想代理某个行为,也需要实现接口所有方法;
  3. 代理类和委托类需要一一对应。如果你有段逻辑需要对所有的方法都附加上,静态代理是无法实现的。

3. 代理模式适用场景

针对代理模式的三个优点,我们来看看有哪些适用场景。

  1. 远程调用代理:在分布式系统中,我们经常会调用其他系统的服务。通过代理模式,可以对客户端代码隐藏远程调用的细节;
  2. 虚代理:有一个典型的场景,加载一个包含大量大 size 图片的页面时,为了更好的用户体验,可以通过图片代理类先把图片的位置占好,保证排版的正确。当滚动到某个图片位置的时候才去加载图片;
  3. 保护代理:当委托对象需要访问权限控制时,可以通过代理类来控制权限进行保护;
  4. 智能指引:为委托对象增加一层控制。比如记录访问次数,当为 0 的时候,可以释放掉。第一次引用一个对对象时,把它装入内存。访问委托对象前,检查是否已经有其他访问已经锁定了它,以确保其他对象不能改变它。

4. 小结

代理模式由 Suject 接口,RealSubject 实现和 Proxy 类构成。Proxy 类同样要实现 Suject 接口。同时 Proxy 类依赖 RealSubject 类。代理模式对方的调用增加了间接性。利用间接性,可以加入额外的逻辑。这也是我们常说的 AOP,即面向切面编程。