所谓代理,是指具有与代理元(被代理的对象)具有相同的接口的类,客户端必须通过代理与被代理的目标类交互,而代理一般在交互的过程中(交互前后),进行某些特别的处理。Proxy模式是很常见的模式,在我们生活中处处可见,例如我们买火车票不一定非要到火车站去买,可以到一些火车票的代售点去买。寄信不一定是自己去寄,可以把信委托给邮局,由邮局把信送到目的地。
1. 代理:一个角色代表别一个角色来完成某些特定的功能。
比如:生产商,中间商,客户这三者这间的关系
客户买产品并不直接与生产商打交道,也不用知道产品是如何产生的,客户只与中间商打交道,而中间商就可以对产品进行一些包装,提供一些售后的服务。
代理模式有三个角色: 1. 抽象主题角色 2. 代理主题角色 3. 实际被代理角色
其它类通过访问代理主题角色来访问实际被代理角色。
2. 下面我们来个一个静态代理的实现。
我以一个坦克为例。
抽象主题角色:Moveable
- package com.gjy.proxy;
- blic interface Moveable {
- void move();
代理主题角色:TanktimeProxy
- package com.gjy.proxy;
- public class TanktimeProxy implements Moveable{
- private Moveable t;
- public TanktimeProxy(Moveable t) {
- super();
- this.t = t;
- }
- @Override
- public void move() {
- long time1 = System.currentTimeMillis();
- System.out.println("time1="+time1);
- t.move();
- long time2 = System.currentTimeMillis();
- System.out.println("time2="+time2);
- System.out.println("运行时间为:"+(time2-time1));
- }
- }
实际被代理对象:Tank
- package com.gjy.proxy;
- public class Tank implements Moveable{
- @Override
- public void move() {
- System.out.println("TanK moving........");
- }
- }
测试:
- package com.gjy.proxy;
- public class TestTank {
- public static void main(String[] args) {
- Tank t = new Tank();
- Moveable move = new TanktimeProxy(t);
- move.move();
- }
- }
从上例可以看到代理主题角色:TanktimeProxy实现了对Tank的move()方法运行时间的计算,而TanktimeProxy,Tank都实现了Moveable接口,通过调用TanktimeProxy的move()方法我们可以实现对Tank的move()方法的运行时间的计算,而不用在Tank的move()方法中作任何实现,这就是代理的作用。代理实现时TanktimeProxy,Tank必需实现Moveable接口。
下面我想在TanK的move()方法前后加上日志:
我必需再写一个类来实现这一功能:
- package com.gjy.proxy;
- public class TanklogProxy implements Moveable{
- private Moveable t;
- public TanklogProxy(Moveable t) {
- super();
- this.t = t;
- }
- @Override
- public void move() {
- System.out.println("start move........");
- t.move();
- System.out.println("end move......");
- }
- }
测试:
- package com.gjy.proxy;
- public class TestTank {
- public static void main(String[] args) {
- Tank t = new Tank();
- Moveable move = new TanktimeProxy(t);
- Moveable movet = new TanklogProxy(move);
- movet.move();
- }
- }
这样我通过代理在Tank的move()方法前后加入了日志和时间统计的功能,由于TanktimeProxy,TanklogProxy都实现了Moveable接口,所以TanklogProxy可以代理TanktimeProxy,反过来也可以,它们对Tank的代理顺序是可以交换的。
比如在玩“极品飞车”这款游戏,如果游戏者手中的金钱达到了一定的数量就可以到车店买一部性能更高的赛车,那么这个卖车的“车店”就是一个典型的“汽车厂家”的“代理”,他为汽车厂家“提供卖车的服务”给有需求的人士。从面向对象的方面考虑,“销售汽车的代理”也是一个对象,那么这个对象也具有一定的状态,在软件项目中这个对象也具有管理财务进销存的基本功能,那么在设计时就要以面向OOP编程的思想来考虑软件的类结构,这个销售汽车的代理也是一个类了。
【代理模式解释】
类型:结构模式
对一些对象提供代理,以限制那些对象去访问其它对象。
【代理模式UML图】
【代理模式-JAVA代码实现】
新建一个买车的接口:
package buy_car_package; public interface buy_car_package { public void buy_car(); } |
新建一个people人类,具有买车的行为,所以实现接口buy_car_package:
package buy_car_imple; import buy_car_package.buy_car_package; public class people implements buy_car_package { private int cash; private String username; public int getCash() { return cash; } public void setCash(int cash) { this.cash = cash; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public void buy_car() { System.out.println(username + "买了一台新车"); } } |
people类不能拥有车,必须经过proxy代理类的认证,符合条件之后才可以拥有车辆,新建一个代理,这个代理类来考察当前的people是否有资格进行买车:
package buy_car_imple; import buy_car_package.buy_car_package; public class proxy_buy_car_imple implements buy_car_package { private people people; public people getPeople() { return people; } public void setPeople(people people) { this.people = people; } public void buy_car() { if (people.getCash() > 3000) { System.out.println(people.getUsername() + "花" + people.getCash() + "块 买了新车 交易结束"); } else { System.out.println(people.getUsername() + "金钱不够,请继续比赛!"); } } } |
最后创建一个客户端,用来模拟买车的行为:
package run_main; import buy_car_imple.people; import buy_car_imple.proxy_buy_car_imple; public class run_main { public static void main(String[] args) { people people_ref1 = new people(); people_ref1.setCash(4000); people_ref1.setUsername("高洪岩"); people people_ref2 = new people(); people_ref2.setCash(2000); people_ref2.setUsername("岩洪高"); proxy_buy_car_imple proxy_buy_car_imple = new proxy_buy_car_imple(); proxy_buy_car_imple.setPeople(people_ref1); proxy_buy_car_imple.buy_car(); proxy_buy_car_imple.setPeople(people_ref2); proxy_buy_car_imple.buy_car(); } } |
程序运行结果如下:
高洪岩花4000块 买了新车 交易结束 岩洪高金钱不够,请继续比赛! |
这样people就不可能自由的拥有车辆,必须经过proxy的认证之后才可以。
而代理模式在GOF四人帮的介绍中大体有4种使用情景:
(1)远程代理。典型的就是客户端与webservice使用的情况,客户端由于是针对OOP编程,而不是针对webservice中的方法进行编程,所以得在客户端模拟一下webservice的环境,用proxy来对webservice进行包装,这样就可以使用proxy代理类来远程操作webservice了。
(2)虚拟代理。比如你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有100MB,在打开文件时不可能将所有的图片都显示出来,这样就可以使用代理模式,当需要查看图片时,用proxy来进行大图片的打开。
(3)安全代理。其实也就是本例中所举的买车的例子,金钱不够不可以买车!
(4)智能指引。比如在访问一个对象时检测其是否被锁定等情况。
将你要完成的一些事情交给代理去完成,在此·使用魔乐课堂李兴华老师讲课时用到的一个例子:如果你是图书馆管理员,你主要的工作是管理书籍,但现在上头需要让你也能担当保安的责任,那么,我们可以给图书管理员设置一个代理对象--代理1,保安的责任就让这个代理去做,如果上头又需要你也担当清扫的工作,那么,我们可以再设置一个代理对象:代理2,清扫的工作就交给代理2去做。我想,说这里,该明白的人也都名白了,代理模式就是将你要做的一些事分给一个你的代理去完成。
动态代理模式所必须的3个条件:
1、被代理对象——图书馆管理员
2、可执行者(代理的工作类/接口)——保安/清扫的工作
3、代理类——图书管理员的代理
接下来,我们以上述例子为例,讲解代理模式
首先,我们需要声明一个接口,即被代理对象需要去实现的接口,该接口的直接作用提供在获取动态代理对象的时候需要传入代理对象的类实现的接口对象。
- public interface Manager {
- void manage();
- }
然后写被代理对象,要求被代理对象实现上面的接口
- import com.cen.dao_interface.Manager;;
- public class LibraryManager implements Manager {
- @Override
- public void manage() {
- System.out.println("在管理图书!");
- }
- }
再写好要添加的工作类
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- public class BaoAnHandler implements InvocationHandler {
- Object object;
- /**
- * 构造方法,用于接收传递过来的被代理对象
- * @param o
- */
- public BaoAnHandler(Object o) {
- this.object = o;
- }
- @Override
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- Object resultObject;
- resultObject=method.invoke(object, args);
- baoAn();//调用新增加的方法
- return resultObject;
- }
- public void baoAn() {
- System.out.println("在做安保!");
- }
- }
注意,该类必须要实现InvocationHandler接口,并且在invoke方法内将要添加的工作逻辑调用。
最后写获取代理类对象的方法实现:
- @Test
- public void test_HelloWordProxy3() throws Exception {
- //一个被代理对象
- Manager manager = new LibraryManager();
- //一个可执行者,即添加的逻辑
- InvocationHandler invocationHandler = new BaoAnHandler(manager);
- //一个代理
- Manager proxy = (Manager) Proxy.newProxyInstance(manager.getClass()
- .getClassLoader(), manager.getClass().getInterfaces(),
- invocationHandler);
- //调用代理的方法
- proxy.manage();
- }
所谓代理,是指具有与代理元(被代理的对象)具有相同的接口的类,客户端必须通过代理与被代理的目标类交互,而代理一般在交互的过程中(交互前后),进行某些特别的处理。Proxy模式是很常见的模式,在我们生活中处处可见,例如我们买火车票不一定非要到火车站去买,可以到一些火车票的代售点去买。寄信不一定是自己去寄,可以把信委托给邮局,由邮局把信送到目的地。
代理结构如下图所示
以通过代售点买火车票为例,代码实现如下:
//提供买票的公共接口
interface Passenger {
public void buyTicket();
}
//乘客实体
public class RealPassenger implements Passenger {
@Override
public void buyTicket() {
// TODO Auto-generated method stub
System.out.print("购买了火车票");
}
}
//代售点
public class Proxy implements Passenger {
Passenger passenger;
public Proxy(Passenger p) {
this.passenger = p;
}
@Override
public void buyTicket() {
// TODO Auto-generated method stub
System.out.println("通过代售点");
passenger.buyTicket();
}
}
//测试类
public class Client {
public static void main(String[] args) {
Passenger passenger = new RealPassenger();
Passenger proxy = new Proxy(passenger);
proxy.buyTicket();
}
}
输出结果:
通过代售点
购买了火车票
以上的也可叫做静态代理,是为了区别代理模式在Java中的另一种实现——动态代理。动态代理的应用十分广泛,在struts2的拦截器中就用到了动态代理机制。还是以买车票为例子,现在用动态代理来实现,其中不懂的接口方法查查Java api就会明白了,在此不多做解释,代码如下(注意PassengerProxy类):
import java.lang.reflect.*;
public interface Passenger {
public void buyTicket();
}
public class RealPassenger implements Passenger {
@Override
public void buyTicket() {
// TODO Auto-generated method stub
System.out.println("购买了车票");
}
}
// 用动态代理实现
public class PassengerProxy implements InvocationHandler {
public Object obj;
// 把obj交给代理类
public Object obj(Object obj) {
this.obj = obj;
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
System.out.println("通过代理");
method.invoke(obj, args);
return null;
}
}
public class Client {
public static void main(String[] args) {
PassengerProxy proxy = new PassengerProxy();
Passenger passenger = (Passenger) proxy.obj(new RealPassenger());
passenger.buyTicket();
}
}