美文网首页
代理模式

代理模式

作者: wuchao226 | 来源:发表于2021-06-23 14:34 被阅读0次

    代理模式定义

    为其它对象提供一种代理以控制对这个对象的访问。

    代理模式使用场景

    当无法或不想直接访问某个对象或访问某个对象存在困难时可以通过一个代理对象来间接访问,为保证客户端使用的透明性,委托对象与代理对象需要实现相同的接口。

    代理模式的 UML 类图

    /**
     * 抽象主题类
     */
    public abstract class Subject {
        /**
         * 一个普通的业务方法
         */
        public abstract void request();
    }
    
    /**
     * 实现抽象主题的真实主题类
     */
    public class RealSubject extends Subject{
        @Override
        public void request() {
            // RealSubject 中 request 的具体逻辑
            System.out.println("Real  Subject!");
        }
    }
    
    /**
     * 代理类
     */
    public class ProxySubject extends Subject {
        // 持有真实主题的引用
        private RealSubject mRealSubject;
    
        @Override
        public void request() {
            if (mRealSubject == null) {
                mRealSubject = new RealSubject();
            }
            preRequest();
            // 通过真实主题引用的对象调用真实主题中的逻辑方法
            mRealSubject.request();
            postRequest();
        }
    
        public void preRequest() {
            System.out.println("访问真实主题之前的预处理。");
        }
        public void postRequest() {
            System.out.println("访问真实主题之后的后续处理。");
        }
    }
    
    public class Client {
        public static void main(String[] args) {
            // 通过真实对象构造一个代理对象
            ProxySubject proxySubject = new ProxySubject();
            // 调用代理相关方法
            proxySubject.request();
        }
    }
    

    程序运行的结果如下:

    访问真实主题之前的预处理。
    访问真实主题方法...
    访问真实主题之后的后续处理。
    

    代理模式的主要角色如下。

    • 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
    • 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
    • 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。

    代理模式的简单实现

    小明通过法律途径解决公司拖欠问题,需要请律师作为代理人,将诉讼流程抽象在一个接口类中。

    /**
     * 诉讼接口类
     */
    public interface ILawsuit {
        /**
         * 提交申请
         */
        void submit();
    
        /**
         * 进行举证
         */
        void burden();
    
        /**
         * 开始辩护
         */
        void defend();
    
        /**
         * 诉讼完成
         */
        void finish();
    }
    
    /**
     * 具体诉讼人
     */
    public class XiaoMin 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("诉讼成功");
        }
    }
    

    该类实现 ILawsuit 并对其中四个方法做出具体的逻辑实现。

    /**
     * 代理律师
     */
    public class Lawyer implements ILawsuit {
        // 持有一个具体被代理者的引用
        private ILawsuit mILawsuit;
    
        public Lawyer(ILawsuit iLawsuit) {
            this.mILawsuit = iLawsuit;
        }
    
        @Override
        public void submit() {
            mILawsuit.submit();
        }
    
        @Override
        public void burden() {
            mILawsuit.burden();
        }
    
        @Override
        public void defend() {
            mILawsuit.defend();
        }
    
        @Override
        public void finish() {
            mILawsuit.finish();
        }
    }
    
    public class Client {
        public static void main(String[] args) {
            XiaoMin xiaoMin = new XiaoMin();
            Lawyer lawyer = new Lawyer(xiaoMin);
            lawyer.submit();
            lawyer.burden();
            lawyer.defend();
            lawyer.finish();
        }
    }
    

    程序运行的结果如下:

    拖欠工资,申请仲裁
    证据充足
    辩护
    诉讼成功
    

    代理模式主要还是一种委托机制,真实对象将方法的执行委托给代理对象。代理类也可以代理多个被代理类,而具体代理的是哪个,要看代理类中所持有的实际对象类型。

    比如代理“小慧”,只要在定义一个 XiaoHui 实现 ILawsuit 类即可,再在客户类中修改高层模块调用逻辑。

    代理模式分为两部分,一是静态代理,二是动态代理
    静态代理如上诉示例,代理者的代码由程序员自己或通过一些自动化工具生成固定的代码在对其进行编译,也就是说代码运行前代理类的 class 编译文件就已经存在;
    动态代理与静态代理相反,通过反射机制动态的生产代理者的对象,也就是说在 code 阶段就不需要知道代理谁,将会在执行阶段决定代理谁。Java 提供了一个便捷的动态代理接口 InvocationHandler ,实现该接口需要重写其调用方法 invoke

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

    主要通过 invoke 方法调用具体的被代理方法,也就是真实的方法。

    public class DynamicProxy implements InvocationHandler {
        // 被代理类的引用
        private Object obj;
    
        public DynamicProxy(Object obj) {
            this.obj = obj;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 调用被代理对象的方法
            Object result = method.invoke(obj, args);
            return result;
        }
    }
    

    声明一个 Object 类的引用,该引用将指向被代理类,调用代理类的具体方法则在 invoke 方法中执行。

    // 修改后的客户类
    public class Client {
        public static void main(String[] args) {
            ILawsuit xiaoMin = new XiaoMin();
            // 构造一个动态代理
            DynamicProxy dynamicProxy = new DynamicProxy(xiaoMin);
            // 获取被代理类 xiaoMin 的 ClassLoader
            ClassLoader classLoader = xiaoMin.getClass().getClassLoader();
            // 动态构造一个代理者
            ILawsuit lawyer = (ILawsuit) Proxy.newProxyInstance(classLoader, new Class[]{ILawsuit.class}, dynamicProxy);
            lawyer.submit();
            lawyer.burden();
            lawyer.defend();
            lawyer.finish();
        }
    }
    

    程序运行的结果如下:

    拖欠工资,申请仲裁
    证据充足
    辩护
    诉讼成功
    

    动态代理通过一个代理类来代理 N 多个被代理类,其实质是对代理者与被代理者进行解耦,使两者没有直接的耦合关系。静态代理只能为给定接口下的实现类做代理,如果接口不同需要重新定义不同的代理类。

    相关文章

      网友评论

          本文标题:代理模式

          本文链接:https://www.haomeiwen.com/subject/zhdmyltx.html