代理模式

Android知识总结

一、使用场景

  • 当一个对象不能或者不想直接访问另一个对象时,可以通过一个代理对象来间接访问。为保证客户端使用的透明性,委托对象和代理对象要实现同样的接口。
  • 被访问的对象不想暴露全部内容时,可以通过代理去掉不想被访问的内容。
  • Interface: 抽象接口,声明真是主体与代理主题的共同接口方法。
  • RealSubject: 真实主题类,定义了代理所表示的真是对象,执行具体的业务方法。客户端通过代理类来间接的调动这个真实主题中的方法。
  • ProxySubject: 代理类,持有一个真实类的引用,在接口方法中调用真实主题相应的方法,达到代理的作用。

注释:通过代理使client和真实对象隔离,使用户不用操作真实对象只用操作代理类就可达到指定效果。

二、静态代理

public interface ILawsuit {
    void submit();//提交申请
    void burden();//进行举证
    void defend();//开始辩护
    void finish();//诉讼完成
}
public class Civilian implements ILawsuit {
    @Override
    public void submit() {
        System.out.println("起诉");
    }

    @Override
    public void burden() {
        System.out.println("举证");
    }

    @Override
    public void defend() {
        System.out.println("辩护");
    }

    @Override
    public void finish() {
        System.out.println("胜诉");
    }
}
public class Lawyer implements ILawsuit {
    private ILawsuit civilian;

    public Lawyer(ILawsuit civilian) {
        this.civilian = civilian;
    }

    @Override
    public void submit() {
        civilian.submit();
    }

    @Override
    public void burden() {
        civilian.burden();
    }

    @Override
    public void defend() {
        civilian.defend();
    }

    @Override
    public void finish() {
        civilian.finish();
    }
}
public class Client {
    public static void main(String[] args) {
        ILawsuit civilian = new Civilian();
        ILawsuit lawyer = new Lawyer(civilian);
        lawyer.submit();
        lawyer.burden();
        lawyer.defend();
        lawyer.finish();
    }
}

缺点:扩招能力差和可维护性差。违反开闭原则。

三、动态代理

动态代理通过反射动态的生成代理者对象(代理类 ProxySubject),也就是说在写代码的时候根本不知道要代理谁,具体代理谁会在执行阶段决定。

Java提供了一个便捷的动态代理接口InvocationHandler,动态代理类只要实现这个接口就行:

public class DynamicProxy implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return null;
    }
}

看一下动态代理的用法:

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 {
        //当然这里可以对方法名进行判断过滤 if(method.getName().equals("***"))
        Object result = method.invoke(object,args);
        return result;
    }
}

客户端调用:
java

public class Main {
    public static void main(String[] args) {
        ILawsuit lawsuit = new Civilian();
        DynamicProxy proxy = new DynamicProxy(lawsuit);
        ClassLoader loader = lawsuit.getClass().getClassLoader();
        //动态创建代理类,需要传入一个类加载器ClassLoader;一个你希望这个代理实现的接口列表,这里要代理ILawsuit接口;
        //第一个参数是ClassLoader; 第二个参数是生成代理类指定接口的Class数组;第三个参数是InvocationHandler的实现
        ILawsuit lawyer = (ILawsuit) Proxy.newProxyInstance(loader, new Class[]{ILawsuit.class}, proxy);
        lawyer.submit();
        lawyer.burden();
        lawyer.defend();
        lawyer.finish();
    }
}

kotlin

object MainTest{
    @JvmStatic
    fun main(argc : Array){
        val lawsuit = Civilian()
        val messageInterface = Proxy.newProxyInstance(MainTest::class.java.classLoader,
            arrayOf>(ILawsuit ::class.java) 
        ) { _, method, args -> method!!.invoke(lawsuit , *args) } as ILawsuit 
        messageInterface.setName("小米", 13)
    }
}

  • out 和 in 代表 extends 和 super;out T 等价于 ?extends T
    ;in T等价于 ? super T
  • Class< * >:*号等于Java的Class< ? >
  • *args:等于Java中的可变参数(Object... args)。如下代码:
public class CallJavaUtils {

    public static int addNumbers(String name, int... args) {
        int result = 0;
        for (int i = 0; i < args.length; i++) {
            result += args[i];
        }
        return result;
    }
}
//测试Kotlin传递可变长参数给Java可变参数方法
var numbers:IntArray = intArrayOf(1, 2, 3, 4, 5)
CallJavaUtils.addNumbers("add", *numbers)

代理类进一步封装

public class DynamicProxy implements InvocationHandler {
    /*持有的真实对象*/
    private Object factory;

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

    public Object getFactory() {
        return factory;
    }

    public void setFactory(Object factory) {
        this.factory = factory;
    }

    /**
     * 获取代理对象
     *
     * @return
     */
    private Object getProxyInstance() {
        return Proxy.newProxyInstance(factory.getClass().getClassLoader(),
                factory.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        doSthBefore();
        Object invoke = method.invoke(factory, args);
        doSthAfter();
        return invoke;
    }

    /**
     * 后置处理器
     */
    private void doSthAfter() {
        System.out.println("method after do something.");
    }

    /**
     * 前置处理器
     */
    private void doSthBefore() {
        System.out.println("method before do something.");
    }
}

缺点:运用反射技术使执行效率较差。

四、其他分类

静态代理和动态代理是从code方便进行分类的。这两个分类根据适用范围来分都可以分为下面几种:

  • 远程代理:为摸个对象在不同的内存地址空间提供局部代理,是系统Server部分隐藏,以便Client不用考虑Server的存在。
  • 虚拟代理:如果要创建一个资源消耗较大的对象,可以先用一个代理对象表示,在真正需要的时候才真正创建。
  • 保护代理:用代理对象控制对一个对象的访问,给不同的用户提供不同的访问权限。
  • 智能引用:在引用原始对象的时候附加额外操作,并对指向原始对象的引用增加引用计数

你可能感兴趣的:(代理模式)