介绍
这种类型的设计模式属于结构型模式。
意图:为其他对象提供一种代理的方式控制被代理对象。
主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。
例如:1. 猪八戒去找高翠兰结果是孙悟空变的,可以这样理解:把高翠兰的外貌抽象出来,高翠兰本人和孙悟空都实现了这个接口,猪八戒访问高翠兰的时候看不出来这个是孙悟空,所以说孙悟空是高翠兰代理类。 2. spring aop。
代理模式分为两类:静态代理、动态代理。
实现
图解
通过代理实现简单的登录登出功能
静态代理
//公共接口,目标对象和代理都来实现
interface ILogin{
void login();
void logout();
}
//目标接口
class RealLogin implements ILogin{
public void login(){
try {
Thread.sleep(3200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("登录系统.....");
}
public void logout(){
try {
Thread.sleep(2200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("退出系统....");
}
}
//代理对象,代理目标对象Reallogin
class ProxyLogin implements ILogin{
//此类中包含了目标对象
private RealLogin target;
//构造方法
public ProxyLogin (RealLogin target){
this.target = target;
}
@Override
public void login() {
//开始时间
long begin = System.currentTimeMillis();
target.login();
//结束时间
long end = System.currentTimeMillis();
System.out.println("耗费时长"+(end-begin)+"毫秒");
}
@Override
public void logout() {
long begin = System.currentTimeMillis();
target.logout();
long end = System.currentTimeMillis();
System.out.println("耗费时长"+(end-begin)+"毫秒");
}
}
public class Client {
// 不使用建造者模式,创建和表示在一块,没有分离
public static void main(String[] args) {
ProxyLogin p = new ProxyLogin(new RealLogin());
//获取登录时长
p.login();
}
}
通过代理模式,非常简单的实现了对登录登出时间的捕获,但是,假如客户突然要求我们对所有的类方法的时间进行捕获,那该怎么办呢?总不能每一个类,都写一个代理类吧。这样势必会造成类爆炸,怎么办呢?
动态代理
通过反射机制,利用JDK提供的Proxy类,在程序运行的时候在内存中根据目标对象来创建代理对象,避免了类爆炸的出现。代理方法只写一此,使代码得到了复用。
//公共接口,目标对象和代理都来实现
interface ILogin {
void login();
void logout();
}
// 目标对象,实现公共接口,达到登录登出的功能
class RealLogin implements ILogin {
public void login() {
try {
Thread.sleep(3200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("登录系统.....");
}
public void logout() {
try {
Thread.sleep(2200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("退出系统....");
}
}
// 代理对象,代理目标对象Reallogin
class ProxyLogin implements ILogin {
// 此类中包含了目标对象
private RealLogin target;
// 构造方法
public ProxyLogin(RealLogin target) {
this.target = target;
}
@Override
public void login() {
// 开始时间
long begin = System.currentTimeMillis();
target.login();
// 结束时间
long end = System.currentTimeMillis();
System.out.println("耗费时长" + (end - begin) + "毫秒");
}
@Override
public void logout() {
long begin = System.currentTimeMillis();
target.logout();
long end = System.currentTimeMillis();
System.out.println("耗费时长" + (end - begin) + "毫秒");
}
}
// 此类需要实现InvocationHandler接口
class TimerInvocationHandler implements InvocationHandler {
// 目标对象,通过反射机制获得
private Object target;
// 构造方法
public TimerInvocationHandler(Object target) {
this.target = target;
}
//Object proxy:代理对象的引用,proxy变量中保存代理对象的内存地址(这个参数很少用)
//Method method:目标对象的目标方法
//Object[] args:目标对象的目标方法执行的时候所需要实参
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 开始时间
long begin = System.currentTimeMillis();
// 执行目标对象中的方法
Object retValue = method.invoke(target, args);
// 结束时间
long end = System.currentTimeMillis();
// 计算时间
System.out.println("耗费时长" + (end - begin) + "毫秒");
return retValue;
}
}
public class Client {
public static void main(String[] args) {
// 创建目标对象
ILogin target = new RealLogin();
// 这里的类装载器主要是用来装载在内存中生成的那个临时的字节码,代理类的类装载器需要和目标类的类装载器一致
ClassLoader loader = target.getClass().getClassLoader();
// 代理类和目标类必须实现"同一些"接口。(一个类可以同时实现多个接口)
Class[] interfaces = new Class[] { ProxyLogin.class };
// 当代理对象调用代理方法的时候,"注册"在调用处理器中的invoke方法会自动调用。
InvocationHandler handler = new TimerInvocationHandler(target);
// 创建代理对象:通过JDK内置的动态代理类java.lang.reflect.Proxy完成代理对象的动态创建
ILogin proxy = (ILogin) Proxy.newProxyInstance(loader, interfaces, handler);
// 通过执行代理对象的代理方法去执行目标对象的目标方法
proxy.login();
proxy.logout();
}
}
总结
抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。【好处】
(1) 职责清晰
真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件完成事务,附带的结果就是编程简洁清晰。
(2) 代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了中介的作用和保护了目标对象的作用。
(3) 高扩展性