代理模式
代理模式的定义:给某一个对象提供一个代理,并由代理对象控制对原对象的引用。
代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象。这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法。
举个例子来说明代理的作用:假设我们想邀请一位明星,那么并不是直接联系明星,而是联系明星的经纪人,来达到同样的目的。明星就是一个目标对象,他只要负责活动中的节目,而其他琐碎的事情就交给他的代理人(经纪人)来解决。这就是代理思想在现实中的一个例子。
代理模式的关键点是:代理对象与目标对象。代理对象是对目标对象的扩展,并会调用目标对象。
代理模式包含如下角色:
ISubject:抽象主题角色,是一个接口。该接口是对象和它的代理共用的接口。
RealSubject:真实主题角色,是实现抽象主题接口的类。
Proxy:代理角色,内部含有对真实对象RealSubject的引用,从而可以操作真实对象。代理对象提供与真实对象相同的接口,以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
代理模式的应用:
远程代理:也就是为一个对象在不同的地址空间提供局部代表。这样可以隐藏一个对象存在于不同地址空间的事实。
虚拟代理:是根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的真实对象。
安全代理:用来控制真实对象访问时的权限。
智能代理:是指当调用真实的对象时,代理处理一些另外的事情。
一般将代理分类的话,可分为静态代理和动态代理两种。
静态代理
静态代理比较简单,是由程序员编写的代理类,并在程序运行前就编译好的,而不是由程序动态产生代理类,这就是所谓的静态。
考虑这样的场景,管理员在网站上执行操作,在生成操作结果的同时需要记录操作日志,这是很常见的。此时就可以使用代理模式,代理模式可以通过聚合和继承两种方式实现:
/**
* @Description: 抽象主题接口
* @author: zxt
* @time: 2018年7月7日 下午2:29:46
*/
public interface Manager {
public void doSomething();
}
/**
* @Description: 真实的主题类
* @author: zxt
* @time: 2018年7月7日 下午2:31:21
*/
public class Admin implements Manager {
@Override
public void doSomething() {
System.out.println("这是真实的主题类:Admin doSomething!!!");
}
}
/**
* @Description: 以聚合的方式实现代理主题
* @author: zxt
* @time: 2018年7月7日 下午2:37:08
*/
public class AdminPoly implements Manager {
// 真实主题类的引用
private Admin admin;
public AdminPoly(Admin admin) {
this.admin = admin;
}
@Override
public void doSomething() {
System.out.println("聚合方式实现代理:Admin操作开始!!");
admin.doSomething();
System.out.println("聚合方式实现代理:Admin操作结束!!");
}
}
/**
* @Description: 继承方式实现代理
* @author: zxt
* @time: 2018年7月7日 下午2:40:39
*/
public class AdminProxy extends Admin {
@Override
public void doSomething() {
System.out.println("继承方式实现代理:Admin操作开始!!");
super.doSomething();
System.out.println("继承方式实现代理:Admin操作结束!!");
}
}
public static void main(String[] args) {
// 1、聚合方式的测试
Admin admin = new Admin();
Manager manager = new AdminPoly(admin);
manager.doSomething();
System.out.println("============================");
// 2、继承方式的测试
AdminProxy proxy = new AdminProxy();
proxy.doSomething();
}
聚合实现方式中代理类聚合了被代理类,且代理类及被代理类都实现了同一个接口,可实现灵活多变。继承式的实现方式则不够灵活。
比如,在管理员操作的同时需要进行权限的处理,操作内容的日志记录,操作后数据的变化三个功能。三个功能的排列组合有6种,也就是说使用继承要编写6个继承了Admin的代理类,而使用聚合,仅需要针对权限的处理、日志记录和数据变化三个功能编写代理类,在业务逻辑中根据具体需求改变代码顺序即可。
缺点:
1)、代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
2)、代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。
动态代理
实现动态代理的关键技术是反射。
一般来说,对代理模式而言,一个主题类与一个代理类一一对应,这也是静态代理模式的特点。
但是,也存在这样的情况,有n各主题类,但是代理类中的“前处理、后处理”都是一样的,仅调用主题不同。也就是说,多个主题类对应一个代理类,共享“前处理,后处理”功能,动态调用所需主题,大大减小了程序规模,这就是动态代理模式的特点。动态代理主要有两种:JDK自带的动态代理和CGLIB动态代理。
首先是另一个静态代理的实例:
1、一个可移动接口
public interface Moveable {
public void move();
}
2、一个实现了该接口的Car类
public class Car implements Moveable {
@Override
public void move() {
try {
Thread.sleep(new Random().nextInt(1000));
System.out.println("汽车行驶中----");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
3、现在需要有一个代理类来记录Car的运行时间:
public class CarTimeProxy implements Moveable {
private Moveable m;
public CarTimeProxy(Moveable m) {
super();
this.m = m;
}
@Override
public void move() {
long startTime = System.currentTimeMillis();
System.out.println("汽车行