代理模式的基本概念:
概念:为其他对象提供一种代理,以控制对这个对象的访问(例如火车票代售处,就是火车站的代理)。代理对象起到中介作用,可去掉功能服务(退票服务)或增加额外的服务(手续费)。
分类:
- 远程代理---为不同地理的对象提供局域网代表对象,类似于总店看各个连锁分店的情况(客户端和服务器端,是远程中心的一个索引)
- 虚拟代理---根据需要将资源消耗很大的对象进行延迟,真正需要的时候才进行创建。(网页中图片的加载,先用一张虚拟的图片进行显示,等图片加载完成后再进行显示)
- 保护代理---控制用户的访问权限。(例如发帖必须登陆)
- 智能引用代理---提供对目标对象一些额外的服务。(火车站)
静态代理概念以及实现:
静态代理实现方式:继承和聚合
下面我们举个例子:有一个car类,通过实现接口Moveable
public interface Movable {
void move();
}
实现了接口中行驶的方法move( )
public class Car implements Movable {
@Override
public void move() {
// TODO Auto-generated method stub
System.out.println("car moving.......");
try {
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
此时他有一个需求:记录行驶时间
我们就可以使用代理模式来记录,如果不使用代理模式需要怎么做:
我们需要把
记录行驶时间的业务也放在move方法中
public class Car implements Movable {
@Override
public void move() {
System.out.println("[继承方式]的时间代理启动...........");
long start = System.currentTimeMillis();
System.out.println("car moving.......");
try {
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("花费时间:"+(end - start) + "毫秒");
System.out.println("[继承方式]的时间代理关闭...........");
}
}
然后我们用代理类来实现
继承:代理类直接【继承】被代理类,在代理类中调用父类的被代理方法,在方法的前后增加代理的业务逻辑。下面我们采用继承来实现代理:
我们
创建一个
代理类
( Car2 ) 并且继承了需要被代理的类 ( Car ) ,
在代理类中调用父类需要被代理的方法move( ),
并且在这个代理方法内编写计时的业务逻辑。
这样做的好处是,我们可以创建多个代理类Car4,Car5,采用不同的计时方法,并且均重写move( )方法,我们通过为Car car 注入的不同子类的方式,实现了调用相同的car.move( )方法,可以有
不同的计时方式。通过聚合我们也可以做到。
public class CarTimeProxy_extend extends Car{
@Override
public void move() {
System.out.println("[继承方式]的时间代理启动...........");
long start = System.currentTimeMillis();
super.move();
long end = System.currentTimeMillis();
System.out.println("花费时间:"+(end - start) + "毫秒");
System.out.println("[继承方式]的时间代理关闭...........");
}
}
聚合:代理类实现与被代理的类相同的功能接口,并且
在代理类中声明被代理类的对象,并通过构造方法将对象传进来(初始化)。在代理类的接口方法中调用被代理类对象的方法,并在方法前后增加相应代理业务逻辑。
聚合为什么一定要实现相同的功能接口?
- 利用多态特性,代理类与被代理的类实现了相同接口的,则被代理的类可以被多钟不同的代理类所代理
- 实现了相同的接口,不同的代理类之间也可以相互代理即在代理类中传入另一个实现了同样接口的代理,来让不同的功能的代理都可以聚合在一起(这是跟继承方式最重要的区别)
该例中Car3也实现了Moveable接口,并且声明并传入了代理类Car
然后在代理类Car3的move( )方法中r实现内部对象Car的move( )方法,编写计时的业务逻辑
public class CarTimeProxy_combine implements Movable{
Movable m;
public CarTimeProxy_combine (Movable m) {
this.m = m;
}
@Override
public void move() {
System.out.println("[聚合方式]的时间代理启动...........");
long start = System.currentTimeMillis();
m.move();
long end = System.currentTimeMillis();
System.out.println("花费时间:"+(end - start) + "毫秒");
System.out.println("[聚合方式]的时间代理结束...........");
}
}
聚合与继承方式比较:
我们还以这个car来举例子,但是这个时候我们的需求为:
- 在Car的move( )方法上做时间代理及行驶日志代理
- 以后还需要增加很多其他代理
- 代理间的顺序可以灵活更换
下面我们先用聚合的方式来编写代理类:
[聚合方式]的时间代理:
public class CarTimeProxy_combine implements Movable{
Movable m;
public CarTimeProxy_combine (Movable m) {
this.m = m;
}
@Override
publicvoid move() {
System.out.println("[聚合方式]的时间代理启动...........");
longstart = System.currentTimeMillis();
m.move();
longend = System.currentTimeMillis();
System.out.println("花费时间:"+(end - start) + "毫秒");
System.out.println("[聚合方式]的时间代理结束...........");
}
}
[聚合方式]的行驶日志代理:
public class CarTimeProxy_combine implements Movable{
Movable m;
public CarTimeProxy_combine (Movable m) {
this.m = m;
}
@Override
publicvoid move() {
System.out.println("[聚合方式]的时间代理启动...........");
longstart = System.currentTimeMillis();
m.move();
longend = System.currentTimeMillis();
System.out.println("花费时间:"+(end - start) + "毫秒");
System.out.println("[聚合方式]的时间代理结束...........");
}
}
我们再先用继承的方式来编写代理类:
[继承方式]的时间代理:
public class CarTimeProxy_extend extends Car{
@Override
public void move() {
System.out.println("[继承方式]的时间代理启动...........");
long start = System.currentTimeMillis();
super.move();
long end = System.currentTimeMillis();
System.out.println("花费时间:"+(end - start) + "毫秒");
System.out.println("[继承方式]的时间代理关闭...........");
}
}
[继承方式]的行驶日志代理(这里继承了时间代理类):
public class CarLogProxy_extend extends CarTimeProxy_extend {
public void move(){
System.out.println("[继承方式]的行驶日志代理启动...........");
super.move();
System.out.println("[继承方式]的行驶日志代理关闭...........");
}
}
测试类:
public class Client {
public static void main(String[] args) {
Movable m;
Car car = new Car();
/**
* 一、聚合的方式(较灵活,因为实现了接口)
* 1.1聚合方式的代理,先日志代理,后时间代理
*/
CarTimeProxy_combine carTimeProxy_combine = new CarTimeProxy_combine(car);
CarLogProxy_combine carLogProxy_combine = new CarLogProxy_combine(carTimeProxy_combine);
m = carLogProxy_combine;
m.move();
System.out.println("====先日志代理,后时间代理[聚合方式]结束====");
/**
* 1.2聚合方式的代理,先时间代理,后日志代理(可以灵活切换顺序)
*/
CarLogProxy_combine carLogProxy_combine2 = new CarLogProxy_combine(car);
CarTimeProxy_combine carTimeProxy_combine2 = new CarTimeProxy_combine(carLogProxy_combine2);
m = carTimeProxy_combine2;
m.move();
System.out.println("====先时间代理,后日志代理[聚合方式]结束====");
/**
* 二、继承的方式
* 2.1代理时间
*/
CarTimeProxy_extend carTimeProxy_extend = new CarTimeProxy_extend();
carTimeProxy_extend.move();
System.out.println("====时间代理[继承方式]结束====");
/**
* 2.2先代理日志,后时间(因为已经在类中指明继承关系)不能灵活切换
*/
CarLogProxy_extend carLogProxy_extend = new CarLogProxy_extend();
carLogProxy_extend.move();
System.out.println("====先代理日志,后时间[继承方式]结束====");
}
}
输出:
由此例我们可以得出结论:
聚合:聚合十分灵活,一个代理类可以作为参数注入到另一个代理类中,通过控制声明代理类的顺序,我们可以控制不同代理执行的顺序。当我们需要代理其他实现了Movable接口的类(例如:TankCar,MobileCar),只要传入对应的需要代理的类就可以了。
继承:继承不够灵活,随着功能需求增多,继承体系会非常臃肿,而且需要多重代理时,因为我们在类中各个代理之间的继承是固定的,所以无法改变代理的执行顺序。当我们需要代理其他实现了Movable接口的类(例如:TankCar,MobileCar),我们只能继续无休止得添加新的代理类。