代理模式:为其他对象提供一种代理以控制对这个对象的访问。
一、如何理解代理模式的定义
代理在生活中是一种非常常见的现象,比如奔驰、宝马在中国都有代理商,伊利冷饮在各个城市县城也都有代理商,我们买东西都是到代理商那里买的,而不是直接到厂商那里买。
就如代理模式的定义一样代理商的存在就是控制我们消费者对厂商的访问。下面以买车为例,看看如何用代码解释代理
定义一个买车的接口IBuyCar
,所有买车都人都需要实现它
interface IBuyCar {
//付款
void payment();
//取车
void getCar();
}
定义一个汽车代理商CarProxy
class CarProxy implements IBuyCar {
//在构造方法内传入需要买车的消费者
private IBuyCar mIBuyCar = null;
public CarProxy(IBuyCar iBuyCar){
mIBuyCar = iBuyCar;
System.out.println("代理商收到购车要求");
}
//通过代理商向商家付款
@Override
public void payment() {
mIBuyCar.payment();
}
//通过代理商向商家取车
@Override
public void getCar() {
mIBuyCar.getCar();
}
}
定义一个汽车买家类
class CarBuyer implements IBuyCar {
private String name;
public CarBuyer(String name){
this.name = name;
System.out.println(name + "准备买辆汽车");
}
@Override
public void payment() {
System.out.println(name + "向商家付款买车");
}
@Override
public void getCar() {
System.out.println(name + "取车成功");
}
}
现在可以去买车了
public static void main(String[] args){
//创建一个消费者
IBuyCar carBuyer = new CarBuyer("二丫");
//创建一个代理
IBuyCar carProxy = new CarProxy(carBuyer);
//付款
carProxy.payment();
//取车
carProxy.getCar();
}
运行结果:
二丫准备买辆汽车
代理商收到购车要求
二丫向商家付款买车
二丫取车成功
通过汽车代理商,我们向商家付款成功的买了一辆小汽车了。
二、定义通用的代理模式
代理模式为其它对象提供一种代理以控制对这个对象的访问。通用类图如下:
代理模式也叫做委托模式,是一项基本的设计技巧。
- Subject:抽象主题、接口
- RealSubject:具体主题角色,也被称作委托角色,被代理角色Proxy
- Proxy:代理,委托类、代理类
下面来看一下代理模式的通用写法:
创建Subject主题
interface Subject {
//定义一个方法代表。
void request();
}
定义RealSubject
class RealSubject implements Subject {
//处理具体业务逻辑
@Override
public void request() {
System.out.println("真实的请求");
}
}
定义Proxy
class Proxy implements Subject {
//要代理的实现类
Subject mSubject = null;
//构造方法初始化代理类
public Proxy(Subject subject){
mSubject = subject;
}
@Override
public void request() {
this.before();
mSubject.request();
this.after();
}
//预处理
private void before(){
//do something
System.out.println("预处理");
}
//善后处理
private void after(){
//do something
System.out.println("善后处理");
}
}
最简单的运行:
public static void main(String[] args){
Subject realSubject = new RealSubject();
Subject proxy = new Proxy(realSubject);
proxy.request();
}
执行结果:
预处理
真实的请求
善后处理
三、静态代理
上面第一个买车的例子就是一个简单的静态代理。这里我们只需要在代理类中添加一些方法完善一下就行了。
添加预处理和善后处理
class CarProxy implements IBuyCar {
//在构造方法内传入需要买车的消费者
private IBuyCar mIBuyCar = null;
public CarProxy(IBuyCar iBuyCar){
mIBuyCar = iBuyCar;
System.out.println("代理商收到购车要求");
}
//通过代理商向商家付款
@Override
public void payment() {
beforePay();
mIBuyCar.payment();
afterPay();
}
//通过代理商向商家取车
@Override
public void getCar() {
beforeGetCar();
mIBuyCar.getCar();
afterGetCar();
}
//预处理
private void beforePay(){
//do something
System.out.println("付款之前:需支付8000金融服务费");
}
//善后处理
private void afterPay(){
//do something
System.out.println("付款之后:等待取车");
}
//预处理
private void beforeGetCar(){
//do something
System.out.println("取车之前:签验收合同");
}
//善后处理
private void afterGetCar(){
//do something
System.out.println("取车之后:刚开200米,漏油了!");
}
}
再次执行结果如下:
二丫准备买辆汽车
代理商收到购车要求
付款之前:需支付8000金融服务费
二丫向商家付款买车
付款之后:等待取车
取车之前:签验收合同
二丫取车成功
取车之后:刚开200米,漏油了!
为什么要专门加上这段预处理还善后处理的代码?
这是静态代理的精髓所在,因为CarBuyer之前是只有支付和取车的功能,而通过代理使得CarBuyer增加了预处理和善后处理的功能,扩展了它的功能
为什么叫静态代理?
因为需要事先创建好代理类,区别于动态代理在需要的时候才动态创建。
静态代理优缺点
- 优点:做到了在符合开闭原则的情况下对目标对象进行功能扩展。
- 缺点:代理对象必须提前写出,如果接口层发生了变化,代理对象的代码也要进行维护。
四、动态代理
动态代理也叫JDK代理,在动态代理中我们不需要再创建代理类了,只需要一个动态处理器就可以了。
动态代理之所以又叫JDK代理,因为动态代理使用的是Java JDK提供的代理方法。首先来看Java创建代理对象的方法。
public static Object newProxyInstance(ClassLoader loader,Class>[] interfaces,InvocationHandler h)
newProxyInstance方法有三个参数:
- 第一个参数:类加载器
- 第二个参数:目标对象实现的接口的类型
- 第三个参数:调用处理器,执行目标对象的方法时,会触发事件处理器的方法
第一个和第二个参数写法都是固定的,第三个参数InvocationHandler是一个接口,负责处理具体的逻辑
下面创建InvocationHandler接口的实现类
class CarHandler implements InvocationHandler {
//被代理的对象
private Object target = null;
public CarHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
if (method.getName().equals("payment")){
beforePay();
result = method.invoke(target,args);
afterPay();
}else if (method.getName().equals("getCar")){
beforeGetCar();
result = method.invoke(target,args);
afterGetCar();
}
return result;
}
//预处理
private void beforePay(){
//do something
System.out.println("付款之前:需支付8000金融服务费");
}
//善后处理
private void afterPay(){
//do something
System.out.println("付款之后:等待取车");
}
//预处理
private void beforeGetCar(){
//do something
System.out.println("取车之前:签验收合同");
}
//善后处理
private void afterGetCar(){
//do something
System.out.println("取车之后:刚开200米,漏油了!");
}
}
实现InvocationHandler接口需要实现它的invoke方法
invoke(Object proxy, Method method, Object[] args)
invoke方法也有三个,第一个proxy是代理类的实例,第二个method是方法的反射包装,第三个是方法的参数。
关于反射有不了解的地方可以看下我的 学习Java反射机制
创建好动态代理的核心接口InvocationHandler 的实现类之后,就可以使用动态代理买车了。买车代码:
public static void main(String[] args){
//创建被代理对象
IBuyCar carBuyer = new CarBuyer("老王");
//动态生成一个代理
IBuyCar proxy = (IBuyCar) Proxy.newProxyInstance(CarBuyer.class.getClassLoader(),
CarBuyer.class.getInterfaces(),new CarHandler(carBuyer));
proxy.payment();
proxy.getCar();
}
运行结果:
老王准备买辆汽车
付款之前:需支付8000金融服务费
老王向商家付款买车
付款之后:等待取车
取车之前:签验收合同
老王取车成功
取车之后:刚开200米,漏油了!
使用动态代理我们只需要实现核心接口 InvocationHandler ,代理类由代码运行时JDK创建,不需要我们手动创建。
参考:《设计模式之禅》