设计模式之代理模式
代理模式,给某一个对象提供一个代理,并且由代理对象控制对原对象的引用。
代理模式结构图
代理模式包含Subject(抽象主题角色),Proxy(代理主题角色)和RealSubject(真实主题角色)。
静态代理模式示例
假设有一名毕业生,想要找工作,然后通过51job或者拉勾网进行找工作,投递简历,其实51job和拉勾网相当于个中介,替不同的企业代理,为企业收集了广大招聘者的简历,在这里企业就是就是Subject,想要招人,吸引招聘者投简历,而51job或者拉勾网发布企业的招聘信息,并且会为企业进行招聘推荐和宣传以及招聘后续的求职者的信息反馈。下面是Subject(抽象主题角色),公司的招聘接口,有一个发布职位的方法定义:
/*公司招聘接口,Subject(抽象主题角色)*/
public interface ICompanyRecruitment {
/*发布招聘职位*/
void publishRecruitment();
}
RealSubject(真实主题角色),是具体的公司,实现Subject(抽象主题角色)的发布招聘职位的方法:
/*实际的主题角色(RealSubject),Facebook*/
public class FacebookCompanyRecruitment implements ICompanyRecruitment{
@Override
public void publishRecruitment() {
System.out.println("Facebook发布:招聘高级算法工程师...");
}
}
Proxy(代理主题角色),是代理具体的公司,完成发布职位以及其他前置和后置的处理,代理对象可以在调用被代理对象时添加预处理和后置处理,代理类的实现如下:
/*招聘网站,代理公司发布招聘信息和一些前置和后置的处理*/
public class RecruitingWebsiteProxy implements ICompanyRecruitment{
/*实际的主题角色,也就是公司的发布招聘的实现*/
ICompanyRecruitment companyRecruitment;
public RecruitingWebsiteProxy() {
}
/*构造注入*/
public RecruitingWebsiteProxy(ICompanyRecruitment companyRecruitment) {
this.companyRecruitment = companyRecruitment;
}
/*setter注入,用来注入实际的主题角色对象,也可以使用构造注入*/
public void setCompanyRecruitment(ICompanyRecruitment companyRecruitment){
this.companyRecruitment = companyRecruitment;
}
@Override
public void publishRecruitment() {
System.out.println("前置:根据公司的不同,选择到不同的版块发布...");
companyRecruitment.publishRecruitment();//发布公司的招聘信息
System.out.println("后置:根据招聘岗位的类型,进行个性化的推荐指定用户群...");
}
}
测试类实现如下:
/*代理模式测试*/
public class Test {
public static void main(String[] args) {
/*1.实例化代理类的代理对象实例*/
RecruitingWebsiteProxy recruitingWebsiteProxy = new RecruitingWebsiteProxy();
/*2.设置实际的被代理对象*/
ICompanyRecruitment facebookCompanyRecruitment = new FacebookCompanyRecruitment();
recruitingWebsiteProxy.setCompanyRecruitment(facebookCompanyRecruitment);
/*3.调用代理对象的实现*/
recruitingWebsiteProxy.publishRecruitment();
}
}
测试程序执行结果:
动态代理模式示例
如上面的静态代理模式,可以看出实际的主题角色是必须已经存在的,并且将它作为代理对象的内部成员属性。下面用动态代理实现上面的静态代理的实现,Subject(抽象主题角色),公司的招聘接口,有一个发布职位的方法定义:
下面是Subject(抽象主题角色),公司的招聘接口,有一个发布职位的方法定义:
/*公司招聘接口,Subject(抽象主题角色)*/
public interface ICompanyRecruitment {
/*发布招聘职位*/
void publishRecruitment();
}
FacebookCompanyRecruitment
是RealSubject(真实主题角色),真实角色A,是具体的公司,实现Subject(抽象主题角色)的发布招聘职位的方法:
/*实际的主题角色(RealSubject),Facebook*/
public class FacebookCompanyRecruitment implements ICompanyRecruitment{
@Override
public void publishRecruitment() {
System.out.println("Facebook发布:招聘高级算法工程师...");
}
}
GoogleCompanyRecruitment
是RealSubject(真实主题角色),真实角色B,是具体的公司,实现Subject(抽象主题角色)的发布招聘职位的方法:
/*实际的主题角色(RealSubject),Google*/
public class GoogleCompanyRecruitment implements ICompanyRecruitment {
@Override
public void publishRecruitment() {
System.out.println("Google发布:招聘高级算法工程师...");
}
}
动态代理类:
public class DynamicRecruitingWebsiteProxy implements InvocationHandler {
//被代理对象
private Object obj;
public DynamicRecruitingWebsiteProxy(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("前置:根据公司的不同,选择到不同的版块发布...");
method.invoke(obj,args);//发布公司的招聘信息
System.out.println("后置:根据招聘岗位的类型,进行个性化的推荐指定用户群...");
return null;
}
}
测试代码:
/*动态代理测试类*/
public class Client {
/*DynamicRecruitingWebsiteProxy 只用了这个代理类实现了代理多个实际的被代理对象*/
public static void main(String[] args) {
InvocationHandler handler = null;
ICompanyRecruitment companyRecruitment = null;
/*1.Facebook发布招聘岗位*/
System.out.println("=====================Fackbook==========================");
handler = new DynamicRecruitingWebsiteProxy(new FacebookCompanyRecruitment());
companyRecruitment = (ICompanyRecruitment) Proxy.newProxyInstance(ICompanyRecruitment.class.getClassLoader(),new Class[]{ICompanyRecruitment.class},handler);
companyRecruitment.publishRecruitment();
/*2.Google发布招聘岗位*/
System.out.println("=====================Google==========================");
handler = new DynamicRecruitingWebsiteProxy(new GoogleCompanyRecruitment());
companyRecruitment = (ICompanyRecruitment) Proxy.newProxyInstance(ICompanyRecruitment.class.getClassLoader(),new Class[]{ICompanyRecruitment.class},handler);
companyRecruitment.publishRecruitment();
}
}
测试结果:
动态代理实现相关类为于java.lang.reflect
包,主要涉及了两个类:
-
InvocationHandler
接口。它是代理实例的调用处理程序实现的接口,改接口定义了如下方法:public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
该方法中第一个参数
proxy
表示代理类,第二个参数method
表示需要代理的方法,第三个参数args
表示代理方法的参数数组。 -
Proxy
类。该类即动态代理类,该类最常用的方法为:public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)throws IllegalArgumentException
newProxyInstance()
方法用于根据传入的接口类型interfaces
返回一个动态创建的代理类的实例,方法中第一个参数loader
表示代理类的类加载器,第二个参数interfaces
表示代理类实现的接口列表(与真实主题类的接口列表一致),第三个参数h表示所指派的调用处理程序类。
什么场景下使用代理模式
如果某个应用的调用端不想或者不能直接引用一个对象,此时可以采用代理模式。
代理模式优缺点
优点:能够协调调用者和被调用者,降低了系统的耦合度。
缺点:由于增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
静态代理和动态代理的区别
a.静态代理,代理类需要自己编写代码产生。
b.动态代理,代理类通过 Proxy.newInstance() 方法产生。
c.静态代理和动态代理的区别是在于是否需要开发者自己定义 Proxy 类。