定义:
代理模式(Proxy),为其他对象提供一种代理以控制对这个对象的访问。
代理模式是一种比较贴近于生活的 设计模式,现实生活中也有很多代理模式的例子:
- 住酒店不一定需要亲自到酒店去,还可以通过微信支付下的同程艺龙来订酒店。
- 我们可以通过中介去找房子,不用直接跟房东沟通(现实生活中,我们更希望直接跟房东沟通)
- 春运买票买不到,我们可以找黄牛替我们抢票
- 想访问国外的网站,可以使用代理服务器进行访问。
代理模式通常解决的问题是,我们没有办法直接访问某一个对象,但是却想使用这个对象的一些功能,这个时候我们就可以创建一个代理对象,通过访问代理对象,间接的访问这个对象。
我们想去旅行,但是没有办法提前去目的地订酒店,所以我们通过同程艺龙来订酒店。同程艺龙就是一个代理对象,他和酒店一样,提供了订酒店的方法。
我们没有渠道去直接联系房东,所以我们可以通过中介公司租房,房东把租房这件事委托给了中介公司,这里中介公司就是一个代理对象。代理对象除了调用真实对象的方法,还可以对方法进行增强。比如中介公司可以赚差价。
图解:
这里可以看出,无论是代理对象和被代理对象,都实现了Subject接口,同时被代理对象作为代理对象的成员变量。这个结构与装饰模式非常像,下图是装饰模式的示意图:
那么它们之间有什么区别呢?
首先从定义上可以看出,装饰模式强调的是添加职责,而代理模式强调的是间接访问。
其次从结构设计上来说,装饰模式是将一个个功能进行拆分,使用时再动态的进行组装,生成不同功能的对象。而代理模式却是在编译之前就已经确定了代理对象和被代理对象之间的关系。
最后就使用上来说,装饰模式需要客户端去组装对象,而代理模式却只需要客户端去调用代理对象,代理对象和被代理对象的关系被封装到了代理对象中,对用户而言是无感的。
实例:
这里举一个订酒店的实例,小赵在同程艺龙上预定了橘子酒店,这里酒店就是图中的Subject,它提供了订酒店、付款的方法,橘子酒店就是RealSubject,它是酒店的一个具体实现。而同程艺龙作为酒店的代理,提供优质的客户服务,其中包括订酒店前的电话确认、订酒店后的客户回访、付款时提供红包减免等。
代码:
/**
* 酒店.
*
* @author jialin.li
* @date 2019-12-27 11:37
*/
public interface Hotel {
/** 订酒店 **/
void hotelBooking(String name);
/** 付款 **/
double pay();
}
/**
* 橘子酒店.
*
* @author jialin.li
* @date 2019-12-27 13:17
*/
public class OrangeHotel implements Hotel {
@Override
public void hotelBooking(String name) {
System.out.println(name + ",欢迎入住橘子酒店");
}
@Override
public double pay() {
return 120d;
}
}
/**
* 同程艺龙
*
* @author jialin.li
* @date 2019-12-27 13:20
*/
public class Elong implements Hotel{
private Hotel hotel;
public Elong() {
hotel = new OrangeHotel();
}
@Override
public void hotelBooking(String name) {
System.out.println(confirm(name));
hotel.hotelBooking(name);
System.out.println(callback(name));
}
@Override
public double pay() {
return useHongbao(hotel.pay());
}
private String confirm(String name){
return "亲爱的"+name+"您预定了橘子酒店,记得入住哦!";
}
private String callback(String name){
return "亲爱的"+name+"您的入住已经结束,有问题请及时反馈!";
}
private double useHongbao(double price){
return price - 50.0d;
}
}
/**
* 测试类.
*
* @author jialin.li
* @date 2019-12-27 13:36
*/
public class Main {
public static void main(String[] args) {
String xiaozhao = "小赵";
Elong elong = new Elong();
elong.hotelBooking(xiaozhao);
System.out.print("共花费了:");
System.out.println(elong.pay());
}
}
结果:
亲爱的小赵您预定了橘子酒店,记得入住哦!
小赵,欢迎入住橘子酒店
亲爱的小赵您的入住已经结束,有问题请及时反馈!
共花费了:70.0
有没有什么问题?
可以看出,我们在代理类中直接引用了被代理对象,所以代理类和被代理类是一种一对一的关系,即我们需要为每一个被代理类,创建一个代理类。
假设我们现在有一个打点的需求,要为系统中的一批对象增加记录日志的方法,如果使用代理模式,就要为这一批对象创建代理类,这样的工作量是无疑是巨大的。那么有没有什么办法可以让我们可以少写或者不写代理类,却能完成代理功能呢?这就要引出了我们要讲的一个概念——动态代理(动态代理涉及到、反射类加载的一些知识,所以在下一篇文章中讲解)