设计模式(一):代理模式

1.代理模式

前置知识

1.1什么是代理模式

代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

1.2代理分几种

代理分两种

  1. 静态代理
  2. 动态代理

1.2为什么要用代理模式(代理模式解决了什么问题)?

在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。

以卖房(Subject的request方法)为例,卖房者(RealSubject)想卖房,但是不想搞那么多麻烦事,不想天天招买房的骚扰,就委托中介(ProxySubject)去帮忙卖房,中介帮忙卖房,当然不会简单的卖房,可能还需要收取中介费(preRequest()和postRequest()方法)等等附加收费,最终买房者(客户端Main)买房,找到的是中介,不会与房主接触,与房主接触的是中介,买房者最终跟中介买房,完成操作。

这个是静态代理。真实角色必须是事先已经存在的,并将其作为代理对象的内部属性。但是实际使用时,一个真实角色必须对应一个代理角色,如果大量使用会导致类的急剧膨胀;此外,如果事先并不知道真实角色,该如何使用代理呢?这个问题可以通过Java的动态代理类来解决。

1.3什么情况下用到代理模式?

  • 远程代理:隐藏一个对象存在于不同地址空间的事实;远程机器可能具有更好的计算性能与处理速度,可以快速响应并处理客户端请求。
  • 虚拟代理:是根据需要创建开销很大的对象,通过它来存放实例化需要很长时间的真实对象。这样就可以达到性能的最优化,比如打开一个网页,这个网页里面包含了大量的文字和图片,但我们可以很快看到文字,但是图片却是一张一张地下载后才能看到,那些未打开的图片框,就是通过虚拟代里来替换了真实的图片,此时代理存储了真实图片的路径和尺寸;
  • 安全代理:用来控制真实对象访问时的权限。一般用于对象应该有不同的访问权限的时候;
  • 指针引用:是指当调用真实的对象时,代理处理另外一些事。比如计算真实对象的引用次数,这样当该对象没有引用时,可以自动释放它,或当第一次引用一个持久对象时,将它装入内存,或是在访问一个实际对象前,检查是否已经释放它,以确保其他对象不能改变它。这些都是通过代理在访问一个对象时附加一些内务处理;

-------------------

1.静态代理


  • Subject: 抽象角色,声明真实对象和代理对象的共同接口
  • RealSubject: 真实角色,真实的调用角色
  • Proxy: 代理角色,在执行真实角色之前可以进行其他的附加操作。

代理角色与真实角色都要继承自抽象角色。

下面通过代码实现

Subject.java

/**
 * 抽象角色
 * 抽象方法,只提供方法,不提供具体实现,具体实现由子类提供
 */
public abstract class Subject {
    public abstract void request();
}

RealSubject.java

/**
 * 真实角色
 */
public class RealSubject extends Subject {
    @Override
    public void request() {
        System.out.println("真实的业务");
    }
}

ProxySubject.java

/**
 * 代理角色
 * 调用真实角色的同时可以执行附加操作
 */
public class ProxySubject extends Subject {
    private RealSubject realSubject = new RealSubject();
    @Override
    public void request() {
        preRequest();
        realSubject.request();// 真实角色执行的操作
        postRequest();
    }

    private void preRequest() {
        System.out.println("真实角色执行前的操作");
    }
    private void postRequest() {
        System.out.println("真实角色执行前的操作");
    }
}

执行测试一下

不加代理的情况

public static void main(String[] args) {
    Subject proxySubject = new RealSubject();
    proxySubject.request();
}

执行结果


加上代理

public static void main(String[] args) {
    Subject proxySubject = new ProxySubject();
    proxySubject.request();
}

执行结果


2.动态代理

有了静态代理,为什么还需要动态代理?

如果使用静态代理,那么真实角色必须是事先已经存在的,并将其作为代理对象的内部成员属性。如果一个真实角色必须对应一个代理角色,这将导致系统中的类个数急剧增加,因此需要想办法减少系统中类的个数,此外,如何在事先不知道真实角色的情况下使用代理角色,这都是动态代理需要解决的问题。

Java代码实现

Subject.java

/**
 * 抽象角色
 * 这里必须为接口
 */
public interface Subject {
    void request();
}

RealSubject.java

/**
 * 真实角色
 * 实现接口
 */
public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("真实的业务");
    }
}

DynamicProxy.java

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 动态代理,在运行时生成class,在生成它时必须提供一组interface给它
 * 可以代理Object对象,也就是任何对象
 */
public class DynamicProxy implements InvocationHandler {

    private Object object; //真实对象的引用

    public DynamicProxy(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        preInvoke();
        method.invoke(object, args);// 真实的调用对象
        afterInvoke();
        return null;
    }

    private void preInvoke() {
        System.out.println("调用前做点操作");
    }

    private void afterInvoke() {
        System.out.println("调用后做点操作");
    }
}

下面来测试一下

public class Main {
    public static void main(String[] args) {
        Subject realSubject = new RealSubject();
        InvocationHandler handler = new DynamicProxy(realSubject);

        Subject subject = (Subject) Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler);
        subject.request();
    }

运行结果


猜你喜欢

转载自blog.csdn.net/crankz/article/details/80243027