设计模式读书笔记(十)

"Head First 设计模式第十一章"

Posted by jhljx on January 29, 2018

目录

1. 代理模式

代理模式

本章还是利用糖果机作为例子。

所谓代理(proxy),就是代表某个真实的对象。 在这个案例中,代理就像是糖果机对象一样,但其实幕后是它利用网络和一个远程的真正糖果机通讯。

远程代理就好像“远程对象的本地代表”。你的客户对象所做的就像是在做远程方法的调用,但其实只是调用了本地堆中的“代理”对象上的方法,再由代理处理所有的网络通信的低层细节。

代理模式需要解决的问题如下:
假如我们要设计一个系统,能够调用本地对象,然后将每个请求转发到远程对象上进行。

我们需要一些辅助对象, 帮我们真正进行沟通。这些辅助对象使客户就像在调用本地对象的方法(事实上也是如此)一样。客户调用客户辅助对象上的方法,仿佛客户辅助对象就是真正的服务。客户辅助对象再负责为我们转发这些请求。

换句话说,客户对象以为它调用的是远程服务上的方法,因为客户辅助对象乔装成服务对象,假装自己有客户所要调用的方法。但客户辅助对象不是真正的远程服务。虽然操作看起来很像(因为具有服务所宣称的相同的方法),但是并不真正拥有客户所期望的方法逻辑。客户辅助对象会联系服务器,传送方法调用信息,然后等待服务器返回。

在服务端,服务辅助对象从客户辅助对象中接收请求(通过socket连接),将调用的信息解包,然后调用真正服务对象上的真正方法。所以,对于服务对象来说,调用的是本地的,来自服务辅助对象,而不是远程客户。

服务辅助对象从服务中得到返回值,将它打包,然后运回到客户辅助对象(通过网络socket的输出流),客户辅助对象对信息解包,最后将返回值交给客户对象。

方法调用是如何发生的?

  • 客户对象调用客户辅助对象的doBigThing()方法
  • 客户辅助对象打包调用信息(变量、方法名等),然后通过网络给它运送服务辅助对象。
  • 服务辅助对象把来自客户辅助对象的信息解包,找出被调用的方法(以及在哪个对象内),然后调用真正的服务对象上的真正方法
  • 服务对象上的方法被调用,将结果返回给服务辅助对象。
  • 服务辅助对象把调用的返回信息打包,然后通过网络运回给客户辅助对象。
  • 客户辅助对象把返回值解包,返回给客户对象。对于客户来说,这是完全透明的。

Java RMI提供了客户辅助对象和服务辅助对象,为客户辅助对象创建和服务对象相同的方法。RMI的好处是你不必亲自写任何网络或I/O代码。客户程序调用远程方法(即真正的服务所在)就和在运行客户自己的本地JVM上对对象进行正常方法调用一样。

RMI也提供了所有运行时的基础设施,好让这一切正常工作。这包括了查找服务(lookup service),这个服务用来寻找和访问远程对象。

RMI称呼:RMI将客户辅助对象称为stub(桩),服务辅助对象称为skeleton(骨架)。

制作远程服务

制作远程服务有五个步骤。

  1. 制作远程接口
    远程接口定义出可以让客户远程调用的方法(MyService.java)
  2. 制作远程的实现 这是做实际工作的类,为远程接口中定义的远程方法提供真正的实现(MyServiceImpl.java)
  3. 利rmic产生stub和skeleton 这就是客户和服务辅助类。
  4. 启动RMI registry(rmiregistry) rmiregistry就像是电话簿,客户可以从中查到代理的位置(即客户的stub helper对象)
  5. 开始远程服务 服务实现类会实例化一个服务实例,并将这个服务注册到RMI registry。注册之后,这个服务就可以供客户调用了。

对于RMI,程序员最容易犯的错误是:

  • 忘了在启动远程服务之前先启动rmiregistry(要用Naming.rebind()注册服务,rmiregistry必须是运行的)。
  • 忘了让变量和返回值的类型成为可序列化的类型(这种错误无法在编译期发现,只会在运行时发现)
  • 忘了给客户提供stub类。

客户机JVM中有客户对象和Stub,服务器JVM中有服务对象,Stub,Skeleton。之所以服务器JVM中会需要stub类,是因为stub类是真正服务的替身,当真正服务被绑定到RMI registry时,其实真正被绑定的是stub。

代理模式为另一个对象提供一个替身或占位符以控制这个对象的访问。

使用代理模式创建代表(representative)对象,让代表对象控制某对象的访问,被代理的对象可以是远程的对象、创建开销大的对象或需要安全控制的对象。

代理模式的类图

Proxy和RealSubject都继承了Subject接口,RealSubject是真正做事的对象,它是被proxy代理和控制访问的对象。而Proxy持有RealSubject的引用。在某些例子中,Proxy会控制RealSubject对象的创建和销毁。

客户和RealSubject的交互都必须通过Proxy,因为Proxy和RealSubject实现相同的接口,所以任何用到RealSubject的地方,都可以用Proxy替代。Proxy也控制了对RealSubject的访问。

不同的代理

  • 远程代理 远程代理可以作为另一个JVM上对象的本地代表。调用代理方法,会被代理利用网络转发到远程执行,并且结果会通过网络返回给代理,再由代理将结果转给客户。

    配图

  • 虚拟代理 虚拟代理作为创建开销大的对象的代表。虚拟代理经常知道我们真正需要一个对象的时候才创建它。当对象在创建前和创建中时,由虚拟代理来扮演对象的替身。对象创建后,代理就会直接委托给对象。

    配图

装饰者模式与代理模式的区别

虽然两者基本上都是用一个对象把另一个包起来,然后把调用委托给目标。但它们的目的是不一样的。装饰者为对象增加行为,而代理是控制对象的访问。 代理将客户与远程调用解耦,这是很重要的一点。

装饰者模式装饰对象(不会实例化对象),代理模式代表对象

代理模式和适配器模式的区别

代理模式和适配器模式也很像,都是挡在其他对象的前面,并负责将请求转发给它们。适配器会改变对象适配的接口,而代理则使用相同的接口

Java在java.lang.reflect包中有自己的代理支持,利用这个包可以在运行时动态地创建一个代理类,实现一个或多个接口,并将方法的调用转发到所指定的类。因为实际的代理类在运行时创建,我们称这个Java技术为:动态代理。

  • 装饰者模式:包装另一个对象,并提供额外的行为
  • 外观模式:包装许多对象以简化它们的接口
  • 代理模式:包装另一个对象,并控制对它的访问
  • 适配器模式:包装另一个对象,并提供不同的接口

代理模式还有很多变体,例如:缓存代理,保护代理,同步代理,防火墙代理和写入时复制代理。