代理模式
代理模式(Proxy Pattern)是一个使用率非常高的模式,其定义如下: 为其他对象提供一种代理以控制对这个对象的访问。
代理模式的通用类图如图
看一下类图中的三个角色的定义:
Subject抽象主题角色
抽象主题类可以是抽象类也可以是接口,是一个最普通的业务类型定义,无特殊要求。
RealSubject 具体主题角色
也叫做被委托角色、被代理角色,它才是冤大头,是业务逻辑的具体执行者。
Proxy 代理主题角色
也叫做委托类、代理类,它负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后处理工作。
下面我们举一个卖房子中经常用到的,就是房子中介,有时候我们为了方便可能会把房子让中介给我们出售首先我们应该建立一个接口就是卖房子人的接口HousePeople
public interface HousePeople {
public void IssueMessage();
public void ShowHouse();
public void VisitHouse();
public void SellHouse();
}
然后就是房主类
public class HouseHolder implements HousePeople {
private String name="";
public HouseHolder(String name){
this.name=name;
}
public void IssueMessage() {
// TODO Auto-generated method stub
System.out.println(this.name+"发布卖房子消息");
}
public void ShowHouse() {
// TODO Auto-generated method stub
System.out.println(this.name+"展示房子信息");
}
public void VisitHouse() {
// TODO Auto-generated method stub
System.out.println(this.name+"带领客户参观房子");
}
public void SellHouse() {
// TODO Auto-generated method stub
System.out.println(this.name+"卖出房子");
}
}
然后就是中介
public class HouseProxy implements HousePeople {
private HousePeople householder=null;
public HouseProxy(HousePeople householder){
this.householder=householder;
}
public void IssueMessage() {
// TODO Auto-generated method stub
this.householder.IssueMessage();
}
public void ShowHouse() {
// TODO Auto-generated method stub
this.householder.ShowHouse();
}
public void VisitHouse() {
// TODO Auto-generated method stub
this.householder.VisitHouse();
}
public void SellHouse() {
// TODO Auto-generated method stub
this.householder.SellHouse();
}
}
测试实例
public class testdemo {
public static void main(String arg[]){
HousePeople hh=new HouseHolder("xiaoding");
HousePeople hp=new HouseProxy(hh);
hp.IssueMessage();
hp.ShowHouse();
hp.VisitHouse();
hp.SellHouse();
}
}
结果
xiaoding发布卖房子消息
xiaoding展示房子信息
xiaoding带领客户参观房子
xiaoding卖出房子
代理模式的优点
职责清晰
真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件完成事务,附带的结果就是编程简洁清晰。
高扩展性
具体主题角色是随时都会发生变化的,只要它实现了接口,甭管它如何变化,都逃不脱如来佛的手掌(接口),那我们的代理类完全就可以在不做任何修改的情况下使用。
智能化
这在我们以上讲解中还没有体现出来,不过在我们以下的动态代理章节中你就会看到代理的智能化,读者有兴趣也可以看看Struts是如何把表单元素映射到对象上的。
普通代理
有些时候我们只想把房子交给中介不想过问太多事情,也不想让别人知道这套房子是自己的这时候我们就可以用到普通代理
普通代理,它的要求就是客户端只能访问代理角色,而不能访问真实角色
这时候只需要修改HouseHolder类和HouseProxy类即可,接口类就不用修改
public class HouseHolder implements HousePeople {
private String name="";
public HouseHolder(HousePeople housepeople,String name)throws Exception{
if(housepeople==null)
throw new Exception(name+"没有要出售的房子");
else
this.name=name;
}
public void IssueMessage() {
// TODO Auto-generated method stub
System.out.println(this.name+"发布卖房子消息");
}
public void ShowHouse() {
// TODO Auto-generated method stub
System.out.println(this.name+"展示房子信息");
}
public void VisitHouse() {
// TODO Auto-generated method stub
System.out.println(this.name+"带领客户参观房子");
}
public void SellHouse() {
// TODO Auto-generated method stub
System.out.println(this.name+"卖出房子");
}
}
public class HouseProxy implements HousePeople {
private HousePeople householder=null;
public HouseProxy(String name){
try{
householder=new HouseHolder(this,name);
}catch(Exception e){
System.out.println(e);
}
}
public void IssueMessage() {
// TODO Auto-generated method stub
this.householder.IssueMessage();
}
public void ShowHouse() {
// TODO Auto-generated method stub
this.householder.ShowHouse();
}
public void VisitHouse() {
// TODO Auto-generated method stub
this.householder.VisitHouse();
}
public void SellHouse() {
// TODO Auto-generated method stub
this.householder.SellHouse();
}
}
接下来就是测试实例,我们只需要告诉谁要卖房子即可,其他人是不可能访问到房子持有人的
public class testdemo {
public static void main(String arg[]){
HousePeople hp=new HouseProxy("xiaosan");
hp.IssueMessage();
hp.ShowHouse();
hp.VisitHouse();
hp.SellHouse();
}
}
结果相似
xiaosan发布卖房子消息
xiaosan展示房子信息
xiaosan带领客户参观房子
xiaosan卖出房子
在该模式下,调用者只知代理而不用知道真实的角色是谁,屏蔽了真实角色的变更对高层模块的影响,真实的主题角色爱怎么修改就怎么修改,对高层次的模块没有任何的影响,只要你实现了接口所对应的方法,该模式非常适合对扩展性要求较高的场合。当然,在实际的项目中,一般都是通过约定来禁止new一个真实的角色,也是一个非常好的方案。
注意 普通代理模式的约束问题,尽量通过团队内的编程规范类约束,因为每一个主题类是可被重用的和可维护的,使用技术约束的方式对系统维护是一种非常不利的因素。
强制代理
强制代理则是调用者直接调用真实角色,而不用关心代理是否存在,其代理的产生是由真实角色决定的,一般的思维都是通过代理找到真实的角色,但是强制代理却是要“强制”,你必须通过真实角色查找到代理角色,否则你不能访问,甭管你是通过代理类还是通过直接new一个主题角色类,都不能访问,只有通过真实角色指定的代理类才可以访问,也就是说由真实角色管理代理角色,这么说吧,高层模块new了一个真实角色的对象,返回的却是代理角色
接口类要进行修改,添加返回代理类,仅仅增加了一个getProxy方法,指定要访问自己必须通过哪个代理,实现类也要做适当的修改
public interface HousePeople {
public void IssueMessage();
public void ShowHouse();
public void VisitHouse();
public void SellHouse();
public HousePeople getProxy();
}
HouseHold类,增加了一个私有方法,检查是否是自己指定的代理,是指定的代理则允许访问,否则不允许访问。我们再来看代理角色
public class HouseHolder implements HousePeople {
private String name="";
private HousePeople proxy=null;
public HouseHolder(String name){
this.name=name;
}
public void IssueMessage() {
// TODO Auto-generated method stub
if(this.isProxy())
System.out.println(this.name+"发布卖房子消息");
else
System.out.println("请使用指定的代理访问");
}
public void ShowHouse() {
// TODO Auto-generated method stub
if(this.isProxy())
System.out.println(this.name+"展示房子信息");
else
System.out.println("请使用指定的代理访问");
}
public void VisitHouse() {
// TODO Auto-generated method stub
if(this.isProxy())
System.out.println(this.name+"带领客户参观房子");
else
System.out.println("请使用指定的代理访问");
}
public void SellHouse() {
// TODO Auto-generated method stub
if(this.isProxy())
System.out.println(this.name+"卖出房子");
else
System.out.println("请使用指定的代理访问");
}
public HousePeople getProxy(){
this.proxy=new HouseProxy(this);
return this.proxy;
}
private boolean isProxy(){
if(this.proxy==null)
return false;
else
return true;
}
}
Houseproxy类,代理角色也可以再次被代理,这里我们就没有继续延伸下去了,查找代理的方法就返回自己的实例。
public class HouseProxy implements HousePeople {
private HousePeople householder=null;
public HouseProxy(HousePeople householder){
this.householder=householder;
}
public void IssueMessage() {
// TODO Auto-generated method stub
this.householder.IssueMessage();
}
public void ShowHouse() {
// TODO Auto-generated method stub
this.householder.ShowHouse();
}
public void VisitHouse() {
// TODO Auto-generated method stub
this.householder.VisitHouse();
}
public void SellHouse() {
// TODO Auto-generated method stub
this.householder.SellHouse();
}
public HousePeople getProxy(){
return this;
}
}
测试实例
public class testdemo {
public static void main(String arg[]){
HousePeople hp=new HouseHolder("xiaosan");
HousePeople proxy=hp.getProxy();
proxy.IssueMessage();
proxy.ShowHouse();
proxy.VisitHouse();
proxy.SellHouse();
}
}
结果
xiaosan发布卖房子消息
xiaosan展示房子信息
xiaosan带领客户参观房子
xiaosan卖出房子
强制代理的概念就是要从真实角色查找到代理角色,不允许直接访问真实角色,高层模块只要调用getProxy就可以访问真实角色的所有方法,它根本就不需要产生一个代理出来,代理的管理已经由真实角色自己完成。
动态代理
动态代理是在实现阶段不用关心代理谁,而在运行阶段才指定代理那一个对象,相对的来说,自己写代理类的方式就是静态代理。
接口HousePeople类
public interface HousePeople {
public void IssueMessage();
public void ShowHouse();
public void VisitHouse();
public void SellHouse();
}
HouseHold类
public class HouseHolder implements HousePeople {
private String name="";
public HouseHolder(String name){
this.name=name;
}
public void IssueMessage() {
// TODO Auto-generated method stub
System.out.println(this.name+"发布卖房子消息");
}
public void ShowHouse() {
// TODO Auto-generated method stub
System.out.println(this.name+"展示房子信息");
}
public void VisitHouse() {
// TODO Auto-generated method stub
System.out.println(this.name+"带领客户参观房子");
}
public void SellHouse() {
// TODO Auto-generated method stub
System.out.println(this.name+"卖出房子");
}
}
动态代理类
public class HouseIH implements InvocationHandler {
Class cls=null;
Object obj=null;
public HouseIH(Object obj){
this.obj=obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
Object result=method.invoke(this.obj, args);
if(method.getName().equalsIgnoreCase("VisitHouse"))
System.out.println("有人在看房子");
return result;
}
}
其中invoke方法是接口InvocationHandler定义必须实现的,它完成对真实方法的调用。我们来详细讲解一下InvocationHanlder接口,动态代理是根据被代理的接口生成所有的方法,也就是说给定一个接口,动态代理会宣称“我已经实现该接口下的所有方法了”,那各位读者想想看,动态代理怎么才能实现被代理接口中的方法呢?默认情况下所有的方法返回值都是空的,是的,代理已经实现它了,但是没有任何的逻辑含义,那怎么办?好办,通过InvocationHandler接口,所有方法都由该Handler来进行处理,即所有被代理的方法都由InvocationHandler接管实际的处理任务。测试实例
public class testdemo {
public static void main(String arg[]){
HousePeople hh=new HouseHolder("xiaosan");
InvocationHandler handler=new HouseIH(hh);
ClassLoader cl=hh.getClass().getClassLoader();
HousePeople proxy=(HousePeople)Proxy.newProxyInstance(cl, new Class[]{HousePeople.class}, handler);
proxy.IssueMessage();
proxy.ShowHouse();
proxy.VisitHouse();
proxy.SellHouse();
}
}
结果
xiaosan发布卖房子消息
xiaosan展示房子信息
xiaosan带领客户参观房子
有人在看房子
xiaosan卖出房子
一个类的动态代理类是这样的一个类,由InvocationHandler的实现类实现所有的方法,由其invoke方法接管所有方法的实现