Java学习:设计模式之浅谈代理模式(proxy)

Java学习:设计模式之浅谈代理模式(proxy)我们买房子办理贷款时可以自己到银行办理,但是手续多,也可以找相应的代理公司,这样我们什么都不需要管,

欢迎大家来到IT世界,在知识的湖畔探索吧!

1.浅译代理模式

我们买房子办理贷款时可以自己到银行办理,但是手续多,也可以找相应的代理公司,这样我们什么都不需要管,代理公司会帮我们办理相应的手续。代理模式就是给一个对象提供一个代理对象,由这个代理对象控制对原对象的引用,使代理类在客户端和原对象之间起到一个代理的作用。

一般来说,在开发中,需要对其中任意一个类或方法进行额外的处理时,就需要用到代理模式。

2.介绍代理模式的概念,包括UML图和概念

代理模式的定义:

是一种结构型设计模式,当无法直接访问某个对象或访问某个对象存在困难时可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,所访问的真实对象与代理对象需要实现相同的接口。根据代理模式的使用目的不同,代理模式又可以分为多种类型,例如保护代理、远程代理、虚拟代理、缓冲代理等

代理模式的UML图:

Java学习:设计模式之浅谈代理模式(proxy)

简单结构示意图:

Java学习:设计模式之浅谈代理模式(proxy)

代理模式的角色:

1.抽象对象角色

声明了目标类及代理类对象的共同接口,这样在任何可以使用目标对象的地方都可以使用代理对象。

2.目标对象角色

定义了代理对象所代表的目标对象,在目标对象角色中实现了真实的业务操作,客户端可以通过代理对象角色间接调用目标对象角色中定义的操作。

3.代理对象角色

代理对象内部含有目标对象的引用,从而可以在任何时候操作目标对象;代理对象和目标对象具有统一的接口,以便可以再任何时候替代目标对象。代理对象通常在客户端调用传递给目标对象之前或者之后,执行某些操作,而非单纯的将调用传递给目标对象。

3.1下面给出示例代码并配以讲解代码加深理解:

首先定义AbstractObject类,在其中定义代理类和目标类共有的接口operation()

1publicabstractclass AbstractObject {

2protectedabstractvoid operation();

3 }

下面定义目标实现类:

1publicclass RealObject extends AbstractObject {

2 @Override

3protectedvoid operation() {

4 System.out.println(“do operation…”);

5 }

6 }

下面是代理对象类:

1publicclass ProxyObject extends AbstractObject {

2//对目标类的引用

3private RealObject realObject;

4

5public ProxyObject(RealObject realObject) {

6this.realObject = realObject;

7 }

8

9 @Override

10protectedvoid operation() {

11 System.out.println(“do something before real peration…”);

12if(realObject == null){

13 realObject = new RealObject();

14 }

15 realObject.operation();

16 System.out.println(“do something after real operation…”);

17 }

18 }

下面是测试类:

1publicclass ProxyTest {

2publicstaticvoid main(String[] args) {

3 AbstractObject proxy = new ProxyObject(new RealObject());

4 proxy.operation();

5 }

6 }

执行结果如下图:

Java学习:设计模式之浅谈代理模式(proxy)

从这个例子可以看出,代理对象将客户端的调用委派给了目标对象,在调用目标对象之前及之后都可以执行某些特定的操作。

3.2登入退出功能进化代码:

此为公共接口,目标对象和代理都来实现

Public interface ILogin{

//登录

Void login();

//登出

Void logout();

}

实现目标接口:

目标对象,实现公共接口,达到登录登出的功能

Public class Reallogin implements ILogin{

Public void login(){

Try{

Thread.sleep(1000);

}catch(InterruptedException e){

e.printStackTrace();

}

System.out.println(“登录系统…..”);

}

}

Public void logout(){

Try{

Thread.sleep(2000);

}catch(InterruptedException e){

e.printStackTrace();

}

System.out.println(“退出系统….”);

}

3.再次引发问题,针对代理模式的变种提出问题。

大家看见了,上边的方法中我们加入了线程的睡眠以及用了一种代码进行编写的,这样我就可以通过代理模式来测试登录登出的时间和方便你们理解代码,因为JAVA程序我们遵循OCP(对扩展开放,对修改关闭)原则,所以为了不修改原来代码,我们来采用静态代理模式:

Proxy(代理对象)的代码:

代理对象,代理目标对象Reallogin

Public class ProcyLogin 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)+”毫秒”);

}

}

好,通过代理模式,非常简单的实现了对登录登出时间的捕获,但是,假如客户突然要求我们对所有的类方法的时间进行捕获,那该怎么办呢?总不能每一个类,都写一个代理类,那样太麻烦了吧!怎么呢??

4.根据3中提出的问题,引出变化后的代理模式

分析:通过这里例子以及扩展我们来看一下静态代理模式的缺点吧:

a,如果出现上边的需求,那么势必会出现类爆炸的结果;

b,当然捕捉方法执行时间的代码都一样,我们每个方法都写,每个类都写,这也是代码的重复,没有达到代码复用的效果,这也完全违背了面向对象设计的原则。

思考:防止出现类爆炸,使代码能够得到复用。我们能不能用一个代理类,来代理所有需要计算方法运行时间呢???

那我们就使用动态代理来解决问题

代理方法的编写:

此类需要实现InvocationHandler接口

调用处理器,当代理对象调用代理方法的时候,注册在调用处理器中的invoke方法会自动调用。

Public 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;

}

}

注意这里的测试程序的编写:

JDK内置的动态代理Proxy只能代理接口

如果既想代理接口又想代理抽象类需要使用第三方组件:例如cglib

Public class Test{

publicstaticvoid main(String[] args) {

//创建目标对象

ILogin target = new ProxyLogin();

//创建代理对象:通过JDK内置的动态代理类java.lang.reflect.Proxy完成代理对象的动态创建

//参数:

ClassLoader loader;

这里的类装载器主要是用来装载在内存中生成的那个临时的字节码, 代理类的类装载器需要和目标类的类装载器一致。

Class[] interfaces;

代理类和目标类必须实现“同一些”接口。(一个类可以同时实现多个接口)

InvocationHandler handler;

当代理对象调用代理方法的时候,“注册”在调用处理器中的invoke方法会自动调用。

ILogin proxy = (IUserService)Proxy.newProxyInstance(target.getClass().getClassLoader(),new Class[]{IUserService.class},new TimerInvocationHandler(target));

//通过执行代理对象的代理方法去执行目标对象的目标方法

proxy.login();

proxy.logout();

}

}

以上的代码就顺利的解决了问题。动态代理模式相对来说比较难了解,因为它运用了反射机制。但是想象现实生活中,还是挺容易理解的,例如,工作中介,相当于代理模式中的代理对象,它可以为不同人找不同的工作,我们可以没有见过咱们生活中每个人都有一个工作中介代理对象吧。所以这里可以理解为功能代理对象,即为所有类代理可以实现同一种功能,例如上边的捕捉时间。

代理模式要解决的问题

代码的复用率的问题,以降低开发的成本和周期,还有解决代码的可维护性与可拓展性,使代码更好看,也方便他人理解.

注意事项

1. 接口并不是必须的,大多数情况下,我们为了保持对对象操作的透明性,并强制实现类实现代理类所要调用的所有的方法,我们会让它们实现与同一个接口。

2. 但是我们说代理类它其实只是在一定程度上代表了原来的实现类,所以它们有时候也可以不实现于同一个接口。

3. Proxy封装了对RealSubject的引用,以实现调用RealSubject的功能,并提供了额外的功能

代理模式的使用场景

如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法:

(1)修改原有的方法来适应。显然这违反了“对扩展开放,对修改关闭”的原则。

(2)采用一个代理类调用原来的方法,且对产生的结果进行控制。

1)远程代理,为一个位于不同的地址空间的对象提供一个本地的代理对象。这个不同的地址空间可以是在同一台主机中,也可是在另一台主机中,远程代理又叫做大使(Ambassador)。当客户端对象需要访问远程主机中的对象时可以使用远程代理。

Java学习:设计模式之浅谈代理模式(proxy)

2)现实中的例子就是“翻墙”。不过自从VPN技术被广泛应用外,“翻墙”不但使用了传统的正向代理技术,有的还使用了VPN技术。

3)虚拟代理,根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的对象。

4)安全代理,用来控制真实对象访问时的权限。

5)智能指引,当调用目标对象时,代理可以处理其他的一些操作。比如将对此对象调用的次数记录下来等。

小结:

第一:代理模式能将代理对象与真正被调用的对象分离,在一定程度上降低了系统的耦合度

第二:代理模式在客户端和目标对象之间起到一个中介作用,这样可以起到保护目标对象的作用。代理对象也可以对目标 对象调用之前进行其他操作

第三:代码的复用率的问题,以降低开发的成本和周期,还有解决代码的可维护性与可拓展性,使代码更好看,也方便他 人理解。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/22589.html

(0)

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们YX

mu99908888

在线咨询: 微信交谈

邮件:itzsgw@126.com

工作时间:时刻准备着!

关注微信