likes
comments
collection
share

设计模式之代理模式

作者站长头像
站长
· 阅读数 16

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

设计模式之代理模式 上图中,Subject是一个抽象类或者接口,RealSubject是实现方法类,具体的业务执行,Proxy则是RealSubject的代理,直接和client接触的。

代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。值得注意的是,代理类和被代理类应该共同实现一个接口,或者是共同继承某个类。

代理模式的优点

  • 职责清晰
  • 高扩展,只要实现了接口,都可以用代理
  • 智能化,动态代理

分类

1、 静态代理

以租房为例,我们一般用租房软件、找中介或者找房东。这里的中介就是代理者。

首先定义一个提供了租房方法的接口。

public interface IRentHouse {  
    void rentHouse();  
}  

定义租房的实现类

public class RentHouse implements IRentHouse {  
    @Override  
    public void rentHouse() {  
        System.out.println("租了一间房子。。。");  
    }  
}  

我要租房,房源都在中介手中,所以找中介

public class IntermediaryProxy implements IRentHouse {  
  
    private IRentHouse rentHouse;  

    public IntermediaryProxy(IRentHouse irentHouse){  
        rentHouse = irentHouse;  
    }  

    @Override  
    public void rentHouse() {  
        System.out.println("交中介费");  
        rentHouse.rentHouse();  
        System.out.println("中介负责维修管理");  
    }  
}  

这里中介也实现了租房的接口。

在main方法中测试:

public class Main {  
    public static void main(String[] args){  
        //定义租房  
        IRentHouse rentHouse = new RentHouse();  
        //定义中介  
        IRentHouse intermediary = new IntermediaryProxy(rentHouse);  
        //中介租房  
        intermediary.rentHouse();  
    }  
}  

返回信息

交中介费  
租了一间房子。。。  
中介负责维修管理  

上面例子就是使用的静态代理方式,因为中介这个代理类已经事先写好了,只负责代理租房业务

2、强制代理

如果我们直接找房东要租房,房东会说我把房子委托给中介了,你找中介去租吧。这样我们就又要交一部分中介费了,真坑。

来看代码如何实现,定义一个租房接口,增加一个方法。

public interface IRentHouse {  
    void rentHouse();  
    IRentHouse getProxy();  
}  

这时中介的方法也稍微做一下修改

public class IntermediaryProxy implements IRentHouse {  
  
    private IRentHouse rentHouse;  

    public IntermediaryProxy(IRentHouse irentHouse){  
        rentHouse = irentHouse;  
    }  

    @Override  
    public void rentHouse() {  
        rentHouse.rentHouse();  
    }  

    @Override  
    public IRentHouse getProxy() {  
        return this;  
    }  
}  

其中的getProxy()方法返回中介的代理类对象

我们再来看房东是如何实现租房:

public class LandLord implements IRentHouse {  
  
    private IRentHouse iRentHouse = null;  

    @Override  
    public void rentHouse() {  
        if (isProxy()){  
            System.out.println("租了一间房子。。。");  
        }else {  
            System.out.println("请找中介");  
        }  
    }  

    @Override  
    public IRentHouse getProxy() {  
        iRentHouse = new IntermediaryProxy(this);  
        return iRentHouse;  
    }  

    /**  
    * 校验是否是代理访问  
    * @return  
    */  
    private boolean isProxy(){  
        if(this.iRentHouse == null){  
            return false;  
        }else{  
            return true;  
        }  
    }  
}  

房东的getProxy方法返回的是代理类,然后判断租房方法的调用者是否是中介,不是中介就不租房。

main方法测试:

public static void main(String[] args){  
    IRentHouse iRentHosue = new LandLord();  
    //租客找房东租房  
    iRentHouse.rentHouse();  
    //找中介租房  
    IRentHouse rentHouse = iRentHouse.getProxy();  
    rentHouse.rentHouse();  
}  
//下面是打印结果:  
请找中介  
租了一间房子。。。  

这样就是强制你使用代理,如果不是代理就没法访问。

3、动态代理

我们知道现在的中介不仅仅是有租房业务,同时还有卖房、家政、维修等得业务,只是我们就不能对每一个业务都增加一个代理,就要提供通用的代理方法,这就要通过动态代理来实现了。

中介的代理方法做了一下修改:

public class IntermediaryProxy implements InvocationHandler {  
  
    private Object obj;  

    public IntermediaryProxy(Object object){  
        obj = object;  
    }  

    /**  
    * 调用被代理的方法  
    * @param proxy  
    * @param method  
    * @param args  
    * @return  
    * @throws Throwable  
    */  
    @Override  
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
        Object result = method.invoke(this.obj, args);  
        return result;  
    }  
}  

在这里实现InvocationHandler接口,此接口是JDK提供的动态代理接口,对被代理的方法提供代理。其中invoke方法是接口InvocationHandler定义必须实现的, 它完成对真实方法的调用。动态代理是根据被代理的接口生成所有的方法,也就是说给定一个接口,动态代理就会实现接口下所有的方法。通过 InvocationHandler接口, 所有方法都由该Handler来进行处理, 即所有被代理的方法都由 InvocationHandler接管实际的处理任务。

这里增加一个卖房的业务,代码和租房代码类似。

main方法测试:

public static void main(String[] args){  
    IRentHouse rentHouse = new RentHouse();  
    //定义一个handler  
    InvocationHandler handler = new IntermediaryProxy(rentHouse);  
    //获得类的class loader  
    ClassLoader cl = rentHouse.getClass().getClassLoader();  
    //动态产生一个代理者  
    IRentHouse proxy = (IRentHouse) Proxy.newProxyInstance(cl, new Class[]{IRentHouse.class}, handler);  
    proxy.rentHouse();  

    ISellHouse sellHouse = new SellHouse();  
    InvocationHandler handler1 = new IntermediaryProxy(sellHouse);  
    ClassLoader classLoader = sellHouse.getClass().getClassLoader();  
    ISellHouse proxy1 = (ISellHouse) Proxy.newProxyInstance(classLoader, new Class[]{ISellHouse.class}, handler1);  
    proxy1.sellHouse();  
}  
  
租了一间房子。。。  
买了一间房子。。。  

在main方法中我们用到了Proxy这个类的方法,

public static Object newProxyInstance(ClassLoader loader,  
Class<?>[] interfaces,  
InvocationHandler h)  

loder:类加载器,interfaces:代码要用来代理的接口, h:一个 InvocationHandler 对象 。

InvocationHandler 是一个接口,每个代理的实例都有一个与之关联的 InvocationHandler 实现类,如果代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类,由它决定处理。

public interface InvocationHandler {  
  
    Object invoke(Object proxy, Method method, Object[] args) throws Throwable;  
}  

InvocationHandler 内部只是一个 invoke() 方法,正是这个方法决定了怎么样处理代理传递过来的方法调用。

因为,Proxy 动态产生的代理会调用 InvocationHandler 实现类,所以 InvocationHandler 是实际执行者。

总结

  1. 静态代理,代理类需要自己编写代码写成。
  2. 动态代理,代理类通过 Proxy.newInstance() 方法生成。
  3. JDK实现的代理中不管是静态代理还是动态代理,代理与被代理者都要实现两样接口,它们的实质是面向接口编程。CGLib可以不需要接口。
  4. 动态代理通过 Proxy 动态生成 proxy class,但是它也指定了一个 InvocationHandler 的实现类。
转载自:https://juejin.cn/post/7244567350720430117
评论
请登录