Java设计模式之——代理模式

本文介绍了代理模式的基本概念及其应用场景,通过示例详细展示了静态代理和动态代理的实现方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

代理模式简单介绍

代理模式也称为委托模式,是结构型设计模式之一。那么何为代理呢?其实代理在我们日常生活中也并不少见,对于程序员来说最常接触的莫过于代理上网了,连上代理服务器地址,就可以轻松畅游全世界的网络;还有每天吃饭时赶进度是常事,叫公司的同时帮忙买饭也是一种代理等等…总而言之,也许你并不留意,但是代理的确是无处不在,现实生活中如此,我们的 Code 世界里也是如此!既然如此,我们来深究一下代理模式到底有多普遍。首先还是来看一下它的定义。

代理模式的定义

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

代理模式的使用场景

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

代理模式的 UML 类图

这里写图片描述

角色介绍:

  • Subject:抽象主题类,该类的主要职责是声明真实主题与代理的共同接口方法,该类既可以是一个抽象类也可以是一个接口。
  • RealSubject:真实主题类,该类也称为被委托类或被代理类,该类定义了代理所表示的真实对象,由其执行具体的业务逻辑方法,而客户类则通过代理类间接地调用真实主题类中定义的方法。
  • ProxySubject:代理类,该类也称为委托类或代理类,该类持有一个对真实主题类的引用,在其所实现的接口方法中调用真实主题类中相应的接口方法执行,以此起到代理的作用。
  • Client:客户类,即使用代理类的类型。

根据类图可以得出如下一个代理模式的通用模式代码:

/**
 * 抽象主题类
 */
public abstract class Subject {
    /**
     * 一个普通的业务方法
     */
    public abstract void visit();
}

/**
 * 实现抽象主题的真实主题类,也就是被代理类
 */
public class RealSubject extends Subject {
    @Override
    public void visit() {
        //RealSubject 中 visit 的具体逻辑
        Log.d("Subject", "Real subject!");
    }
}


/**
 * 代理类
 */
public class ProxySubject extends Subject {
    private RealSubject mSubject;

    public ProxySubject(RealSubject subject) {
        this.mSubject = subject;
    }

    @Override
    public void visit() {
        //通过真实主题引用的对象调用真实主题中的逻辑方法
        mSubject.visit();
    }
}


/**
 * 客户类
 */
public class Client {
    public static void main() {
        //构造一个真实主题对象
        RealSubject real = new RealSubject();

        //通过真实主题对象构造一个代理对象
        ProxySubject proxy = new ProxySubject(real);

        //调用代理的相关方法
        proxy.visit();
    }
}

代理模式实战

代理模式就如咱们开头所说,你可以在日常生活中找到很多很多,比如:小民以前在公司上班时,就遇到过被老板拖欠工资甚至克扣工资的情况,这种情况下小民还是通过法律的途径来解决问题,一旦小民选择了走法律途径解决该纠纷,那么不可避免地就需要请一个律师类作为自己的诉讼代理人,我们将诉讼的流程抽象在一个接口类中。

/**
 * 诉讼接口类
 */
public interface ILawsuit {
    //提交申请
    void submit();

    //进行举证
    void burden();

    //开始辩护
    void defend();

    //诉讼完成
    void finish();
}

4 个方法非常简单,都是诉讼的一般流程。

/**
 * 具体诉讼人
 */
public class XiaoMin implements ILawsuit {
    @Override
    public void submit() {
        //老板欠小民工资,小民只好申请仲裁
        Log.d("ILawsuit", "老板拖欠工资,特此申请仲裁!");
    }

    @Override
    public void burden() {
        //小民证据充足,不怕告不赢
        Log.d("ILawsuit", "这是合同书和过去一年的银行工资流水!");
    }

    @Override
    public void defend() {
        //铁证如山,变化也没有什么好说的
        Log.d("ILawsuit", "证据确凿,不需要再说什么了");
    }

    @Override
    public void finish() {
        //结果也是肯定的,必赢
        Log.d("ILawsuit", "诉讼成功,判决老板即日起七天内结算工资");
    }
}

如上所诉,该类实现 ILawsuit 并对其中 4 个方法做出具体的实现逻辑,逻辑很简单,都只是输出一段话而已,当然,小民自己是不会去打官司的,于是小民请个律师代替自己进行诉讼。

public class Lawyer implements ILawsuit {

    private ILawsuit mLawsuit;      //持有一个具体被代理者的引用

    public Lawyer(ILawsuit lawsuit) {
        this.mLawsuit = lawsuit;
    }

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

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

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

    @Override
    public void finish() {
        mLawsuit.finish();
    }
}

律师类表示代理者律师,在该类里面会持有一个被代理者的引用,律师所执行的方法实质就是简单地调用被代理者中的方法,下面来看看客户类中具体的调用执行关系。

public class Client {
    public static void main() {
        //构造一个小民
        ILawsuit xiaomin = new XiaoMin();

        //构造一个代理律师并将小民作为构造参数传递进去
        ILawsuit lawyer = new Lawyer(xiaomin);


        //律师提交诉讼申请
        lawyer.submit();

        //律师进行举证
        lawyer.burden();

        //律师代替小民进行辩护
        lawyer.defend();

        //完成诉讼
        lawyer.finish();
    }
}

运行结果这里不在给出,大家可以看到,其实代理模式也很简单,其主要还是一种委托机制,真实对象将方法的执行委托给代理对象,而且委托的干净利落毫不做作,这也是为什么代理模式也称为委托模式的原因,相信大家不难理解。除此之外,大姐可以继续发散思维,其实我们的代理类完全可以代理多个被代理类,就像上面的例子一样,一个律师可以代理多个人大官司,这里是没有任何问题的,而具体到底是代理的哪个人,这就要看代理类中所持有的实际对象类型,上述的例子中实际对象是 XiaoMin,也就是代理的 XiaoMin.

代理其他人可以么?当然可以,比如小辉,我们只需要在定义一个 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 方法中执行,是不是很简洁?也就是说我们原来由代理类所做的工作现在由 InvocationHandler 来处理,不在需要关系到底代理谁。下面我们修改客户类的逻辑。

public class Client {
    public static void main(){
        //构造一个小民
        ILawsuit xiaomin = new XiaoMin();

        //构造一个动态代理
        DynamicProxy proxy = new DynamicProxy(xiaomin);

        //获取被代理类小民的 ClassLoader
        ClassLoader classLoader = xiaomin.getClass().getClassLoader();

        //动态构造一个代理者律师
        ILawsuit lawyer = (ILawsuit) Proxy.newProxyInstance(classLoader,new Class[]{ILawsuit.class},proxy);

        //律师提交诉讼申请
        lawyer.submit();

        //律师进行举证
        lawyer.burden();

        //律师代替小民进行辩护
        lawyer.defend();

        //完成诉讼
        lawyer.finish();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值