代理模式

2019/10/17 posted in  算法

代理模式介绍

代理模式(Proxy Pattern)也称为委托模式,在我们日常生活中也不少见,对于程序员来说最常接触的莫过于代理上网,连上代理服务器地址,就可以轻松畅游全世界的网络;还有每天吃饭时赶进度是常事,叫公司的同事帮忙买饭也是一种代理;如果你碰到辞职老板不给你发工资,那么你还得请个律师帮你打官司,这也是一种代理。总而言之,也许你并不留意,但是代理的确无处不在,现实生活中如此,我们的Code世界也是如此。

代理模式的定义

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

代理模式的使用场景

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

代理模式的简单实现

就以请律师进行诉讼来举例子

诉讼接口类

public interface ILawsuit {
    //提交申请
    void submit();
    
    //进行举证
    void burden();
    
    //开始辩护
    void defend();
    
    //诉讼完成
    void finish();
}

具体诉讼人

public class XiaoTian 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 mLawsuit;  //持有一个具体被代理者的引用
    
    public Lawyer(ILawsuit lawsuit) {
        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(String[] args) {
        //构造一个小天...
        ILawsuit xiaotian = new XiaoTian();
        
        //构造一个代理律师将小天作为构造参数传进去
        ILawsuit lawyer = new Lawyer(xiaotian);
        
        //律师提交诉讼申请
        lawyer.submit();
        
        //律师进行举证
        lawyer.burden();
        
        //律师代理小天进行辩护
        lawyer.defend();
        
        //完成诉讼
        lawyer.finish();
    }
}

上诉例子使用的是静态代理模式,也可以代理多个类,同时代理多个人进行打官司。静态代理,代理者的代码由程序员自己或通过一些自动化工具生成固定的代码在对其进行编译,也就是说在我们的代码运行前代理类的class编译文件就已存在;而动态代理则与静态代理相反,通过反射机制动态地生成代理者的对象,也就是说我们再code阶段压根就不需要知道代理谁,代理谁我们将会在执行阶段决定。而Java也给我们提供了一个便捷的动态代理接口InvocationHandler,实现该接口需要重写其调用方法invoke。

我们主要通过invoke方法来调用具体的被代理方法,也就是真实的方法。动态代理可以使我们的代码逻辑更简洁,不过在这之间我们得首先完善动态代理类。

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

如上代码所述,我们声明一个Object的引用,该引用指向被代理类,而我们调用被代理类的具体方法则在invoke方法中执行。也就是说我们原来由代理类所做的工作现在由InvocationHandler来处理,不再需要关心到底代理谁。

修改后的客户类

public static void main(String[] args) {
    //构造一个小天
    ILawsuit xiaotian = new XiaoTian();
    
    //构建一个动态代理
    DynamicProxy proxy = new DynamicProxy(xiaotian);
    
    //获取被代理类小天的ClassLoader
    ClassLoader loader = xiaotian.getClass().getClassLoader();
    
    //动态构造一个代理律师
    ILawsuit lawyer = (ILawsuit) Proxy.newProxyInstance(loader, new Class[] {ILawsuit.class}, proxy);
    
    //律师提交诉讼申请
    lawyer.submit();
    
    //律师进行举证
    lawyer.burden();
    
    //律师代替小天进行辩护
    lawyer.defend();
    
    //完成诉讼
    lawyer.finish();
}

由此可见动态代理通过一个代理类来代理N个被代理类,其实质是对代理者与被代理者进行解耦,使两者之间没有直接的耦合关系。相对而言静态代理则只能为给定接口下的实现类做代理,如果接口不同那么就需要重新定义不同代理类,较为复杂,但是静态代理更符合面向对象原则。

总结

其他结构型模式中,都可以看到代理模式的影子,有些模式单独作为一种设计模式,倒不如说是对代理模式的一种针对性优化。而且代理模式几乎没有什么缺点可言,它是细分化至很小的一种模式。