学过设计模式,用过设计模式,但是转瞬又忘了,这些模式到底是什么,到底是干什么用,到底是用在哪里的.看DRP的时候,又介绍了代理模式,介绍了静态代理,动态代理;当时就觉得代理模式,就像是在客户端和真实的主题类中加了一个隔层,就像是解耦,加了一个中间层.
之前学习的时候,感觉那个例子举得不好,我一直不明白为什么要用代理.他的例子说了一个人小明要追小美,但是小明拜托小代去帮忙追小美,给小美送礼物,最后小美喜欢上小代了.当时觉得,小明个二货,追人还要找代理,最后人财两空.其实,完全可以不用代理,自己出马的.
后来发现,代理,现实中很多都需要用到代理的,有的甚至是必须使用代理.比如明星,你要找他们办事,他们就会让你去找他的经纪人,经纪人就是代理;打官司,需要律师,律师就是代理;你家生产了很多产品,但是不想自己去卖,交由销售商去卖,销售商就是代理;还有上网翻墙的时候,使用代理,可以使用静态代理,你明确的指定任意一个,或者使用动态代理,他自己决定用哪个,你不管,只要能上网就成.所以说,代理的运用是相当广泛的.
那个时候学习的代理模式,类图就是如下所示.有一个客户端,一个接口Subject,接口中有一个方法request().还有两个类,代理类和实际对象类来实现这个接口,这样他们就有共同的行为了,可以在任何使用实际对象类的地方使用代理了.
如之前的小明追小美.小明是RealSubject,小吕是ProxySubject,而送礼物是Subject的接口,里面的request()方法可以是送各种东西.那么由于小明和小代都实现了送礼物的接口,所以里面的送各种礼物的方法他们都有.所以,都可以给小美送礼物.而由于小代是代理小明去送礼物的,所以实际小代送给小美的礼物,都是从小明那里拿的.送给小美的任何礼物都是小明要送给小美的.
其实代理模式除了能提供代替功能,还可以提供一些附加功能.如在学习DRP的时候,分析了静态代理和动态代理.他介绍了你可以在代理模式的代理类中添加打印信息,在调用打印前,在调用后打印,调用成功打印,这样就可以添加日志记录的功能,而不影响他们实际的类.这就可以做到对修改关闭,对添加开放.
就如下图的时序图显示的,在客户端调用代理的addUser()方法,代理的该方法可以打印输入参数,然后再去调用实际对象的addUser()方法,然后在添加完成之后打印成功失败信息,最后返回结果.你在代理类中还可以做很多事,而实际对象类只要写最关键的addUser()的代码,其他不用管.这也是单一职责的体现.
但是就如同图中所说的,这样的代理虽然很有好处,但是一个实际对象类对应一个代理类,那么类就太多了.而且这些代理类的方法可能会大量重复,而重复的代码最好不要出现多次.所以一个好的方法就是把这些重复的方法,提取出来,可以试试用泛型.
但是实际Java他们已经考虑到这个问题,所以提供了动态代理.
动态代理的类图是这样的,他和静态代理的类图的区别就是图中的Proxy已经不是我们自己写的一个代理类了,而是java他自己提供的一个Proxy类,他是动态代理类.你可以根据实际对象类的情况,用Proxy的静态方法newProxyInstance()获得相应的实际对象的代理,所以这个代理类代理的是哪个类,只有在运行的时候才能知道.
所以在实际对象类和代理中,他们没有直接的关系了,不用代理再指向实际对象.而他们之间的关系由InvocationHandler(调用处理器)来维护,而这个InvocationHandler的类必须实现InvocationHandler的接口.只要实现了这个接口,类名爱叫啥叫啥.
而为什么InvocationHandler来维护他们的关系,他有什么优势?InvocationHandler的作用是响应代理的任何调用.当客户端调用代理的时候,代理就会找InvocationHandler,要他去实现客户端的请求,而InvocationHandler调用的Invoke方法,再去调用实际对象类的实际方法.
注意InvocationHandler的invoke方法,不管你Proxy动态代理类被调用的是什么方法,对于调用处理器他被调用的只有invoke.以不变应万变,不管你来的是什么对我来说都是invoke,我统一处理了就好.
再看一下动态代理的时序图,下图中的jdk代理类就是上图中的Proxy,而LogHandler对象就是InvocationHandler,UserManageImpl就是RealSubject.
客户端调用jdk代理类的findUserById()方法,而代理立马调用LogHandler的invoke方法,在invoke方法中也和之前一样打印了一些信息,并且调用了实际对象的findUserById()方法,来返回实际的结果.
两幅时序图的区别就是,中间原本的UserManagerImplProxy的代理类被jdk代理类和LogHandler替换了.在静态代理中,需要明确指明代理类是谁,代理的是哪个实际对象类;而在动态代理类中的,则用jdk代理类和LogHandler,来根据实际情况来得到实际的代理类.
jdk代理类可以根据传递过来的参数生成对应的代理,而生成的代理,会有实际对象的所有方法,你调用这些方法的任意方法,都会去找LogHandler的invoke()方法,而invoke方法的参数也会告诉他,他要去找哪个类,找哪个方法,用哪些参数,然后就可以动态的调用方法了
这样就只用一份代码,可以创建所有需要的代理类.
写完了,觉得还是不大了解,只有真正了解,才能真正正确的运用.最后总结一下,用静态代理,符合了单一职责,和开放封闭原则,但是代理类太多,代码重复;动态代理,解决了这些问题,只用一份代码,就可以创建无数代理类,缺点是效率低.而且代理模式可以使RealSubject类中的方法的功能更单一,因为用代理,可以将与功能无关的代码都写到invoke方法中,那么以后要维护RealSubject类也是很方便的.